diff --git a/internal/integration/unified/collection_operation_execution.go b/internal/integration/unified/collection_operation_execution.go index 2060db01e3..86b5eed7e5 100644 --- a/internal/integration/unified/collection_operation_execution.go +++ b/internal/integration/unified/collection_operation_execution.go @@ -523,6 +523,8 @@ func executeDistinct(ctx context.Context, operation *operation) (*operationResul val := elem.Value() switch key { + case "hint": + opts.SetHint(val) case "collation": collation, err := createCollation(val.Document()) if err != nil { diff --git a/mongo/collection.go b/mongo/collection.go index 4c2cfef4f0..300f0c34e1 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -1281,6 +1281,16 @@ func (coll *Collection) Distinct( } op.Comment(comment) } + if args.Hint != nil { + if isUnorderedMap(args.Hint) { + return &DistinctResult{err: ErrMapForOrderedArgument{"hint"}} + } + hint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry) + if err != nil { + return &DistinctResult{err: err} + } + op.Hint(hint) + } retry := driver.RetryNone if coll.client.retryReads { retry = driver.RetryOncePerCommand diff --git a/mongo/options/distinctoptions.go b/mongo/options/distinctoptions.go index e54922711b..0b9167d5d0 100644 --- a/mongo/options/distinctoptions.go +++ b/mongo/options/distinctoptions.go @@ -13,6 +13,7 @@ package options type DistinctOptions struct { Collation *Collation Comment interface{} + Hint interface{} } // DistinctOptionsBuilder contains options to configure distinct operations. Each @@ -60,3 +61,21 @@ func (do *DistinctOptionsBuilder) SetComment(comment interface{}) *DistinctOptio return do } + +// SetHint specifies the index to use for the operation. This should either be +// the index name as a string or the index specification as a document. This +// option is only valid for MongoDB versions >= 7.1. Previous server versions +// will return an error if an index hint is specified. Distinct returns an error +// if the hint parameter is a multi-key map. The default value is nil, which +// means that no index hint will be sent. +// +// SetHint sets the Hint field. +func (do *DistinctOptionsBuilder) SetHint(hint interface{}) *DistinctOptionsBuilder { + do.Opts = append(do.Opts, func(opts *DistinctOptions) error { + opts.Hint = hint + + return nil + }) + + return do +} diff --git a/testdata/crud/unified/distinct-hint.json b/testdata/crud/unified/distinct-hint.json new file mode 100644 index 0000000000..2a6869cbe0 --- /dev/null +++ b/testdata/crud/unified/distinct-hint.json @@ -0,0 +1,139 @@ +{ + "description": "distinct-hint", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "minServerVersion": "7.1.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "distinct-hint-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "distinct-hint-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + } + ] + } + ], + "tests": [ + { + "description": "distinct with hint string", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": 1 + }, + "hint": "_id_" + }, + "expectResult": [ + 11 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": { + "_id": 1 + }, + "hint": "_id_" + }, + "commandName": "distinct", + "databaseName": "distinct-hint-tests" + } + } + ] + } + ] + }, + { + "description": "distinct with hint document", + "operations": [ + { + "name": "distinct", + "object": "collection0", + "arguments": { + "fieldName": "x", + "filter": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "expectResult": [ + 11 + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "distinct": "coll0", + "key": "x", + "query": { + "_id": 1 + }, + "hint": { + "_id": 1 + } + }, + "commandName": "distinct", + "databaseName": "distinct-hint-tests" + } + } + ] + } + ] + } + ] +} diff --git a/testdata/crud/unified/distinct-hint.yml b/testdata/crud/unified/distinct-hint.yml new file mode 100644 index 0000000000..9d277616d3 --- /dev/null +++ b/testdata/crud/unified/distinct-hint.yml @@ -0,0 +1,73 @@ +description: "distinct-hint" + +schemaVersion: "1.0" +runOnRequirements: + # https://jira.mongodb.org/browse/SERVER-14227 + # Server supports distinct with hint starting from 7.1.0. + - minServerVersion: "7.1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name distinct-hint-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 } + +tests: + - description: "distinct with hint string" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: &fieldName x + filter: &filter { _id: 1 } + hint: _id_ + expectResult: [ 11 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + hint: _id_ + commandName: distinct + databaseName: *database0Name + + - description: "distinct with hint document" + operations: + - name: distinct + object: *collection0 + arguments: + fieldName: *fieldName + filter: *filter + hint: + _id: 1 + expectResult: [ 11 ] + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + distinct: *collection0Name + key: *fieldName + query: *filter + hint: + _id: 1 + commandName: distinct + databaseName: *database0Name diff --git a/x/mongo/driver/operation/distinct.go b/x/mongo/driver/operation/distinct.go index cfff2f7c31..3351bd8ffa 100644 --- a/x/mongo/driver/operation/distinct.go +++ b/x/mongo/driver/operation/distinct.go @@ -31,6 +31,7 @@ type Distinct struct { clock *session.ClusterClock collection string comment bsoncore.Value + hint bsoncore.Value monitor *event.CommandMonitor crypt driver.Crypt database string @@ -120,6 +121,9 @@ func (d *Distinct) command(dst []byte, desc description.SelectedServer) ([]byte, if d.comment.Type != bsoncore.Type(0) { dst = bsoncore.AppendValueElement(dst, "comment", d.comment) } + if d.hint.Type != bsoncore.Type(0) { + dst = bsoncore.AppendValueElement(dst, "hint", d.hint) + } if d.key != nil { dst = bsoncore.AppendStringElement(dst, "key", *d.key) } @@ -199,6 +203,16 @@ func (d *Distinct) Comment(comment bsoncore.Value) *Distinct { return d } +// Hint sets a value to help trace an operation. +func (d *Distinct) Hint(hint bsoncore.Value) *Distinct { + if d == nil { + d = new(Distinct) + } + + d.hint = hint + return d +} + // CommandMonitor sets the monitor to use for APM events. func (d *Distinct) CommandMonitor(monitor *event.CommandMonitor) *Distinct { if d == nil {