Skip to content

Commit

Permalink
Add replaceOne
Browse files Browse the repository at this point in the history
  • Loading branch information
qingyang-hu committed Sep 5, 2024
1 parent d503a13 commit 11e4279
Show file tree
Hide file tree
Showing 12 changed files with 512 additions and 95 deletions.
1 change: 1 addition & 0 deletions mongo/bulk_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera
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)
Expand Down
9 changes: 9 additions & 0 deletions mongo/bulk_write_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ type ReplaceOneModel struct {
Filter interface{}
Replacement interface{}
Hint interface{}
Sort interface{}
}

// NewReplaceOneModel creates a new ReplaceOneModel.
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions mongo/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -759,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)
}

Expand Down
2 changes: 2 additions & 0 deletions mongo/integration/crud_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,8 @@ func executeReplaceOne(mt *mtest.T, sess mongo.Session, args bson.Raw) (*mongo.U
opts = opts.SetCollation(createCollation(mt, val.Document()))
case "hint":
opts = opts.SetHint(createHint(mt, val))
case "sort":
opts = opts.SetSort(createSort(mt, val))
case "session":
default:
mt.Fatalf("unrecognized replaceOne option: %v", key)
Expand Down
6 changes: 6 additions & 0 deletions mongo/integration/unified/bulkwrite_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,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":
Expand Down
6 changes: 6 additions & 0 deletions mongo/integration/unified/collection_operation_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down
15 changes: 15 additions & 0 deletions mongo/options/replaceoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -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
Expand Down
239 changes: 239 additions & 0 deletions testdata/crud/unified/bulkWrite-replaceOne-sort.json
Original file line number Diff line number Diff line change
@@ -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
}
]
}
]
}
]
}
1 change: 0 additions & 1 deletion testdata/crud/unified/bulkWrite-updateOne-sort.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@
]
},
"expectError": {
"errorContains": "'update.updates.sort' is an unknown field",
"isClientError": false
}
}
Expand Down
Loading

0 comments on commit 11e4279

Please sign in to comment.