diff --git a/src/collection.ts b/src/collection.ts index 223737c32a..37339881c3 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -17,6 +17,7 @@ import { } from './cursor/list_search_indexes_cursor'; import type { Db } from './db'; import { MongoAPIError, MongoInvalidArgumentError, MongoOperationTimeoutError } from './error'; +import { type ExplainCommandOptions, type ExplainVerbosityLike } from './explain'; import type { MongoClient, PkFactory } from './mongo_client'; import type { Abortable, @@ -863,6 +864,11 @@ export class Collection { filter: Filter, options: DistinctOptions ): Promise[Key]>>>; + distinct>( + key: Key, + filter: Filter, + options: DistinctOptions & { explain: ExplainVerbosityLike | ExplainCommandOptions } + ): Promise; // Embedded documents overload distinct(key: string): Promise; diff --git a/src/cursor/run_command_cursor.ts b/src/cursor/run_command_cursor.ts index 0e9992a2aa..b8969b4c44 100644 --- a/src/cursor/run_command_cursor.ts +++ b/src/cursor/run_command_cursor.ts @@ -4,7 +4,7 @@ import type { Db } from '../db'; import { MongoAPIError, MongoRuntimeError } from '../error'; import { executeOperation } from '../operations/execute_operation'; import { GetMoreOperation } from '../operations/get_more'; -import { RunCommandOperation } from '../operations/run_command'; +import { RunCommandOperation, RunCursorCommandOperation } from '../operations/run_command'; import type { ReadConcernLike } from '../read_concern'; import type { ReadPreferenceLike } from '../read_preference'; import type { ClientSession } from '../sessions'; @@ -143,7 +143,7 @@ export class RunCommandCursor extends AbstractCursor { /** @internal */ protected async _initialize(session: ClientSession): Promise { - const operation = new RunCommandOperation(this.db, this.command, { + const operation = new RunCursorCommandOperation(this.db, this.command, { ...this.cursorOptions, session: session, readPreference: this.cursorOptions.readPreference, diff --git a/src/operations/distinct.ts b/src/operations/distinct.ts index cb6891fc47..2302a3c081 100644 --- a/src/operations/distinct.ts +++ b/src/operations/distinct.ts @@ -1,10 +1,8 @@ -import type { Document } from '../bson'; +import { type Document } from '../bson'; +import { type Connection } from '../cmap/connection'; +import { MongoDBResponse } from '../cmap/wire_protocol/responses'; import type { Collection } from '../collection'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type TimeoutContext } from '../timeout'; -import { decorateWithCollation, decorateWithReadConcern } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { type CommandOperationOptions, ModernizedCommandOperation } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -27,7 +25,8 @@ export type DistinctOptions = CommandOperationOptions & { * Return a list of distinct values for the given key across a collection. * @internal */ -export class DistinctOperation extends CommandOperation { +export class DistinctOperation extends ModernizedCommandOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; override options: DistinctOptions; collection: Collection; /** Field of the document to find distinct values for. */ @@ -56,48 +55,21 @@ export class DistinctOperation extends CommandOperation { return 'distinct' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { - const coll = this.collection; - const key = this.key; - const query = this.query; - const options = this.options; - - // Distinct command - const cmd: Document = { - distinct: coll.collectionName, - key: key, - query: query + override buildCommandDocument(_connection: Connection): Document { + return { + distinct: this.collection.collectionName, + key: this.key, + query: this.query }; + } - // Add maxTimeMS if defined - if (typeof options.maxTimeMS === 'number') { - cmd.maxTimeMS = options.maxTimeMS; - } - - // we check for undefined specifically here to allow falsy values - // eslint-disable-next-line no-restricted-syntax - if (typeof options.comment !== 'undefined') { - cmd.comment = options.comment; - } - - if (options.hint != null) { - cmd.hint = options.hint; + override handleOk( + response: InstanceType + ): any[] | Document { + if (this.explain) { + return response.toObject(this.bsonOptions); } - - // Do we have a readConcern specified - decorateWithReadConcern(cmd, coll, options); - - // Have we specified collation - decorateWithCollation(cmd, coll, options); - - const result = await super.executeCommand(server, session, cmd, timeoutContext); - - // @ts-expect-error: Explain always returns a document - return this.explain ? result : result.values; + return response.toObject(this.bsonOptions).values; } } diff --git a/src/operations/profiling_level.ts b/src/operations/profiling_level.ts index 7c860a244b..90b310b8af 100644 --- a/src/operations/profiling_level.ts +++ b/src/operations/profiling_level.ts @@ -1,15 +1,22 @@ +import { BSONType, type Document } from '../bson'; +import { type Connection } from '../cmap/connection'; +import { MongoDBResponse } from '../cmap/wire_protocol/responses'; import type { Db } from '../db'; import { MongoUnexpectedServerResponseError } from '../error'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type TimeoutContext } from '../timeout'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { type CommandOperationOptions, ModernizedCommandOperation } from './command'; /** @public */ export type ProfilingLevelOptions = CommandOperationOptions; +class ProfilingLevelResponse extends MongoDBResponse { + get was() { + return this.get('was', BSONType.int, true); + } +} + /** @internal */ -export class ProfilingLevelOperation extends CommandOperation { +export class ProfilingLevelOperation extends ModernizedCommandOperation { + override SERVER_COMMAND_RESPONSE_TYPE = ProfilingLevelResponse; override options: ProfilingLevelOptions; constructor(db: Db, options: ProfilingLevelOptions) { @@ -21,14 +28,13 @@ export class ProfilingLevelOperation extends CommandOperation { return 'profile' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { - const doc = await super.executeCommand(server, session, { profile: -1 }, timeoutContext); - if (doc.ok === 1) { - const was = doc.was; + override buildCommandDocument(_connection: Connection): Document { + return { profile: -1 }; + } + + override handleOk(response: InstanceType): string { + if (response.ok === 1) { + const was = response.was; if (was === 0) return 'off'; if (was === 1) return 'slow_only'; if (was === 2) return 'all'; diff --git a/src/operations/remove_user.ts b/src/operations/remove_user.ts index 7f484ba89a..e0c73d29aa 100644 --- a/src/operations/remove_user.ts +++ b/src/operations/remove_user.ts @@ -1,15 +1,16 @@ +import { type Document } from '../bson'; +import { type Connection } from '../cmap/connection'; +import { MongoDBResponse } from '../cmap/wire_protocol/responses'; import type { Db } from '../db'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type TimeoutContext } from '../timeout'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { type CommandOperationOptions, ModernizedCommandOperation } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ export type RemoveUserOptions = CommandOperationOptions; /** @internal */ -export class RemoveUserOperation extends CommandOperation { +export class RemoveUserOperation extends ModernizedCommandOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; override options: RemoveUserOptions; username: string; @@ -23,12 +24,11 @@ export class RemoveUserOperation extends CommandOperation { return 'dropUser' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { - await super.executeCommand(server, session, { dropUser: this.username }, timeoutContext); + override buildCommandDocument(_connection: Connection): Document { + return { dropUser: this.username }; + } + + override handleOk(_response: InstanceType): boolean { return true; } } diff --git a/src/operations/run_command.ts b/src/operations/run_command.ts index 4b967ee3cd..506ae70a8e 100644 --- a/src/operations/run_command.ts +++ b/src/operations/run_command.ts @@ -1,13 +1,13 @@ import type { BSONSerializeOptions, Document } from '../bson'; -import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses'; +import { type Connection } from '../cmap/connection'; +import { CursorResponse, MongoDBResponse, type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses'; import { type Db } from '../db'; -import { type TODO_NODE_3286 } from '../mongo_types'; import type { ReadPreferenceLike } from '../read_preference'; -import type { Server } from '../sdam/server'; +import type { ServerCommandOptions } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type TimeoutContext } from '../timeout'; import { MongoDBNamespace } from '../utils'; -import { AbstractOperation } from './operation'; +import { ModernizedOperation } from './operation'; /** @public */ export type RunCommandOptions = { @@ -25,7 +25,8 @@ export type RunCommandOptions = { } & BSONSerializeOptions; /** @internal */ -export class RunCommandOperation extends AbstractOperation { +export class RunCommandOperation extends ModernizedOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; command: Document; override options: RunCommandOptions & { responseType?: MongoDBResponseConstructor }; @@ -44,29 +45,52 @@ export class RunCommandOperation extends AbstractOperation { return 'runCommand' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { - this.server = server; - const res: TODO_NODE_3286 = await server.command( - this.ns, - this.command, - { - ...this.options, - readPreference: this.readPreference, - session, - timeoutContext - }, - this.options.responseType - ); - - return res; + override buildCommand(_connection: Connection, _session?: ClientSession): Document { + return this.command; + } + + override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions { + return { session: this.session, timeoutContext }; } } -export class RunAdminCommandOperation extends AbstractOperation { +/** @internal */ +export class RunCursorCommandOperation extends ModernizedOperation { + override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse; + command: Document; + override options: RunCommandOptions & { responseType?: MongoDBResponseConstructor }; + + constructor( + parent: Db, + command: Document, + options: RunCommandOptions & { responseType?: MongoDBResponseConstructor } + ) { + super(options); + this.command = command; + this.options = options; + this.ns = parent.s.namespace.withCollection('$cmd'); + } + + override get commandName() { + return 'runCommand' as const; + } + + override buildCommand(_connection: Connection, _session?: ClientSession): Document { + return this.command; + } + + override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions { + return { session: this.session, timeoutContext }; + } + override handleOk( + response: InstanceType + ): CursorResponse { + return response; + } +} + +export class RunAdminCommandOperation extends ModernizedOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; command: Document; override options: RunCommandOptions & { noResponse?: boolean; @@ -90,18 +114,11 @@ export class RunAdminCommandOperation extends AbstractOperation return 'runCommand' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { - this.server = server; - const res: TODO_NODE_3286 = await server.command(this.ns, this.command, { - ...this.options, - readPreference: this.readPreference, - session, - timeoutContext - }); - return res; + override buildCommand(_connection: Connection, _session?: ClientSession): Document { + return this.command; + } + + override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions { + return { session: this.session, timeoutContext }; } } diff --git a/src/operations/set_profiling_level.ts b/src/operations/set_profiling_level.ts index d76473f263..e3460bbfce 100644 --- a/src/operations/set_profiling_level.ts +++ b/src/operations/set_profiling_level.ts @@ -1,10 +1,10 @@ +import { type Document } from '../bson'; +import { type Connection } from '../cmap/connection'; +import { MongoDBResponse } from '../cmap/wire_protocol/responses'; import type { Db } from '../db'; import { MongoInvalidArgumentError } from '../error'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type TimeoutContext } from '../timeout'; import { enumToString } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { type CommandOperationOptions, ModernizedCommandOperation } from './command'; const levelValues = new Set(['off', 'slow_only', 'all']); @@ -22,7 +22,8 @@ export type ProfilingLevel = (typeof ProfilingLevel)[keyof typeof ProfilingLevel export type SetProfilingLevelOptions = CommandOperationOptions; /** @internal */ -export class SetProfilingLevelOperation extends CommandOperation { +export class SetProfilingLevelOperation extends ModernizedCommandOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; override options: SetProfilingLevelOptions; level: ProfilingLevel; profile: 0 | 1 | 2; @@ -52,21 +53,22 @@ export class SetProfilingLevelOperation extends CommandOperation return 'profile' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { + override buildCommandDocument(_connection: Connection): Document { const level = this.level; if (!levelValues.has(level)) { + // TODO(NODE-3483): Determine error to put here throw new MongoInvalidArgumentError( `Profiling level must be one of "${enumToString(ProfilingLevel)}"` ); } - // TODO(NODE-3483): Determine error to put here - await super.executeCommand(server, session, { profile: this.profile }, timeoutContext); - return level; + return { profile: this.profile }; + } + + override handleOk( + _response: InstanceType + ): ProfilingLevel { + return this.level; } } diff --git a/src/operations/stats.ts b/src/operations/stats.ts index aafd3bf1ba..399bb9c531 100644 --- a/src/operations/stats.ts +++ b/src/operations/stats.ts @@ -1,9 +1,8 @@ import type { Document } from '../bson'; +import { type Connection } from '../cmap/connection'; +import { MongoDBResponse } from '../cmap/wire_protocol/responses'; import type { Db } from '../db'; -import type { Server } from '../sdam/server'; -import type { ClientSession } from '../sessions'; -import { type TimeoutContext } from '../timeout'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { type CommandOperationOptions, ModernizedCommandOperation } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -13,7 +12,8 @@ export interface DbStatsOptions extends CommandOperationOptions { } /** @internal */ -export class DbStatsOperation extends CommandOperation { +export class DbStatsOperation extends ModernizedCommandOperation { + override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse; override options: DbStatsOptions; constructor(db: Db, options: DbStatsOptions) { @@ -25,17 +25,12 @@ export class DbStatsOperation extends CommandOperation { return 'dbStats' as const; } - override async execute( - server: Server, - session: ClientSession | undefined, - timeoutContext: TimeoutContext - ): Promise { + override buildCommandDocument(_connection: Connection): Document { const command: Document = { dbStats: true }; if (this.options.scale != null) { command.scale = this.options.scale; } - - return await super.executeCommand(server, session, command, timeoutContext); + return command; } }