diff --git a/mongo/bulk_write.go b/mongo/bulk_write.go index 40f1181e0e..3af38acd4d 100644 --- a/mongo/bulk_write.go +++ b/mongo/bulk_write.go @@ -333,44 +333,39 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera switch converted := model.(type) { case *ReplaceOneModel: - doc, err = createUpdateDoc( - converted.Filter, - converted.Replacement, - converted.Hint, - nil, - converted.Collation, - converted.Upsert, - false, - false, - bw.collection.bsonOpts, - bw.collection.registry) + doc, err = updateDoc{ + filter: converted.Filter, + update: converted.Replacement, + hint: converted.Hint, + sort: converted.Sort, + collation: converted.Collation, + upsert: converted.Upsert, + }.marshal(bw.collection.bsonOpts, bw.collection.registry) hasHint = hasHint || (converted.Hint != nil) case *UpdateOneModel: - doc, err = createUpdateDoc( - converted.Filter, - converted.Update, - converted.Hint, - converted.ArrayFilters, - converted.Collation, - converted.Upsert, - false, - true, - bw.collection.bsonOpts, - bw.collection.registry) + doc, err = updateDoc{ + filter: converted.Filter, + update: converted.Update, + hint: converted.Hint, + sort: converted.Sort, + arrayFilters: converted.ArrayFilters, + collation: converted.Collation, + upsert: converted.Upsert, + checkDollarKey: true, + }.marshal(bw.collection.bsonOpts, bw.collection.registry) hasHint = hasHint || (converted.Hint != nil) hasArrayFilters = hasArrayFilters || (converted.ArrayFilters != nil) case *UpdateManyModel: - doc, err = createUpdateDoc( - converted.Filter, - converted.Update, - converted.Hint, - converted.ArrayFilters, - converted.Collation, - converted.Upsert, - true, - true, - bw.collection.bsonOpts, - bw.collection.registry) + doc, err = updateDoc{ + filter: converted.Filter, + update: converted.Update, + hint: converted.Hint, + arrayFilters: converted.ArrayFilters, + collation: converted.Collation, + upsert: converted.Upsert, + multi: true, + checkDollarKey: true, + }.marshal(bw.collection.bsonOpts, bw.collection.registry) hasHint = hasHint || (converted.Hint != nil) hasArrayFilters = hasArrayFilters || (converted.ArrayFilters != nil) } @@ -420,19 +415,20 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera return op.Result(), err } -func createUpdateDoc( - filter interface{}, - update interface{}, - hint interface{}, - arrayFilters *options.ArrayFilters, - collation *options.Collation, - upsert *bool, - multi bool, - checkDollarKey bool, - bsonOpts *options.BSONOptions, - registry *bsoncodec.Registry, -) (bsoncore.Document, error) { - f, err := marshal(filter, bsonOpts, registry) +type updateDoc struct { + filter interface{} + update interface{} + hint interface{} + sort interface{} + arrayFilters *options.ArrayFilters + collation *options.Collation + upsert *bool + multi bool + checkDollarKey bool +} + +func (doc updateDoc) marshal(bsonOpts *options.BSONOptions, registry *bsoncodec.Registry) (bsoncore.Document, error) { + f, err := marshal(doc.filter, bsonOpts, registry) if err != nil { return nil, err } @@ -440,42 +436,52 @@ func createUpdateDoc( uidx, updateDoc := bsoncore.AppendDocumentStart(nil) updateDoc = bsoncore.AppendDocumentElement(updateDoc, "q", f) - u, err := marshalUpdateValue(update, bsonOpts, registry, checkDollarKey) + u, err := marshalUpdateValue(doc.update, bsonOpts, registry, doc.checkDollarKey) if err != nil { return nil, err } updateDoc = bsoncore.AppendValueElement(updateDoc, "u", u) - if multi { - updateDoc = bsoncore.AppendBooleanElement(updateDoc, "multi", multi) + if doc.multi { + updateDoc = bsoncore.AppendBooleanElement(updateDoc, "multi", doc.multi) + } + if doc.sort != nil { + if isUnorderedMap(doc.sort) { + return nil, ErrMapForOrderedArgument{"sort"} + } + s, err := marshal(doc.sort, bsonOpts, registry) + if err != nil { + return nil, err + } + updateDoc = bsoncore.AppendDocumentElement(updateDoc, "sort", s) } - if arrayFilters != nil { + if doc.arrayFilters != nil { reg := registry - if arrayFilters.Registry != nil { - reg = arrayFilters.Registry + if doc.arrayFilters.Registry != nil { + reg = doc.arrayFilters.Registry } - arr, err := marshalValue(arrayFilters.Filters, bsonOpts, reg) + arr, err := marshalValue(doc.arrayFilters.Filters, bsonOpts, reg) if err != nil { return nil, err } updateDoc = bsoncore.AppendArrayElement(updateDoc, "arrayFilters", arr.Data) } - if collation != nil { - updateDoc = bsoncore.AppendDocumentElement(updateDoc, "collation", bsoncore.Document(collation.ToDocument())) + if doc.collation != nil { + updateDoc = bsoncore.AppendDocumentElement(updateDoc, "collation", bsoncore.Document(doc.collation.ToDocument())) } - if upsert != nil { - updateDoc = bsoncore.AppendBooleanElement(updateDoc, "upsert", *upsert) + if doc.upsert != nil { + updateDoc = bsoncore.AppendBooleanElement(updateDoc, "upsert", *doc.upsert) } - if hint != nil { - if isUnorderedMap(hint) { + if doc.hint != nil { + if isUnorderedMap(doc.hint) { return nil, ErrMapForOrderedArgument{"hint"} } - hintVal, err := marshalValue(hint, bsonOpts, registry) + hintVal, err := marshalValue(doc.hint, bsonOpts, registry) if err != nil { return nil, err } diff --git a/mongo/bulk_write_models.go b/mongo/bulk_write_models.go index 64f4589189..f22c2b8c4e 100644 --- a/mongo/bulk_write_models.go +++ b/mongo/bulk_write_models.go @@ -125,6 +125,7 @@ type ReplaceOneModel struct { Filter interface{} Replacement interface{} Hint interface{} + Sort interface{} } // NewReplaceOneModel creates a new ReplaceOneModel. @@ -173,6 +174,14 @@ func (rom *ReplaceOneModel) SetUpsert(upsert bool) *ReplaceOneModel { return rom } +// SetSort specifies which document the operation replaces if the query matches multiple documents. The first document +// matched by the sort order will be replaced. This option is only valid for MongoDB versions >= 8.0. The driver will +// return an error if the sort parameter is a multi-key map. The default value is nil. +func (rom *ReplaceOneModel) SetSort(sort interface{}) *ReplaceOneModel { + rom.Sort = &sort + return rom +} + func (*ReplaceOneModel) writeModel() {} // UpdateOneModel is used to update at most one document in a BulkWrite operation. @@ -183,6 +192,7 @@ type UpdateOneModel struct { Update interface{} ArrayFilters *options.ArrayFilters Hint interface{} + Sort interface{} } // NewUpdateOneModel creates a new UpdateOneModel. @@ -238,6 +248,14 @@ func (uom *UpdateOneModel) SetUpsert(upsert bool) *UpdateOneModel { return uom } +// SetSort specifies which document the operation updates if the query matches multiple documents. The first document +// matched by the sort order will be updated. This option is only valid for MongoDB versions >= 8.0. The driver will +// return an error if the sort parameter is a multi-key map. The default value is nil. +func (uom *UpdateOneModel) SetSort(sort interface{}) *UpdateOneModel { + uom.Sort = sort + return uom +} + func (*UpdateOneModel) writeModel() {} // UpdateManyModel is used to update multiple documents in a BulkWrite operation. diff --git a/mongo/collection.go b/mongo/collection.go index dbe238a9e3..698be220e1 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -548,17 +548,17 @@ func (coll *Collection) updateOrReplace(ctx context.Context, filter bsoncore.Doc // collation, arrayFilters, upsert, and hint are included on the individual update documents rather than as part of the // command - updateDoc, err := createUpdateDoc( - filter, - update, - uo.Hint, - uo.ArrayFilters, - uo.Collation, - uo.Upsert, - multi, - checkDollarKey, - coll.bsonOpts, - coll.registry) + updateDoc, err := updateDoc{ + filter: filter, + update: update, + hint: uo.Hint, + sort: uo.Sort, + arrayFilters: uo.ArrayFilters, + collation: uo.Collation, + upsert: uo.Upsert, + multi: multi, + checkDollarKey: checkDollarKey, + }.marshal(coll.bsonOpts, coll.registry) if err != nil { return nil, err } @@ -598,7 +598,6 @@ func (coll *Collection) updateOrReplace(ctx context.Context, filter bsoncore.Doc } op = op.Let(let) } - if uo.BypassDocumentValidation != nil && *uo.BypassDocumentValidation { op = op.BypassDocumentValidation(*uo.BypassDocumentValidation) } @@ -760,6 +759,7 @@ func (coll *Collection) ReplaceOne(ctx context.Context, filter interface{}, uOpts.Hint = opt.Hint uOpts.Let = opt.Let uOpts.Comment = opt.Comment + uOpts.Sort = opt.Sort updateOptions = append(updateOptions, uOpts) } diff --git a/mongo/integration/collection_test.go b/mongo/integration/collection_test.go index 15c0e9324c..8fb6363f31 100644 --- a/mongo/integration/collection_test.go +++ b/mongo/integration/collection_test.go @@ -640,6 +640,17 @@ func TestCollection(t *testing.T) { assert.Equal(mt, int64(0), res.ModifiedCount, "expected modified count 0, got %v", res.ModifiedCount) assert.NotNil(mt, res.UpsertedID, "expected upserted ID, got nil") }) + // Require 8.0 servers for sort support. + mt.RunOpts("error with sort", mtest.NewOptions().MinServerVersion("8.0"), func(mt *mtest.T) { + filter := bson.D{{"x", bson.D{{"$gte", 3}}}} + update := bson.D{{"$inc", bson.D{{"x", 1}}}} + + _, err := mt.Coll.UpdateMany(context.Background(), + filter, update, + &options.UpdateOptions{Sort: bson.D{{"_id", -1}}}, + ) + assert.ErrorContains(t, err, "Cannot specify sort with multi=true", "expected an error on UpdateMany with sort") + }) mt.Run("write error", func(mt *mtest.T) { filter := bson.D{{"_id", "foo"}} update := bson.D{{"$set", bson.D{{"_id", 3.14159}}}} diff --git a/mongo/integration/unified/bulkwrite_helpers.go b/mongo/integration/unified/bulkwrite_helpers.go index 1d43fca40a..632abe9698 100644 --- a/mongo/integration/unified/bulkwrite_helpers.go +++ b/mongo/integration/unified/bulkwrite_helpers.go @@ -90,6 +90,12 @@ func createBulkWriteModel(rawModel bson.Raw) (mongo.WriteModel, error) { return nil, fmt.Errorf("error creating hint: %w", err) } uom.SetHint(hint) + case "sort": + sort, err := createSort(val) + if err != nil { + return nil, fmt.Errorf("error creating sort: %w", err) + } + uom.SetSort(sort) case "update": update, err = createUpdateValue(val) if err != nil { @@ -242,6 +248,12 @@ func createBulkWriteModel(rawModel bson.Raw) (mongo.WriteModel, error) { return nil, fmt.Errorf("error creating hint: %w", err) } rom.SetHint(hint) + case "sort": + sort, err := createSort(val) + if err != nil { + return nil, fmt.Errorf("error creating sort: %w", err) + } + rom.SetSort(sort) case "replacement": replacement = val.Document() case "upsert": diff --git a/mongo/integration/unified/collection_operation_execution.go b/mongo/integration/unified/collection_operation_execution.go index 1235e4d62d..fb22506149 100644 --- a/mongo/integration/unified/collection_operation_execution.go +++ b/mongo/integration/unified/collection_operation_execution.go @@ -1248,6 +1248,12 @@ func executeReplaceOne(ctx context.Context, operation *operation) (*operationRes return nil, fmt.Errorf("error creating hint: %w", err) } opts.SetHint(hint) + case "sort": + sort, err := createSort(val) + if err != nil { + return nil, fmt.Errorf("error creating sort: %w", err) + } + opts.SetSort(sort) case "replacement": replacement = val.Document() case "upsert": diff --git a/mongo/integration/unified/crud_helpers.go b/mongo/integration/unified/crud_helpers.go index 0a01685988..7ddbd5502d 100644 --- a/mongo/integration/unified/crud_helpers.go +++ b/mongo/integration/unified/crud_helpers.go @@ -63,6 +63,8 @@ func createUpdateArguments(args bson.Raw) (*updateArguments, error) { ua.opts.SetHint(hint) case "let": ua.opts.SetLet(val.Document()) + case "sort": + ua.opts.SetSort(val.Document()) case "update": ua.update, err = createUpdateValue(val) if err != nil { @@ -160,6 +162,18 @@ func createHint(val bson.RawValue) (interface{}, error) { return hint, nil } +func createSort(val bson.RawValue) (interface{}, error) { + var sort interface{} + + switch val.Type { + case bsontype.EmbeddedDocument: + sort = val.Document() + default: + return nil, fmt.Errorf("unrecognized sort value type %s", val.Type) + } + return sort, nil +} + func createCommentString(val bson.RawValue) (string, error) { switch val.Type { case bsontype.String: diff --git a/mongo/options/replaceoptions.go b/mongo/options/replaceoptions.go index f7d3960194..32878d29ea 100644 --- a/mongo/options/replaceoptions.go +++ b/mongo/options/replaceoptions.go @@ -40,6 +40,12 @@ type ReplaceOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // A document specifying which document should be replaced if the filter used by the operation matches multiple + // documents in the collection. If set, the first document in the sorted order will be replaced. This option is + // only valid for MongoDB versions >= 8.0. The driver will return an error if the sort parameter is a multi-key + // map. The default value is nil. + Sort interface{} } // Replace creates a new ReplaceOptions instance. @@ -83,6 +89,12 @@ func (ro *ReplaceOptions) SetLet(l interface{}) *ReplaceOptions { return ro } +// SetSort sets the value for the Sort field. +func (ro *ReplaceOptions) SetSort(s interface{}) *ReplaceOptions { + ro.Sort = s + return ro +} + // MergeReplaceOptions combines the given ReplaceOptions instances into a single ReplaceOptions in a last-one-wins // fashion. // @@ -112,6 +124,9 @@ func MergeReplaceOptions(opts ...*ReplaceOptions) *ReplaceOptions { if ro.Let != nil { rOpts.Let = ro.Let } + if ro.Sort != nil { + rOpts.Sort = ro.Sort + } } return rOpts diff --git a/mongo/options/updateoptions.go b/mongo/options/updateoptions.go index 5206f9f01b..4032842c8e 100644 --- a/mongo/options/updateoptions.go +++ b/mongo/options/updateoptions.go @@ -45,6 +45,12 @@ type UpdateOptions struct { // Values must be constant or closed expressions that do not reference document fields. Parameters can then be // accessed as variables in an aggregate expression context (e.g. "$$var"). Let interface{} + + // A document specifying which document should be updated if the filter used by the operation matches multiple + // documents in the collection. If set, the first document in the sorted order will be updated. This option is + // only valid for MongoDB versions >= 8.0. The driver will return an error if the sort parameter is a multi-key + // map. The default value is nil. + Sort interface{} } // Update creates a new UpdateOptions instance. @@ -94,6 +100,12 @@ func (uo *UpdateOptions) SetLet(l interface{}) *UpdateOptions { return uo } +// SetSort sets the value for the Sort field. +func (uo *UpdateOptions) SetSort(s interface{}) *UpdateOptions { + uo.Sort = s + return uo +} + // MergeUpdateOptions combines the given UpdateOptions instances into a single UpdateOptions in a last-one-wins fashion. // // Deprecated: Merging options structs will not be supported in Go Driver 2.0. Users should create a @@ -125,6 +137,9 @@ func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions { if uo.Let != nil { uOpts.Let = uo.Let } + if uo.Sort != nil { + uOpts.Sort = uo.Sort + } } return uOpts diff --git a/testdata/crud/unified/bulkWrite-replaceOne-sort.json b/testdata/crud/unified/bulkWrite-replaceOne-sort.json new file mode 100644 index 0000000000..c0bd383514 --- /dev/null +++ b/testdata/crud/unified/bulkWrite-replaceOne-sort.json @@ -0,0 +1,239 @@ +{ + "description": "BulkWrite replaceOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite replaceOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "BulkWrite replaceOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "replaceOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/testdata/crud/unified/bulkWrite-replaceOne-sort.yml b/testdata/crud/unified/bulkWrite-replaceOne-sort.yml new file mode 100644 index 0000000000..9594166f5b --- /dev/null +++ b/testdata/crud/unified/bulkWrite-replaceOne-sort.yml @@ -0,0 +1,94 @@ +description: BulkWrite replaceOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: database0 + client: client0 + databaseName: crud-tests + - collection: + id: collection0 + database: database0 + collectionName: coll0 + +initialData: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: BulkWrite replaceOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - object: collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: { _id: {$gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: BulkWrite replaceOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - object: collection0 + name: bulkWrite + arguments: + requests: + - replaceOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectError: + isClientError: false + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/testdata/crud/unified/bulkWrite-updateOne-sort.json b/testdata/crud/unified/bulkWrite-updateOne-sort.json new file mode 100644 index 0000000000..f78bd3bf3e --- /dev/null +++ b/testdata/crud/unified/bulkWrite-updateOne-sort.json @@ -0,0 +1,255 @@ +{ + "description": "BulkWrite updateOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite updateOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ] + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "BulkWrite updateOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "updateOne": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": [ + { + "$set": { + "x": 1 + } + } + ] + } + } + ] + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": [ + { + "$set": { + "x": 1 + } + } + ], + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/testdata/crud/unified/bulkWrite-updateOne-sort.yml b/testdata/crud/unified/bulkWrite-updateOne-sort.yml new file mode 100644 index 0000000000..9446986fab --- /dev/null +++ b/testdata/crud/unified/bulkWrite-updateOne-sort.yml @@ -0,0 +1,94 @@ +description: BulkWrite updateOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: database0 + client: client0 + databaseName: crud-tests + - collection: + id: collection0 + database: database0 + collectionName: coll0 + +initialData: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: BulkWrite updateOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - object: collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: [ $set: { x: 1 } ] + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: [ $set: { x: 1 } ] + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: BulkWrite updateOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - object: collection0 + name: bulkWrite + arguments: + requests: + - updateOne: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: [ $set: { x: 1 } ] + expectError: + isClientError: false + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: [ $set: { x: 1 } ] + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/testdata/crud/unified/replaceOne-sort.json b/testdata/crud/unified/replaceOne-sort.json new file mode 100644 index 0000000000..cf2271dda5 --- /dev/null +++ b/testdata/crud/unified/replaceOne-sort.json @@ -0,0 +1,232 @@ +{ + "description": "replaceOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 1 + } + ] + } + ] + }, + { + "description": "replaceOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "replacement": { + "x": 1 + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "x": 1 + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/testdata/crud/unified/replaceOne-sort.yml b/testdata/crud/unified/replaceOne-sort.yml new file mode 100644 index 0000000000..3395b795e8 --- /dev/null +++ b/testdata/crud/unified/replaceOne-sort.yml @@ -0,0 +1,94 @@ +description: replaceOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: client0 + observeEvents: [ commandStartedEvent, commandSucceededEvent ] + - database: + id: database0 + client: client0 + databaseName: crud-tests + - collection: + id: collection0 + database: database0 + collectionName: coll0 + +initialData: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: ReplaceOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - name: replaceOne + object: collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 1 } + + - description: replaceOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - name: replaceOne + object: collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + replacement: { x: 1 } + expectError: + isClientError: false + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { x: 1 } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } diff --git a/testdata/crud/unified/updateOne-sort.json b/testdata/crud/unified/updateOne-sort.json new file mode 100644 index 0000000000..8fe4f50b94 --- /dev/null +++ b/testdata/crud/unified/updateOne-sort.json @@ -0,0 +1,240 @@ +{ + "description": "updateOne-sort", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent", + "commandSucceededEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with sort option", + "runOnRequirements": [ + { + "minServerVersion": "8.0" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectResult": { + "matchedCount": 1, + "modifiedCount": 1, + "upsertedCount": 0 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + }, + { + "commandSucceededEvent": { + "reply": { + "ok": 1, + "n": 1 + }, + "commandName": "update" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 34 + } + ] + } + ] + }, + { + "description": "updateOne with sort option unsupported (server-side error)", + "runOnRequirements": [ + { + "maxServerVersion": "7.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "sort": { + "_id": -1 + }, + "update": { + "$inc": { + "x": 1 + } + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "u": { + "$inc": { + "x": 1 + } + }, + "sort": { + "_id": -1 + }, + "multi": { + "$$unsetOrMatches": false + }, + "upsert": { + "$$unsetOrMatches": false + } + } + ] + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ] + } + ] +} diff --git a/testdata/crud/unified/updateOne-sort.yml b/testdata/crud/unified/updateOne-sort.yml new file mode 100644 index 0000000000..a278575fa5 --- /dev/null +++ b/testdata/crud/unified/updateOne-sort.yml @@ -0,0 +1,96 @@ +description: updateOne-sort + +schemaVersion: "1.0" + +createEntities: + - client: + id: client0 + observeEvents: + - commandStartedEvent + - commandSucceededEvent + - database: + id: database0 + client: client0 + databaseName: crud-tests + - collection: + id: collection0 + database: database0 + collectionName: coll0 + +initialData: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + +tests: + - description: UpdateOne with sort option + runOnRequirements: + - minServerVersion: "8.0" + operations: + - name: updateOne + object: collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: { $inc: { x: 1 } } + expectResult: + matchedCount: 1 + modifiedCount: 1 + upsertedCount: 0 + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + - commandSucceededEvent: + reply: { ok: 1, n: 1 } + commandName: update + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 34 } + + - description: updateOne with sort option unsupported (server-side error) + runOnRequirements: + - maxServerVersion: "7.99" + operations: + - name: updateOne + object: collection0 + arguments: + filter: { _id: { $gt: 1 } } + sort: { _id: -1 } + update: { $inc: { x: 1 } } + expectError: + isClientError: false + expectEvents: + - client: client0 + events: + - commandStartedEvent: + command: + update: coll0 + updates: + - q: { _id: { $gt: 1 } } + u: { $inc: { x: 1 } } + sort: { _id: -1 } + multi: { $$unsetOrMatches: false } + upsert: { $$unsetOrMatches: false } + outcome: + - collectionName: coll0 + databaseName: crud-tests + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 }