Skip to content

refactor(NODE-7090): refactor remaining cursor operations to subclass ModernizedOperation #4609

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

Merged
merged 5 commits into from
Aug 5, 2025
Merged
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
77 changes: 28 additions & 49 deletions src/operations/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import type { Document } from '../bson';
import { CursorResponse, ExplainedCursorResponse } from '../cmap/wire_protocol/responses';
import { type AbstractCursorOptions, type CursorTimeoutMode } from '../cursor/abstract_cursor';
import { MongoInvalidArgumentError } from '../error';
import {
decorateWithExplain,
type ExplainOptions,
validateExplainTimeoutOptions
} from '../explain';
import { ReadConcern } from '../read_concern';
import type { Server } from '../sdam/server';
import type { ClientSession } from '../sessions';
import { type ExplainOptions } from '../explain';
import type { ServerCommandOptions } from '../sdam/server';
import { formatSort, type Sort } from '../sort';
import { type TimeoutContext } from '../timeout';
import { type MongoDBNamespace, normalizeHintField } from '../utils';
import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
import {
type CollationOptions,
type CommandOperationOptions,
ModernizedCommandOperation
} from './command';
import { Aspect, defineAspects, type Hint } from './operation';

/**
Expand Down Expand Up @@ -92,7 +90,9 @@ export interface FindOneOptions extends FindOptions {
}

/** @internal */
export class FindOperation extends CommandOperation<CursorResponse> {
export class FindOperation extends ModernizedCommandOperation<CursorResponse> {
override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse;

/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
Expand All @@ -116,39 +116,32 @@ export class FindOperation extends CommandOperation<CursorResponse> {

// special case passing in an ObjectId as a filter
this.filter = filter != null && filter._bsontype === 'ObjectId' ? { _id: filter } : filter;

this.SERVER_COMMAND_RESPONSE_TYPE = this.explain ? ExplainedCursorResponse : CursorResponse;
}

override get commandName() {
return 'find' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<CursorResponse> {
this.server = server;

const options = this.options;
override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
return {
...this.options,
...this.bsonOptions,
documentsReturnedIn: 'firstBatch',
session: this.session,
timeoutContext
};
}

let findCommand = makeFindCommand(this.ns, this.filter, options);
if (this.explain) {
validateExplainTimeoutOptions(this.options, this.explain);
findCommand = decorateWithExplain(findCommand, this.explain);
}
override handleOk(
response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): CursorResponse {
return response;
}

return await server.command(
this.ns,
findCommand,
{
...this.options,
...this.bsonOptions,
documentsReturnedIn: 'firstBatch',
session,
timeoutContext
},
this.explain ? ExplainedCursorResponse : CursorResponse
);
override buildCommandDocument(): Document {
return makeFindCommand(this.ns, this.filter, this.options);
}
}

Expand Down Expand Up @@ -217,15 +210,6 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp
findCommand.comment = options.comment;
}

if (typeof options.maxTimeMS === 'number') {
findCommand.maxTimeMS = options.maxTimeMS;
}

const readConcern = ReadConcern.fromOptions(options);
if (readConcern) {
findCommand.readConcern = readConcern.toJSON();
}

if (options.max) {
findCommand.max = options.max;
}
Expand Down Expand Up @@ -263,11 +247,6 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp
if (typeof options.allowPartialResults === 'boolean') {
findCommand.allowPartialResults = options.allowPartialResults;
}

if (options.collation) {
findCommand.collation = options.collation;
}

if (typeof options.allowDiskUse === 'boolean') {
findCommand.allowDiskUse = options.allowDiskUse;
}
Expand Down
38 changes: 18 additions & 20 deletions src/operations/get_more.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Long } from '../bson';
import type { Document, Long } from '../bson';
import { type Connection } from '../cmap/connection';
import { CursorResponse } from '../cmap/wire_protocol/responses';
import { MongoRuntimeError } from '../error';
import type { Server } from '../sdam/server';
import type { ClientSession } from '../sessions';
import type { Server, ServerCommandOptions } from '../sdam/server';
import { type TimeoutContext } from '../timeout';
import { maxWireVersion, type MongoDBNamespace } from '../utils';
import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
import { Aspect, defineAspects, ModernizedOperation, type OperationOptions } from './operation';

/** @internal */
export interface GetMoreOptions extends OperationOptions {
Expand Down Expand Up @@ -37,7 +37,8 @@ export interface GetMoreCommand {
}

/** @internal */
export class GetMoreOperation extends AbstractOperation {
export class GetMoreOperation extends ModernizedOperation<CursorResponse> {
override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse;
cursorId: Long;
override options: GetMoreOptions;

Expand All @@ -53,19 +54,8 @@ export class GetMoreOperation extends AbstractOperation {
override get commandName() {
return 'getMore' as const;
}
/**
* Although there is a server already associated with the get more operation, the signature
* for execute passes a server so we will just use that one.
*/
override async execute(
server: Server,
_session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<CursorResponse> {
if (server !== this.server) {
throw new MongoRuntimeError('Getmore must run on the same server operation began on');
}

override buildCommand(connection: Connection): Document {
if (this.cursorId == null || this.cursorId.isZero()) {
throw new MongoRuntimeError('Unable to iterate cursor with no id');
}
Expand All @@ -92,18 +82,26 @@ export class GetMoreOperation extends AbstractOperation {

// we check for undefined specifically here to allow falsy values
// eslint-disable-next-line no-restricted-syntax
if (this.options.comment !== undefined && maxWireVersion(server) >= 9) {
if (this.options.comment !== undefined && maxWireVersion(connection) >= 9) {
getMoreCmd.comment = this.options.comment;
}

const commandOptions = {
return getMoreCmd;
}

override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
return {
returnFieldSelector: null,
documentsReturnedIn: 'nextBatch',
timeoutContext,
...this.options
};
}

return await server.command(this.ns, getMoreCmd, commandOptions, CursorResponse);
override handleOk(
response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): CursorResponse {
return response;
}
}

Expand Down
21 changes: 13 additions & 8 deletions src/operations/indexes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Document } from '../bson';
import { type Connection } from '../cmap/connection';
import { CursorResponse } from '../cmap/wire_protocol/responses';
import type { Collection } from '../collection';
import { type AbstractCursorOptions } from '../cursor/abstract_cursor';
Expand All @@ -12,6 +13,7 @@ import {
type CollationOptions,
CommandOperation,
type CommandOperationOptions,
ModernizedCommandOperation,
type OperationParent
} from './command';
import { Aspect, defineAspects } from './operation';
Expand Down Expand Up @@ -366,7 +368,8 @@ export type ListIndexesOptions = AbstractCursorOptions & {
};

/** @internal */
export class ListIndexesOperation extends CommandOperation<CursorResponse> {
export class ListIndexesOperation extends ModernizedCommandOperation<CursorResponse> {
override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse;
/**
* @remarks WriteConcern can still be present on the options because
* we inherit options from the client/db/collection. The
Expand All @@ -389,12 +392,8 @@ export class ListIndexesOperation extends CommandOperation<CursorResponse> {
return 'listIndexes' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<CursorResponse> {
const serverWireVersion = maxWireVersion(server);
override buildCommandDocument(connection: Connection): Document {
const serverWireVersion = maxWireVersion(connection);

const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {};

Expand All @@ -406,7 +405,13 @@ export class ListIndexesOperation extends CommandOperation<CursorResponse> {
command.comment = this.options.comment;
}

return await super.executeCommand(server, session, command, timeoutContext, CursorResponse);
return command;
}

override handleOk(
response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
): CursorResponse {
return response;
}
}

Expand Down
45 changes: 22 additions & 23 deletions src/operations/kill_cursors.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Long } from '../bson';
import { MongoRuntimeError } from '../error';
import type { Server } from '../sdam/server';
import { type Connection } from '../cmap/connection';
import { MongoDBResponse } from '../cmap/wire_protocol/responses';
import { type MongoError, MongoRuntimeError } from '../error';
import type { Server, ServerCommandOptions } from '../sdam/server';
import type { ClientSession } from '../sessions';
import { type TimeoutContext } from '../timeout';
import { type MongoDBNamespace, squashError } from '../utils';
import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
import { type MongoDBNamespace } from '../utils';
import { Aspect, defineAspects, ModernizedOperation, type OperationOptions } from './operation';

/**
* https://www.mongodb.com/docs/manual/reference/command/killCursors/
Expand All @@ -16,7 +18,8 @@ interface KillCursorsCommand {
comment?: unknown;
}

export class KillCursorsOperation extends AbstractOperation {
export class KillCursorsOperation extends ModernizedOperation<void> {
override SERVER_COMMAND_RESPONSE_TYPE = MongoDBResponse;
cursorId: Long;

constructor(cursorId: Long, ns: MongoDBNamespace, server: Server, options: OperationOptions) {
Expand All @@ -30,15 +33,7 @@ export class KillCursorsOperation extends AbstractOperation {
return 'killCursors' as const;
}

override async execute(
server: Server,
session: ClientSession | undefined,
timeoutContext: TimeoutContext
): Promise<void> {
if (server !== this.server) {
throw new MongoRuntimeError('Killcursor must run on the same server operation began on');
}

override buildCommand(_connection: Connection, _session?: ClientSession): KillCursorsCommand {
const killCursors = this.ns.collection;
if (killCursors == null) {
// Cursors should have adopted the namespace returned by MongoDB
Expand All @@ -50,15 +45,19 @@ export class KillCursorsOperation extends AbstractOperation {
killCursors,
cursors: [this.cursorId]
};
try {
await server.command(this.ns, killCursorsCommand, {
session,
timeoutContext
});
} catch (error) {
// The driver should never emit errors from killCursors, this is spec-ed behavior
squashError(error);
}

return killCursorsCommand;
}

override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
return {
session: this.session,
timeoutContext
};
}

override handleError(_error: MongoError): void {
// The driver should never emit errors from killCursors, this is spec-ed behavior
}
}

Expand Down
Loading