Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GODRIVER-3285 Allow sort option in client bulk write. #1923

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions internal/integration/unified/client_operation_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ func createClientUpdateOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error)
Collation *options.Collation
Hint *bson.RawValue
Upsert *bool
Sort *bson.RawValue
}
err := bson.Unmarshal(value, &v)
if err != nil {
Expand All @@ -340,12 +341,17 @@ func createClientUpdateOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error)
return nil, err
}
}
var sort interface{}
if v.Sort != nil {
sort = v.Sort.Document()
}
model := &mongo.ClientUpdateOneModel{
Filter: v.Filter,
Update: v.Update,
Collation: v.Collation,
Hint: hint,
Upsert: v.Upsert,
Sort: sort,
}
if len(v.ArrayFilters) > 0 {
model.ArrayFilters = v.ArrayFilters
Expand Down Expand Up @@ -405,6 +411,7 @@ func createClientReplaceOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error)
Collation *options.Collation
Hint *bson.RawValue
Upsert *bool
Sort *bson.RawValue
}
err := bson.Unmarshal(value, &v)
if err != nil {
Expand All @@ -417,6 +424,10 @@ func createClientReplaceOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error)
return nil, err
}
}
var sort interface{}
if v.Sort != nil {
sort = v.Sort.Document()
}
ns := strings.SplitN(v.Namespace, ".", 2)
return &mongo.ClientBulkWrite{
Database: ns[0],
Expand All @@ -427,6 +438,7 @@ func createClientReplaceOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error)
Collation: v.Collation,
Hint: hint,
Upsert: v.Upsert,
Sort: sort,
},
}, nil
}
Expand Down
14 changes: 14 additions & 0 deletions mongo/client_bulk_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ func (mb *modelBatches) appendBatches(fn functionSet, dst []byte, maxCount, tota
arrayFilters: model.ArrayFilters,
collation: model.Collation,
upsert: model.Upsert,
sort: model.Sort,
multi: false,
checkDollarKey: true,
}).marshal(mb.client.bsonOpts, mb.client.registry)
Expand Down Expand Up @@ -342,6 +343,7 @@ func (mb *modelBatches) appendBatches(fn functionSet, dst []byte, maxCount, tota
arrayFilters: nil,
collation: model.Collation,
upsert: model.Upsert,
sort: model.Sort,
multi: false,
checkDollarKey: false,
}).marshal(mb.client.bsonOpts, mb.client.registry)
Expand Down Expand Up @@ -603,6 +605,7 @@ type clientUpdateDoc struct {
hint interface{}
arrayFilters []interface{}
collation *options.Collation
sort interface{}
upsert *bool
multi bool
checkDollarKey bool
Expand Down Expand Up @@ -657,6 +660,17 @@ func (d *clientUpdateDoc) marshal(bsonOpts *options.BSONOptions, registry *bson.
doc = bsoncore.AppendValueElement(doc, "hint", hintVal)
}

if d.sort != nil {
if isUnorderedMap(d.sort) {
return nil, ErrMapForOrderedArgument{"sort"}
}
sortVal, err := marshalValue(d.sort, bsonOpts, registry)
if err != nil {
return nil, err
}
doc = bsoncore.AppendValueElement(doc, "sort", sortVal)
}

return bsoncore.AppendDocumentEnd(doc, uidx)
}

Expand Down
18 changes: 18 additions & 0 deletions mongo/client_bulk_write_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type ClientUpdateOneModel struct {
Update interface{}
ArrayFilters []interface{}
Hint interface{}
Sort interface{}
}

// NewClientUpdateOneModel creates a new ClientUpdateOneModel.
Expand Down Expand Up @@ -105,6 +106,14 @@ func (uom *ClientUpdateOneModel) SetUpsert(upsert bool) *ClientUpdateOneModel {
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest clarifying this sentence:

The driver will return an error if the sort parameter is a multi-key map.

Note that the sort parameter cannot be an unordered since different orders mean different things. Specifically, when sorting on multiple fields, sort order is evaluated from left to right.

Because sort order matters (evaluating left to right), the driver will return an error if the sort parameter is a multi-key map to prevent unintentional behavior.

SetSort for ClientReplaceOneModel also needs to be updated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update the comment for findoptions, too.

func (uom *ClientUpdateOneModel) SetSort(sort interface{}) *ClientUpdateOneModel {
uom.Sort = sort
return uom
}

// ClientUpdateManyModel is used to update multiple documents in a client-level BulkWrite operation.
//
// See corresponding setter methods for documentation.
Expand Down Expand Up @@ -176,6 +185,7 @@ type ClientReplaceOneModel struct {
Filter interface{}
Replacement interface{}
Hint interface{}
Sort interface{}
}

// NewClientReplaceOneModel creates a new ClientReplaceOneModel.
Expand Down Expand Up @@ -222,6 +232,14 @@ func (rom *ClientReplaceOneModel) SetUpsert(upsert bool) *ClientReplaceOneModel
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 *ClientReplaceOneModel) SetSort(sort interface{}) *ClientReplaceOneModel {
rom.Sort = sort
return rom
}

// ClientDeleteOneModel is used to delete at most one document in a client-level BulkWriteOperation.
//
// See corresponding setter methods for documentation.
Expand Down
163 changes: 163 additions & 0 deletions testdata/crud/unified/client-bulkWrite-replaceOne-sort.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
{
"description": "client bulkWrite updateOne-sort",
"schemaVersion": "1.4",
"runOnRequirements": [
{
"minServerVersion": "8.0",
"serverless": "forbid"
}
],
"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
}
]
}
],
"_yamlAnchors": {
"namespace": "crud-tests.coll0"
},
"tests": [
{
"description": "client bulkWrite replaceOne with sort option",
"operations": [
{
"object": "client0",
"name": "clientBulkWrite",
"arguments": {
"models": [
{
"replaceOne": {
"namespace": "crud-tests.coll0",
"filter": {
"_id": {
"$gt": 1
}
},
"sort": {
"_id": -1
},
"replacement": {
"x": 1
}
}
}
]
}
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"commandName": "bulkWrite",
"databaseName": "admin",
"command": {
"bulkWrite": 1,
"ops": [
{
"update": 0,
"filter": {
"_id": {
"$gt": 1
}
},
"updateMods": {
"x": 1
},
"sort": {
"_id": -1
},
"multi": {
"$$unsetOrMatches": false
},
"upsert": {
"$$unsetOrMatches": false
}
}
],
"nsInfo": [
{
"ns": "crud-tests.coll0"
}
]
}
}
},
{
"commandSucceededEvent": {
"reply": {
"ok": 1,
"nErrors": 0,
"nMatched": 1,
"nModified": 1
},
"commandName": "bulkWrite"
}
}
]
}
],
"outcome": [
{
"collectionName": "coll0",
"databaseName": "crud-tests",
"documents": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 1
}
]
}
]
}
]
}
77 changes: 77 additions & 0 deletions testdata/crud/unified/client-bulkWrite-replaceOne-sort.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
description: client bulkWrite updateOne-sort

schemaVersion: "1.4"

runOnRequirements:
- minServerVersion: "8.0"
serverless: forbid # Serverless does not support bulkWrite: CLOUDP-256344.

createEntities:
- client:
id: &client0 client0
observeEvents:
- commandStartedEvent
- commandSucceededEvent
- database:
id: &database0 database0
client: *client0
databaseName: &database0Name crud-tests
- collection:
id: &collection0 collection0
database: *database0
collectionName: &collection0Name coll0

initialData:
- collectionName: *collection0Name
databaseName: *database0Name
documents:
- { _id: 1, x: 11 }
- { _id: 2, x: 22 }
- { _id: 3, x: 33 }

_yamlAnchors:
namespace: &namespace "crud-tests.coll0"

tests:
- description: client bulkWrite replaceOne with sort option
operations:
- object: *client0
name: clientBulkWrite
arguments:
models:
- replaceOne:
namespace: *namespace
filter: { _id: { $gt: 1 } }
sort: { _id: -1 }
replacement: { x: 1 }
expectEvents:
- client: *client0
events:
- commandStartedEvent:
commandName: bulkWrite
databaseName: admin
command:
bulkWrite: 1
ops:
- update: 0
filter: { _id: { $gt: 1 } }
updateMods: { x: 1 }
sort: { _id: -1 }
multi: { $$unsetOrMatches: false }
upsert: { $$unsetOrMatches: false }
nsInfo:
- ns: *namespace
- commandSucceededEvent:
reply:
ok: 1
nErrors: 0
nMatched: 1
nModified: 1
commandName: bulkWrite
outcome:
- collectionName: *collection0Name
databaseName: *database0Name
documents:
- { _id: 1, x: 11 }
- { _id: 2, x: 22 }
- { _id: 3, x: 1 }
Loading
Loading