From eb020e07e49476f353fff624619be7619f6bbe6f Mon Sep 17 00:00:00 2001 From: Matt Broadstone Date: Mon, 30 Nov 2020 16:02:32 -0500 Subject: [PATCH] refactor: move find command building all into the find operation One of the changes made in merging the core and native drivers was to merge the wire protocol methods for executing find operations. A vestigial piece of this was that the find command was being built in the wire protocol layer, rather than the operation layer. This patch teases these two apart, so that the `query` wire protocol method only creates and executes an `OP_QUERY` message, and the modern and legacy find commands are built in the find operation's definition itself. NODE-2900 --- src/cmap/connection.ts | 13 +- src/cmap/wire_protocol/query.ts | 276 ++++++---------------- src/index.ts | 1 - src/operations/find.ts | 326 ++++++++++++++++---------- src/operations/indexes.ts | 5 +- src/operations/list_collections.ts | 6 +- src/sdam/server.ts | 10 +- test/functional/max_staleness.test.js | 8 +- 8 files changed, 292 insertions(+), 353 deletions(-) diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index e9517896a0..fe7dbdfbed 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -4,7 +4,14 @@ import { StreamDescription, StreamDescriptionOptions } from './stream_descriptio import * as wp from './wire_protocol'; import { CommandStartedEvent, CommandFailedEvent, CommandSucceededEvent } from './events'; import { updateSessionFromResponse } from '../sessions'; -import { uuidV4, ClientMetadata, now, calculateDurationInMs, Callback } from '../utils'; +import { + uuidV4, + ClientMetadata, + now, + calculateDurationInMs, + Callback, + MongoDBNamespace +} from '../utils'; import { MongoError, MongoNetworkError, @@ -23,7 +30,7 @@ import type { GetMoreOptions } from './wire_protocol/get_more'; import type { InsertOptions, UpdateOptions, RemoveOptions } from './wire_protocol/index'; import type { Stream } from './connect'; import type { LoggerOptions } from '../logger'; -import type { FindOptions } from '../operations/find'; +import type { QueryOptions } from './wire_protocol/query'; const kStream = Symbol('stream'); const kQueue = Symbol('queue'); @@ -246,7 +253,7 @@ export class Connection extends EventEmitter { wp.command(makeServerTrampoline(this), ns, cmd, options as CommandOptions, callback); } - query(ns: string, cmd: Document, options: FindOptions, callback: Callback): void { + query(ns: MongoDBNamespace, cmd: Document, options: QueryOptions, callback: Callback): void { wp.query(makeServerTrampoline(this), ns, cmd, options, callback); } diff --git a/src/cmap/wire_protocol/query.ts b/src/cmap/wire_protocol/query.ts index af84db347a..7c17e22202 100644 --- a/src/cmap/wire_protocol/query.ts +++ b/src/cmap/wire_protocol/query.ts @@ -1,175 +1,40 @@ -import { command, CommandOptions } from './command'; -import { Query } from '../commands'; -import { MongoError } from '../../error'; -import { maxWireVersion, collectionNamespace, Callback, decorateWithExplain } from '../../utils'; -import { getReadPreference, isSharded, applyCommonQueryOptions } from './shared'; -import { Document, pluckBSONSerializeOptions } from '../../bson'; +import { OpQueryOptions, Query } from '../commands'; +import type { Callback, MongoDBNamespace } from '../../utils'; +import { BSONSerializeOptions, Document, pluckBSONSerializeOptions } from '../../bson'; import type { Server } from '../../sdam/server'; -import type { ReadPreferenceLike } from '../../read_preference'; -import type { FindOptions } from '../../operations/find'; -import { Explain } from '../../explain'; +import { ReadPreference } from '../../read_preference'; /** @internal */ -export interface QueryOptions extends CommandOptions { - readPreference?: ReadPreferenceLike; +export interface QueryOptions extends BSONSerializeOptions { + readPreference: ReadPreference; + documentsReturnedIn?: string; + batchSize?: number; + limit?: number; + skip?: number; + projection?: Document; + tailable?: boolean; + awaitData?: boolean; + noCursorTimeout?: boolean; + /** @deprecated use `noCursorTimeout` instead */ + timeout?: boolean; + partial?: boolean; + oplogReplay?: boolean; } export function query( server: Server, - ns: string, - cmd: Document, - options: FindOptions, + ns: MongoDBNamespace, + findCommand: Document, + options: QueryOptions, callback: Callback ): void { options = options || {}; - if (cmd == null) { - return callback(new MongoError(`command ${JSON.stringify(cmd)} does not return a cursor`)); - } - - if (maxWireVersion(server) < 4) { - const query = prepareLegacyFindQuery(server, ns, cmd, options); - const queryOptions = applyCommonQueryOptions( - {}, - Object.assign(options, { ...pluckBSONSerializeOptions(options) }) - ); - - queryOptions.fullResult = true; - if (typeof query.documentsReturnedIn === 'string') { - queryOptions.documentsReturnedIn = query.documentsReturnedIn; - } - - server.s.pool.write(query, queryOptions, callback); - return; - } - - const readPreference = getReadPreference(cmd, options); - let findCmd = prepareFindCommand(server, ns, cmd); - - // If we have explain, we need to rewrite the find command - // to wrap it in the explain command - const explain = Explain.fromOptions(options); - if (explain) { - findCmd = decorateWithExplain(findCmd, explain); - } - - // NOTE: This actually modifies the passed in cmd, and our code _depends_ on this - // side-effect. Change this ASAP - cmd.virtual = false; - - const commandOptions = Object.assign( - { - documentsReturnedIn: 'firstBatch', - numberToReturn: 1, - slaveOk: readPreference.slaveOk() - }, - options - ); - - command(server, ns, findCmd, commandOptions, callback); -} - -function prepareFindCommand(server: Server, ns: string, cmd: Document) { - const findCmd: Document = { - find: collectionNamespace(ns) - }; - - if (cmd.query) { - if (cmd.query['$query']) { - findCmd.filter = cmd.query['$query']; - } else { - findCmd.filter = cmd.query; - } - } - - let sortValue = cmd.sort; - if (Array.isArray(sortValue)) { - const sortObject: Document = {}; - - if (sortValue.length > 0 && !Array.isArray(sortValue[0])) { - let sortDirection = sortValue[1]; - if (sortDirection === 'asc') { - sortDirection = 1; - } else if (sortDirection === 'desc') { - sortDirection = -1; - } - - sortObject[sortValue[0]] = sortDirection; - } else { - for (let i = 0; i < sortValue.length; i++) { - let sortDirection = sortValue[i][1]; - if (sortDirection === 'asc') { - sortDirection = 1; - } else if (sortDirection === 'desc') { - sortDirection = -1; - } - - sortObject[sortValue[i][0]] = sortDirection; - } - } - - sortValue = sortObject; - } - - if (typeof cmd.allowDiskUse === 'boolean') { - findCmd.allowDiskUse = cmd.allowDiskUse; - } - - if (cmd.sort) findCmd.sort = sortValue; - if (cmd.fields) findCmd.projection = cmd.fields; - if (cmd.hint) findCmd.hint = cmd.hint; - if (cmd.skip) findCmd.skip = cmd.skip; - if (cmd.limit) findCmd.limit = cmd.limit; - if (cmd.limit < 0) { - findCmd.limit = Math.abs(cmd.limit); - findCmd.singleBatch = true; - } - - if (typeof cmd.batchSize === 'number') { - if (cmd.batchSize < 0) { - if (cmd.limit !== 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) { - findCmd.limit = Math.abs(cmd.batchSize); - } - - findCmd.singleBatch = true; - } - - findCmd.batchSize = Math.abs(cmd.batchSize); - } - - if (cmd.comment) findCmd.comment = cmd.comment; - if (cmd.maxScan) findCmd.maxScan = cmd.maxScan; - if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS; - if (cmd.min) findCmd.min = cmd.min; - if (cmd.max) findCmd.max = cmd.max; - findCmd.returnKey = cmd.returnKey ? cmd.returnKey : false; - findCmd.showRecordId = cmd.showDiskLoc ? cmd.showDiskLoc : false; - if (cmd.snapshot) findCmd.snapshot = cmd.snapshot; - if (cmd.tailable) findCmd.tailable = cmd.tailable; - if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay; - if (cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout; - if (cmd.awaitData) findCmd.awaitData = cmd.awaitData; - if (cmd.awaitdata) findCmd.awaitData = cmd.awaitdata; - if (cmd.partial) findCmd.partial = cmd.partial; - if (cmd.collation) findCmd.collation = cmd.collation; - if (cmd.readConcern) findCmd.readConcern = cmd.readConcern; - - return findCmd; -} - -function prepareLegacyFindQuery( - server: Server, - ns: string, - cmd: Document, - options: FindOptions -): Query { - options = options || {}; - - const readPreference = getReadPreference(cmd, options); - const batchSize = cmd.batchSize || options.batchSize || 0; - const limit = cmd.limit || options.limit; - const numberToSkip = cmd.skip || options.skip || 0; - + const isExplain = typeof findCommand.$explain !== 'undefined'; + const readPreference = options.readPreference ?? ReadPreference.primary; + const batchSize = options.batchSize || 0; + const limit = options.limit; + const numberToSkip = options.skip || 0; let numberToReturn = 0; if ( limit && @@ -180,66 +45,57 @@ function prepareLegacyFindQuery( numberToReturn = batchSize; } - const findCmd: Document = {}; - if (isSharded(server) && readPreference) { - findCmd['$readPreference'] = readPreference.toJSON(); + if (isExplain) { + // nToReturn must be 0 (match all) or negative (match N and close cursor) + // nToReturn > 0 will give explain results equivalent to limit(0) + numberToReturn = -Math.abs(limit || 0); } - if (cmd.sort) findCmd['$orderby'] = cmd.sort; - if (cmd.hint) findCmd['$hint'] = cmd.hint; - if (cmd.snapshot) findCmd['$snapshot'] = cmd.snapshot; - if (typeof cmd.returnKey !== 'undefined') findCmd['$returnKey'] = cmd.returnKey; - if (cmd.maxScan) findCmd['$maxScan'] = cmd.maxScan; - if (cmd.min) findCmd['$min'] = cmd.min; - if (cmd.max) findCmd['$max'] = cmd.max; - if (typeof cmd.showDiskLoc !== 'undefined') { - findCmd['$showDiskLoc'] = cmd.showDiskLoc; - } else if (typeof cmd.showRecordId !== 'undefined') { - findCmd['$showDiskLoc'] = cmd.showRecordId; + const queryOptions: OpQueryOptions = { + numberToSkip, + numberToReturn, + pre32Limit: typeof limit === 'number' ? limit : undefined, + checkKeys: false, + slaveOk: readPreference.slaveOk() + }; + + if (options.projection) { + queryOptions.returnFieldSelector = options.projection; } - if (cmd.comment) findCmd['$comment'] = cmd.comment; - if (cmd.maxTimeMS) findCmd['$maxTimeMS'] = cmd.maxTimeMS; - if (options.explain) { - // nToReturn must be 0 (match all) or negative (match N and close cursor) - // nToReturn > 0 will give explain results equivalent to limit(0) - numberToReturn = -Math.abs(cmd.limit || 0); - findCmd['$explain'] = true; + const query = new Query(ns.toString(), findCommand, queryOptions); + if (typeof options.tailable === 'boolean') { + query.tailable = options.tailable; } - findCmd['$query'] = cmd.query; - if (cmd.readConcern && cmd.readConcern.level !== 'local') { - throw new MongoError( - `server find command does not support a readConcern level of ${cmd.readConcern.level}` - ); + if (typeof options.oplogReplay === 'boolean') { + query.oplogReplay = options.oplogReplay; } - if (cmd.readConcern) { - cmd = Object.assign({}, cmd); - delete cmd['readConcern']; + if (typeof options.timeout === 'boolean') { + query.noCursorTimeout = options.timeout; + } else if (typeof options.noCursorTimeout === 'boolean') { + query.noCursorTimeout = options.noCursorTimeout; } - const serializeFunctions = - typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false; - const ignoreUndefined = - typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false; + if (typeof options.awaitData === 'boolean') { + query.awaitData = options.awaitData; + } - const query = new Query(ns, findCmd, { - numberToSkip, - numberToReturn, - pre32Limit: typeof limit === 'number' ? limit : undefined, - checkKeys: false, - returnFieldSelector: cmd.fields, - serializeFunctions, - ignoreUndefined - }); + if (typeof options.partial === 'boolean') { + query.partial = options.partial; + } - if (typeof cmd.tailable === 'boolean') query.tailable = cmd.tailable; - if (typeof cmd.oplogReplay === 'boolean') query.oplogReplay = cmd.oplogReplay; - if (typeof cmd.noCursorTimeout === 'boolean') query.noCursorTimeout = cmd.noCursorTimeout; - if (typeof cmd.awaitData === 'boolean') query.awaitData = cmd.awaitData; - if (typeof cmd.partial === 'boolean') query.partial = cmd.partial; + server.s.pool.write( + query, + { fullResult: true, ...pluckBSONSerializeOptions(options) }, + (err, result) => { + if (err || !result) return callback(err, result); + if (isExplain && result.documents && result.documents[0]) { + return callback(undefined, result.documents[0]); + } - query.slaveOk = readPreference.slaveOk(); - return query; + callback(undefined, result); + } + ); } diff --git a/src/index.ts b/src/index.ts index a953cebe81..58e38af436 100644 --- a/src/index.ts +++ b/src/index.ts @@ -143,7 +143,6 @@ export type { UpdateOptions as WireUpdateOptions, RemoveOptions as WireRemoveOptions } from './cmap/wire_protocol/index'; -export type { QueryOptions } from './cmap/wire_protocol/query'; export type { CollationOptions, WriteCommandOptions } from './cmap/wire_protocol/write_command'; export type { CollectionPrivate, CollectionOptions } from './collection'; export type { AggregationCursorOptions } from './cursor/aggregation_cursor'; diff --git a/src/operations/find.ts b/src/operations/find.ts index 9256dfb64c..2a64dbe0f0 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -1,16 +1,23 @@ import { Aspect, defineAspects, Hint } from './operation'; -import { maxWireVersion, MongoDBNamespace, Callback, normalizeHintField } from '../utils'; +import { + maxWireVersion, + MongoDBNamespace, + Callback, + normalizeHintField, + decorateWithExplain +} from '../utils'; import { MongoError } from '../error'; import type { Document } from '../bson'; import type { Server } from '../sdam/server'; import type { Collection } from '../collection'; import type { CollationOptions } from '../cmap/wire_protocol/write_command'; -import type { QueryOptions } from '../cmap/wire_protocol/query'; import { CommandOperation, CommandOperationOptions } from './command'; import { Sort, formatSort } from '../sort'; +import { isSharded } from '../cmap/wire_protocol/shared'; +import { ReadConcern } from '../read_concern'; /** @public */ -export interface FindOptions extends QueryOptions, CommandOperationOptions { +export interface FindOptions extends CommandOperationOptions { /** Sets the limit of documents returned in the query. */ limit?: number; /** Set to sort the documents coming back from the query. Array of indexes, `[['a', 1]]` etc. */ @@ -59,11 +66,8 @@ const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5; /** @internal */ export class FindOperation extends CommandOperation { - cmd: Document; filter: Document; - hint?: Hint; - constructor( collection: Collection | undefined, ns: MongoDBNamespace, @@ -89,169 +93,243 @@ export class FindOperation extends CommandOperation { // special case passing in an ObjectId as a filter this.filter = filter != null && filter._bsontype === 'ObjectID' ? { _id: filter } : filter; - - // FIXME: this should be removed as part of NODE-2790 - this.cmd = { - find: this.ns.toString(), - query: this.filter - }; } execute(server: Server, callback: Callback): void { - // copied from `CommandOperationV2`, to be subclassed in the future this.server = server; - const serverWireVersion = maxWireVersion(server); + const serverWireVersion = maxWireVersion(server); const options = this.options; if (typeof options.allowDiskUse !== 'undefined' && serverWireVersion < 4) { callback(new MongoError('The `allowDiskUse` option is not supported on MongoDB < 3.2')); return; } - const findCommand: Document = Object.assign({}, this.cmd); + if (options.collation && serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) { + callback( + new MongoError( + `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation` + ) + ); - if (options.sort) { - findCommand.sort = formatSort(options.sort); + return; } - if (options.projection) { - let projection = options.projection; - if (projection && !Buffer.isBuffer(projection) && Array.isArray(projection)) { - projection = projection.length - ? projection.reduce((result, field) => { - result[field] = 1; - return result; - }, {}) - : { _id: 1 }; + if (serverWireVersion < 4) { + if (this.readConcern && this.readConcern.level !== 'local') { + callback( + new MongoError( + `server find command does not support a readConcern level of ${this.readConcern.level}` + ) + ); + + return; } - findCommand.fields = projection; + const findCommand = makeLegacyFindCommand(this.ns, this.filter, options); + if (isSharded(server) && this.readPreference) { + findCommand.$readPreference = this.readPreference.toJSON(); + } + + server.query( + this.ns, + findCommand, + { + ...this.options, + ...this.bsonOptions, + documentsReturnedIn: 'firstBatch', + readPreference: this.readPreference + }, + callback + ); + + return; } - if (options.hint) { - findCommand.hint = normalizeHintField(options.hint); + let findCommand = makeFindCommand(this.ns, this.filter, options); + if (this.explain) { + findCommand = decorateWithExplain(findCommand, this.explain); } - if (typeof options.skip === 'number') { - findCommand.skip = options.skip; + server.command( + this.ns.toString(), + findCommand, + { + fullResult: !!this.fullResponse, + ...this.options, + ...this.bsonOptions, + documentsReturnedIn: 'firstBatch' + }, + callback + ); + } +} + +function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOptions): Document { + const findCommand: Document = { + find: ns.collection, + filter + }; + + if (options.sort) { + findCommand.sort = formatSort(options.sort); + } + + if (options.projection) { + let projection = options.projection; + if (projection && Array.isArray(projection)) { + projection = projection.length + ? projection.reduce((result, field) => { + result[field] = 1; + return result; + }, {}) + : { _id: 1 }; } - if (typeof options.limit === 'number') { - if (options.limit < 0 && maxWireVersion(server) >= 4) { - findCommand.limit = Math.abs(options.limit); - findCommand.singleBatch = true; - } else { - findCommand.limit = options.limit; - } + findCommand.projection = projection; + } + + if (options.hint) { + findCommand.hint = normalizeHintField(options.hint); + } + + if (typeof options.skip === 'number') { + findCommand.skip = options.skip; + } + + if (typeof options.limit === 'number') { + if (options.limit < 0) { + findCommand.limit = -options.limit; + findCommand.singleBatch = true; + } else { + findCommand.limit = options.limit; } + } - if (typeof options.batchSize === 'number') { - if (options.batchSize < 0) { - if ( - options.limit && - options.limit !== 0 && - Math.abs(options.batchSize) < Math.abs(options.limit) - ) { - findCommand.limit = Math.abs(options.batchSize); - } - - findCommand.singleBatch = true; - } else { - findCommand.batchSize = Math.abs(options.batchSize); + if (typeof options.batchSize === 'number') { + if (options.batchSize < 0) { + if ( + options.limit && + options.limit !== 0 && + Math.abs(options.batchSize) < Math.abs(options.limit) + ) { + findCommand.limit = -options.batchSize; } - } - if (typeof options.singleBatch === 'boolean') { - findCommand.singleBatch = options.singleBatch; + findCommand.singleBatch = true; + } else { + findCommand.batchSize = options.batchSize; } + } - if (options.comment) { - findCommand.comment = options.comment; - } + if (typeof options.singleBatch === 'boolean') { + findCommand.singleBatch = options.singleBatch; + } - if (typeof options.maxTimeMS === 'number') { - findCommand.maxTimeMS = options.maxTimeMS; - } + if (options.comment) { + findCommand.comment = options.comment; + } - if (this.readConcern) { - findCommand.readConcern = this.readConcern.toJSON(); - } + if (typeof options.maxTimeMS === 'number') { + findCommand.maxTimeMS = options.maxTimeMS; + } - if (options.max) { - findCommand.max = options.max; - } + const readConcern = ReadConcern.fromOptions(options); + if (readConcern) { + findCommand.readConcern = readConcern.toJSON(); + } - if (options.min) { - findCommand.min = options.min; - } + if (options.max) { + findCommand.max = options.max; + } - if (typeof options.returnKey === 'boolean') { - findCommand.returnKey = options.returnKey; - } + if (options.min) { + findCommand.min = options.min; + } - if (typeof options.showRecordId === 'boolean') { - findCommand.showRecordId = options.showRecordId; - } + if (typeof options.returnKey === 'boolean') { + findCommand.returnKey = options.returnKey; + } - if (typeof options.tailable === 'boolean') { - findCommand.tailable = options.tailable; - } + if (typeof options.showRecordId === 'boolean') { + findCommand.showRecordId = options.showRecordId; + } - if (typeof options.timeout === 'boolean') { - findCommand.noCursorTimeout = options.timeout; - } else if (typeof options.noCursorTimeout === 'boolean') { - findCommand.noCursorTimeout = options.noCursorTimeout; - } + if (typeof options.tailable === 'boolean') { + findCommand.tailable = options.tailable; + } - if (typeof options.awaitData === 'boolean') { - findCommand.awaitData = options.awaitData; - } + if (typeof options.timeout === 'boolean') { + findCommand.noCursorTimeout = options.timeout; + } else if (typeof options.noCursorTimeout === 'boolean') { + findCommand.noCursorTimeout = options.noCursorTimeout; + } - if (typeof options.allowPartialResults === 'boolean') { - findCommand.allowPartialResults = options.allowPartialResults; - } + if (typeof options.awaitData === 'boolean') { + findCommand.awaitData = options.awaitData; + } - if (options.collation) { - if (serverWireVersion < SUPPORTS_WRITE_CONCERN_AND_COLLATION) { - callback( - new MongoError( - `Server ${server.name}, which reports wire version ${serverWireVersion}, does not support collation` - ) - ); + if (typeof options.allowPartialResults === 'boolean') { + findCommand.allowPartialResults = options.allowPartialResults; + } - return; - } + if (options.collation) { + findCommand.collation = options.collation; + } - findCommand.collation = options.collation; - } + if (typeof options.allowDiskUse === 'boolean') { + findCommand.allowDiskUse = options.allowDiskUse; + } - if (typeof options.allowDiskUse === 'boolean') { - findCommand.allowDiskUse = options.allowDiskUse; - } + return findCommand; +} - if (this.explain) { - // TODO: For now, we need to manually ensure explain is in the options. This will change after cursor refactor. - this.options.explain = this.explain.verbosity; - } +function makeLegacyFindCommand( + ns: MongoDBNamespace, + filter: Document, + options: FindOptions +): Document { + const findCommand: Document = { + $query: filter + }; + + if (options.sort) { + findCommand.$orderby = formatSort(options.sort); + } - // TODO: use `MongoDBNamespace` through and through - server.query( - this.ns.toString(), - findCommand, - { fullResult: !!this.fullResponse, ...this.options, ...this.bsonOptions }, - (err, result) => { - if (err) return callback(err); - if (this.explain) { - // TODO: NODE-2900 - if (result.documents && result.documents[0]) { - return callback(undefined, result.documents[0]); - } - } - - callback(undefined, result); - } - ); + if (options.hint) { + findCommand.$hint = normalizeHintField(options.hint); + } + + if (typeof options.returnKey === 'boolean') { + findCommand.$returnKey = options.returnKey; + } + + if (options.max) { + findCommand.$max = options.max; } + + if (options.min) { + findCommand.$min = options.min; + } + + if (typeof options.showRecordId === 'boolean') { + findCommand.$showDiskLoc = options.showRecordId; + } + + if (options.comment) { + findCommand.$comment = options.comment; + } + + if (typeof options.maxTimeMS === 'number') { + findCommand.$maxTimeMS = options.maxTimeMS; + } + + if (typeof options.explain !== 'undefined') { + findCommand.$explain = true; + } + + return findCommand; } defineAspects(FindOperation, [Aspect.READ_OPERATION, Aspect.RETRYABLE, Aspect.EXPLAINABLE]); diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index b4ea309cfe..fbb60cf8c2 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -15,7 +15,6 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; import type { Db } from '../db'; import type { CollationOptions } from '../cmap/wire_protocol/write_command'; -import type { FindOptions } from './find'; import { AbstractCursor } from '../cursor/abstract_cursor'; import type { ClientSession } from '../sessions'; import { executeOperation, ExecutionResult } from './execute_operation'; @@ -337,13 +336,13 @@ export class ListIndexesOperation extends CommandOperation): void { const serverWireVersion = maxWireVersion(server); if (serverWireVersion < LIST_INDEXES_WIRE_VERSION) { - const systemIndexesNS = this.collectionNamespace.withCollection('system.indexes').toString(); + const systemIndexesNS = this.collectionNamespace.withCollection('system.indexes'); const collectionNS = this.collectionNamespace.toString(); server.query( systemIndexesNS, { query: { ns: collectionNS } }, - this.options as FindOptions, + { ...this.options, readPreference: this.readPreference }, callback ); return; diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 1c115972c2..70362b53fd 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -1,6 +1,6 @@ import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; -import { maxWireVersion, Callback, getTopology } from '../utils'; +import { maxWireVersion, Callback, getTopology, MongoDBNamespace } from '../utils'; import * as CONSTANTS from '../constants'; import type { Document } from '../bson'; import type { Server } from '../sdam/server'; @@ -76,9 +76,9 @@ export class ListCollectionsOperation extends CommandOperation { if (result && result.documents && Array.isArray(result.documents)) { result.documents = result.documents.map(documentTransform); diff --git a/src/sdam/server.ts b/src/sdam/server.ts index 5e63ca351b..8e3f1799b2 100644 --- a/src/sdam/server.ts +++ b/src/sdam/server.ts @@ -14,7 +14,8 @@ import { maxWireVersion, ClientMetadataOptions, Callback, - CallbackWithType + CallbackWithType, + MongoDBNamespace } from '../utils'; import { ServerType, @@ -39,12 +40,11 @@ import type { MongoCredentials } from '../cmap/auth/mongo_credentials'; import type { ServerHeartbeatSucceededEvent } from './events'; import type { ClientSession } from '../sessions'; import type { CommandOptions } from '../cmap/wire_protocol/command'; -import type { QueryOptions } from '../cmap/wire_protocol/query'; import type { GetMoreOptions } from '../cmap/wire_protocol/get_more'; import type { WriteCommandOptions } from '../cmap/wire_protocol/write_command'; import type { Document, Long } from '../bson'; import type { AutoEncrypter } from '../deps'; -import type { FindOptions } from '../operations/find'; +import type { QueryOptions } from '../cmap/wire_protocol/query'; // Used for filtering out fields for logging const DEBUG_FIELDS = [ @@ -302,7 +302,7 @@ export class Server extends EventEmitter { } /** Execute a query against the server */ - query(ns: string, cmd: Document, options: FindOptions, callback: Callback): void { + query(ns: MongoDBNamespace, cmd: Document, options: QueryOptions, callback: Callback): void { if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) { callback(new MongoError('server is closed')); return; @@ -527,7 +527,7 @@ function makeOperationHandler( server: Server, connection: Connection, cmd: Document, - options: CommandOptions | WriteCommandOptions | QueryOptions | GetMoreOptions | undefined, + options: CommandOptions | WriteCommandOptions | GetMoreOptions | undefined, callback: Callback ): CallbackWithType { const session = options?.session; diff --git a/test/functional/max_staleness.test.js b/test/functional/max_staleness.test.js index f72a7381d4..1066f8a9f8 100644 --- a/test/functional/max_staleness.test.js +++ b/test/functional/max_staleness.test.js @@ -64,7 +64,7 @@ describe('Max Staleness', function () { .toArray(function (err) { expect(err).to.not.exist; expect(test.checkCommand).to.eql({ - $query: { find: 'test', filter: {}, returnKey: false, showRecordId: false }, + $query: { find: 'test', filter: {} }, $readPreference: { mode: 'secondary', maxStalenessSeconds: 250 } }); @@ -99,7 +99,7 @@ describe('Max Staleness', function () { .toArray(function (err) { expect(err).to.not.exist; expect(test.checkCommand).to.eql({ - $query: { find: 'test', filter: {}, returnKey: false, showRecordId: false }, + $query: { find: 'test', filter: {} }, $readPreference: { mode: 'secondary', maxStalenessSeconds: 250 } }); @@ -135,7 +135,7 @@ describe('Max Staleness', function () { .toArray(function (err) { expect(err).to.not.exist; expect(test.checkCommand).to.eql({ - $query: { find: 'test', filter: {}, returnKey: false, showRecordId: false }, + $query: { find: 'test', filter: {} }, $readPreference: { mode: 'secondary', maxStalenessSeconds: 250 } }); @@ -170,7 +170,7 @@ describe('Max Staleness', function () { .toArray(function (err) { expect(err).to.not.exist; expect(test.checkCommand).to.eql({ - $query: { find: 'test', filter: {}, returnKey: false, showRecordId: false }, + $query: { find: 'test', filter: {} }, $readPreference: { mode: 'secondary', maxStalenessSeconds: 250 } });