Skip to content

Commit 95289a6

Browse files
findoperation
1 parent aab0bd1 commit 95289a6

File tree

3 files changed

+127
-168
lines changed

3 files changed

+127
-168
lines changed

src/operations/find.ts

Lines changed: 114 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@ import type { Document } from '../bson';
22
import { CursorResponse, ExplainedCursorResponse } from '../cmap/wire_protocol/responses';
33
import { type AbstractCursorOptions, type CursorTimeoutMode } from '../cursor/abstract_cursor';
44
import { MongoInvalidArgumentError } from '../error';
5-
import {
6-
decorateWithExplain,
7-
type ExplainOptions,
8-
validateExplainTimeoutOptions
9-
} from '../explain';
10-
import { ReadConcern } from '../read_concern';
11-
import type { Server } from '../sdam/server';
12-
import type { ClientSession } from '../sessions';
5+
import { type ExplainOptions } from '../explain';
6+
import type { ServerCommandOptions } from '../sdam/server';
137
import { formatSort, type Sort } from '../sort';
148
import { type TimeoutContext } from '../timeout';
159
import { type MongoDBNamespace, normalizeHintField } from '../utils';
16-
import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
10+
import {
11+
type CollationOptions,
12+
type CommandOperationOptions,
13+
ModernizedCommandOperation
14+
} from './command';
1715
import { Aspect, defineAspects, type Hint } from './operation';
1816

1917
/**
@@ -92,7 +90,9 @@ export interface FindOneOptions extends FindOptions {
9290
}
9391

9492
/** @internal */
95-
export class FindOperation extends CommandOperation<CursorResponse> {
93+
export class FindOperation extends ModernizedCommandOperation<CursorResponse> {
94+
override SERVER_COMMAND_RESPONSE_TYPE = CursorResponse;
95+
9696
/**
9797
* @remarks WriteConcern can still be present on the options because
9898
* we inherit options from the client/db/collection. The
@@ -116,167 +116,145 @@ export class FindOperation extends CommandOperation<CursorResponse> {
116116

117117
// special case passing in an ObjectId as a filter
118118
this.filter = filter != null && filter._bsontype === 'ObjectId' ? { _id: filter } : filter;
119+
120+
this.SERVER_COMMAND_RESPONSE_TYPE = this.explain ? ExplainedCursorResponse : CursorResponse;
119121
}
120122

121123
override get commandName() {
122124
return 'find' as const;
123125
}
124126

125-
override async execute(
126-
server: Server,
127-
session: ClientSession | undefined,
128-
timeoutContext: TimeoutContext
129-
): Promise<CursorResponse> {
130-
this.server = server;
131-
132-
const options = this.options;
133-
134-
let findCommand = makeFindCommand(this.ns, this.filter, options);
135-
if (this.explain) {
136-
validateExplainTimeoutOptions(this.options, this.explain);
137-
findCommand = decorateWithExplain(findCommand, this.explain);
138-
}
139-
140-
return await server.command(
141-
this.ns,
142-
findCommand,
143-
{
144-
...this.options,
145-
...this.bsonOptions,
146-
documentsReturnedIn: 'firstBatch',
147-
session,
148-
timeoutContext
149-
},
150-
this.explain ? ExplainedCursorResponse : CursorResponse
151-
);
127+
override buildOptions(timeoutContext: TimeoutContext): ServerCommandOptions {
128+
return {
129+
...this.options,
130+
...this.bsonOptions,
131+
documentsReturnedIn: 'firstBatch',
132+
session: this.session,
133+
timeoutContext
134+
};
152135
}
153-
}
154-
155-
function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOptions): Document {
156-
const findCommand: Document = {
157-
find: ns.collection,
158-
filter
159-
};
160136

161-
if (options.sort) {
162-
findCommand.sort = formatSort(options.sort);
137+
override handleOk(
138+
response: InstanceType<typeof this.SERVER_COMMAND_RESPONSE_TYPE>
139+
): CursorResponse {
140+
return response;
163141
}
164142

165-
if (options.projection) {
166-
let projection = options.projection;
167-
if (projection && Array.isArray(projection)) {
168-
projection = projection.length
169-
? projection.reduce((result, field) => {
170-
result[field] = 1;
171-
return result;
172-
}, {})
173-
: { _id: 1 };
143+
override buildCommandDocument(): Document {
144+
const options = this.options;
145+
const filter = this.filter;
146+
const ns = this.ns;
147+
const findCommand: Document = {
148+
find: ns.collection,
149+
filter
150+
};
151+
152+
if (options.sort) {
153+
findCommand.sort = formatSort(options.sort);
174154
}
175155

176-
findCommand.projection = projection;
177-
}
156+
if (options.projection) {
157+
let projection = options.projection;
158+
if (projection && Array.isArray(projection)) {
159+
projection = projection.length
160+
? projection.reduce((result, field) => {
161+
result[field] = 1;
162+
return result;
163+
}, {})
164+
: { _id: 1 };
165+
}
178166

179-
if (options.hint) {
180-
findCommand.hint = normalizeHintField(options.hint);
181-
}
167+
findCommand.projection = projection;
168+
}
182169

183-
if (typeof options.skip === 'number') {
184-
findCommand.skip = options.skip;
185-
}
170+
if (options.hint) {
171+
findCommand.hint = normalizeHintField(options.hint);
172+
}
186173

187-
if (typeof options.limit === 'number') {
188-
if (options.limit < 0) {
189-
findCommand.limit = -options.limit;
190-
findCommand.singleBatch = true;
191-
} else {
192-
findCommand.limit = options.limit;
174+
if (typeof options.skip === 'number') {
175+
findCommand.skip = options.skip;
193176
}
194-
}
195177

196-
if (typeof options.batchSize === 'number') {
197-
if (options.batchSize < 0) {
198-
findCommand.limit = -options.batchSize;
199-
} else {
200-
if (options.batchSize === options.limit) {
201-
// Spec dictates that if these are equal the batchSize should be one more than the
202-
// limit to avoid leaving the cursor open.
203-
findCommand.batchSize = options.batchSize + 1;
178+
if (typeof options.limit === 'number') {
179+
if (options.limit < 0) {
180+
findCommand.limit = -options.limit;
181+
findCommand.singleBatch = true;
204182
} else {
205-
findCommand.batchSize = options.batchSize;
183+
findCommand.limit = options.limit;
206184
}
207185
}
208-
}
209186

210-
if (typeof options.singleBatch === 'boolean') {
211-
findCommand.singleBatch = options.singleBatch;
212-
}
213-
214-
// we check for undefined specifically here to allow falsy values
215-
// eslint-disable-next-line no-restricted-syntax
216-
if (options.comment !== undefined) {
217-
findCommand.comment = options.comment;
218-
}
219-
220-
if (typeof options.maxTimeMS === 'number') {
221-
findCommand.maxTimeMS = options.maxTimeMS;
222-
}
187+
if (typeof options.batchSize === 'number') {
188+
if (options.batchSize < 0) {
189+
findCommand.limit = -options.batchSize;
190+
} else {
191+
if (options.batchSize === options.limit) {
192+
// Spec dictates that if these are equal the batchSize should be one more than the
193+
// limit to avoid leaving the cursor open.
194+
findCommand.batchSize = options.batchSize + 1;
195+
} else {
196+
findCommand.batchSize = options.batchSize;
197+
}
198+
}
199+
}
223200

224-
const readConcern = ReadConcern.fromOptions(options);
225-
if (readConcern) {
226-
findCommand.readConcern = readConcern.toJSON();
227-
}
201+
if (typeof options.singleBatch === 'boolean') {
202+
findCommand.singleBatch = options.singleBatch;
203+
}
228204

229-
if (options.max) {
230-
findCommand.max = options.max;
231-
}
205+
// we check for undefined specifically here to allow falsy values
206+
// eslint-disable-next-line no-restricted-syntax
207+
if (options.comment !== undefined) {
208+
findCommand.comment = options.comment;
209+
}
232210

233-
if (options.min) {
234-
findCommand.min = options.min;
235-
}
211+
if (options.max) {
212+
findCommand.max = options.max;
213+
}
236214

237-
if (typeof options.returnKey === 'boolean') {
238-
findCommand.returnKey = options.returnKey;
239-
}
215+
if (options.min) {
216+
findCommand.min = options.min;
217+
}
240218

241-
if (typeof options.showRecordId === 'boolean') {
242-
findCommand.showRecordId = options.showRecordId;
243-
}
219+
if (typeof options.returnKey === 'boolean') {
220+
findCommand.returnKey = options.returnKey;
221+
}
244222

245-
if (typeof options.tailable === 'boolean') {
246-
findCommand.tailable = options.tailable;
247-
}
223+
if (typeof options.showRecordId === 'boolean') {
224+
findCommand.showRecordId = options.showRecordId;
225+
}
248226

249-
if (typeof options.oplogReplay === 'boolean') {
250-
findCommand.oplogReplay = options.oplogReplay;
251-
}
227+
if (typeof options.tailable === 'boolean') {
228+
findCommand.tailable = options.tailable;
229+
}
252230

253-
if (typeof options.timeout === 'boolean') {
254-
findCommand.noCursorTimeout = !options.timeout;
255-
} else if (typeof options.noCursorTimeout === 'boolean') {
256-
findCommand.noCursorTimeout = options.noCursorTimeout;
257-
}
231+
if (typeof options.oplogReplay === 'boolean') {
232+
findCommand.oplogReplay = options.oplogReplay;
233+
}
258234

259-
if (typeof options.awaitData === 'boolean') {
260-
findCommand.awaitData = options.awaitData;
261-
}
235+
if (typeof options.timeout === 'boolean') {
236+
findCommand.noCursorTimeout = !options.timeout;
237+
} else if (typeof options.noCursorTimeout === 'boolean') {
238+
findCommand.noCursorTimeout = options.noCursorTimeout;
239+
}
262240

263-
if (typeof options.allowPartialResults === 'boolean') {
264-
findCommand.allowPartialResults = options.allowPartialResults;
265-
}
241+
if (typeof options.awaitData === 'boolean') {
242+
findCommand.awaitData = options.awaitData;
243+
}
266244

267-
if (options.collation) {
268-
findCommand.collation = options.collation;
269-
}
245+
if (typeof options.allowPartialResults === 'boolean') {
246+
findCommand.allowPartialResults = options.allowPartialResults;
247+
}
248+
if (typeof options.allowDiskUse === 'boolean') {
249+
findCommand.allowDiskUse = options.allowDiskUse;
250+
}
270251

271-
if (typeof options.allowDiskUse === 'boolean') {
272-
findCommand.allowDiskUse = options.allowDiskUse;
273-
}
252+
if (options.let) {
253+
findCommand.let = options.let;
254+
}
274255

275-
if (options.let) {
276-
findCommand.let = options.let;
256+
return findCommand;
277257
}
278-
279-
return findCommand;
280258
}
281259

282260
defineAspects(FindOperation, [

test/integration/server-selection/operation_count.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('Server Operation Count Tests', function () {
7070
it('is zero after a successful command', loadBalancedTestMetadata, async function () {
7171
const server = Array.from(client.topology.s.servers.values())[0];
7272
expect(server.s.operationCount).to.equal(0);
73-
const commandSpy = sinon.spy(server, 'command');
73+
const commandSpy = sinon.spy(server, 'modernCommand');
7474

7575
await collection.findOne({ count: 1 });
7676

@@ -84,7 +84,7 @@ describe('Server Operation Count Tests', function () {
8484
const server = Array.from(client.topology.s.servers.values())[0];
8585
expect(server.s.operationCount).to.equal(0);
8686

87-
const commandSpy = sinon.spy(server, 'command');
87+
const commandSpy = sinon.spy(server, 'modernCommand');
8888

8989
const error = await collection.findOne({ count: 1 }).catch(e => e);
9090

@@ -104,7 +104,7 @@ describe('Server Operation Count Tests', function () {
104104
sinon
105105
.stub(ConnectionPool.prototype, 'checkOut')
106106
.rejects(new Error('unable to checkout connection'));
107-
const commandSpy = sinon.spy(server, 'command');
107+
const commandSpy = sinon.spy(server, 'modernCommand');
108108

109109
const error = await collection.findOne({ count: 1 }).catch(e => e);
110110

test/unit/operations/find.test.ts

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { expect } from 'chai';
22
import * as sinon from 'sinon';
33

4-
import { FindOperation, ns, Server, ServerDescription } from '../../mongodb';
5-
import { topologyWithPlaceholderClient } from '../../tools/utils';
4+
import { FindOperation, ns } from '../../mongodb';
65

76
describe('FindOperation', function () {
87
const namespace = ns('db.coll');
@@ -33,34 +32,16 @@ describe('FindOperation', function () {
3332
});
3433
});
3534

36-
describe('#execute', function () {
37-
context('command construction', () => {
38-
const namespace = ns('db.collection');
39-
const topology = topologyWithPlaceholderClient([], {} as any);
40-
const server = new Server(topology, new ServerDescription('a:1'), {} as any);
35+
context('command construction', () => {
36+
const namespace = ns('db.collection');
4137

42-
it('should build basic find command with filter', async () => {
43-
const findOperation = new FindOperation(namespace, filter);
44-
const stub = sinon.stub(server, 'command').resolves({});
45-
await findOperation.execute(server, undefined);
46-
expect(stub).to.have.been.calledOnceWith(namespace, {
47-
find: namespace.collection,
48-
filter
49-
});
50-
});
51-
52-
it('should build find command with oplogReplay', async () => {
53-
const options = {
54-
oplogReplay: true
55-
};
56-
const findOperation = new FindOperation(namespace, {}, options);
57-
const stub = sinon.stub(server, 'command').resolves({});
58-
await findOperation.execute(server, undefined);
59-
expect(stub).to.have.been.calledOnceWith(
60-
namespace,
61-
sinon.match.has('oplogReplay', options.oplogReplay)
62-
);
63-
});
38+
it('should build find command with oplogReplay', () => {
39+
const options = {
40+
oplogReplay: true
41+
};
42+
const findOperation = new FindOperation(namespace, {}, options);
43+
const command = findOperation.buildCommandDocument();
44+
expect(command.oplogReplay).to.be.true;
6445
});
6546
});
6647
});

0 commit comments

Comments
 (0)