Skip to content

refactor(NODE-7086): refactor misc operations #4613

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -863,6 +864,11 @@ export class Collection<TSchema extends Document = Document> {
filter: Filter<TSchema>,
options: DistinctOptions
): Promise<Array<Flatten<WithId<TSchema>[Key]>>>;
distinct<Key extends keyof WithId<TSchema>>(
key: Key,
filter: Filter<TSchema>,
options: DistinctOptions & { explain: ExplainVerbosityLike | ExplainCommandOptions }
): Promise<Document>;

// Embedded documents overload
distinct(key: string): Promise<any[]>;
Expand Down
4 changes: 2 additions & 2 deletions src/cursor/run_command_cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -143,7 +143,7 @@ export class RunCommandCursor extends AbstractCursor {

/** @internal */
protected async _initialize(session: ClientSession): Promise<InitialCursorResponse> {
const operation = new RunCommandOperation<CursorResponse>(this.db, this.command, {
const operation = new RunCursorCommandOperation(this.db, this.command, {
...this.cursorOptions,
session: session,
readPreference: this.cursorOptions.readPreference,
Expand Down
64 changes: 18 additions & 46 deletions src/operations/distinct.ts
Original file line number Diff line number Diff line change
@@ -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 */
Expand All @@ -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<any[]> {
export class DistinctOperation extends ModernizedCommandOperation<any[] | Document> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
override options: DistinctOptions;
collection: Collection;
/** Field of the document to find distinct values for. */
Expand Down Expand Up @@ -56,48 +55,21 @@ export class DistinctOperation extends CommandOperation<any[]> {
return 'distinct' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<any[]> {
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<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): 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;
}
}

Expand Down
32 changes: 19 additions & 13 deletions src/operations/profiling_level.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
export class ProfilingLevelOperation extends ModernizedCommandOperation<string> {
override SERVER_COMMAND_RESPONSE_TYPE = ProfilingLevelResponse;
override options: ProfilingLevelOptions;

constructor(db: Db, options: ProfilingLevelOptions) {
Expand All @@ -21,14 +28,13 @@ export class ProfilingLevelOperation extends CommandOperation<string> {
return 'profile' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<string> {
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<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): 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';
Expand Down
22 changes: 11 additions & 11 deletions src/operations/remove_user.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
export class RemoveUserOperation extends ModernizedCommandOperation<boolean> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
override options: RemoveUserOptions;
username: string;

Expand All @@ -23,12 +24,11 @@ export class RemoveUserOperation extends CommandOperation<boolean> {
return 'dropUser' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<boolean> {
await super.executeCommand(server, session, { dropUser: this.username }, timeoutContext);
override buildCommandDocument(_connection: Connection): Document {
return { dropUser: this.username };
}

override handleOk(_response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>): boolean {
return true;
}
}
Expand Down
93 changes: 55 additions & 38 deletions src/operations/run_command.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -25,7 +25,8 @@ export type RunCommandOptions = {
} & BSONSerializeOptions;

/** @internal */
export class RunCommandOperation<T = Document> extends AbstractOperation<T> {
export class RunCommandOperation<T = Document> extends ModernizedOperation<T> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
command: Document;
override options: RunCommandOptions & { responseType?: MongoDBResponseConstructor };

Expand All @@ -44,29 +45,52 @@ export class RunCommandOperation<T = Document> extends AbstractOperation<T> {
return 'runCommand' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<T> {
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<T = Document> extends AbstractOperation<T> {
/** @internal */
export class RunCursorCommandOperation extends ModernizedOperation<CursorResponse> {
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<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): CursorResponse {
return response;
}
}

export class RunAdminCommandOperation<T = Document> extends ModernizedOperation<T> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
command: Document;
override options: RunCommandOptions & {
noResponse?: boolean;
Expand All @@ -90,18 +114,11 @@ export class RunAdminCommandOperation<T = Document> extends AbstractOperation<T>
return 'runCommand' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<T> {
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 };
}
}
28 changes: 15 additions & 13 deletions src/operations/set_profiling_level.ts
Original file line number Diff line number Diff line change
@@ -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']);

Expand All @@ -22,7 +22,8 @@ export type ProfilingLevel = (typeof ProfilingLevel)[keyof typeof ProfilingLevel
export type SetProfilingLevelOptions = CommandOperationOptions;

/** @internal */
export class SetProfilingLevelOperation extends CommandOperation<ProfilingLevel> {
export class SetProfilingLevelOperation extends ModernizedCommandOperation<ProfilingLevel> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
override options: SetProfilingLevelOptions;
level: ProfilingLevel;
profile: 0 | 1 | 2;
Expand Down Expand Up @@ -52,21 +53,22 @@ export class SetProfilingLevelOperation extends CommandOperation<ProfilingLevel>
return 'profile' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<ProfilingLevel> {
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<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): ProfilingLevel {
return this.level;
}
}
Loading