|
| 1 | +import { type Db } from '..'; |
| 2 | +import { type Document, pluckBSONSerializeOptions } from '../bson'; |
| 3 | +import { type OnDemandDocumentDeserializeOptions } from '../cmap/wire_protocol/on_demand/document'; |
| 4 | +import type { Server } from '../sdam/server'; |
| 5 | +import type { ClientSession } from '../sessions'; |
| 6 | +import { type TimeoutContext } from '../timeout'; |
| 7 | +import { MongoDBNamespace } from '../utils'; |
| 8 | +import { CommandOperation } from './command'; |
| 9 | +import { type FindOptions, makeFindCommand } from './find'; |
| 10 | +import { Aspect, defineAspects } from './operation'; |
| 11 | + |
| 12 | +/** @public */ |
| 13 | +export interface FindOneOptions extends FindOptions { |
| 14 | + /** @deprecated Will be removed in the next major version. User provided value will be ignored. */ |
| 15 | + batchSize?: number; |
| 16 | + /** @deprecated Will be removed in the next major version. User provided value will be ignored. */ |
| 17 | + limit?: number; |
| 18 | + /** @deprecated Will be removed in the next major version. User provided value will be ignored. */ |
| 19 | + noCursorTimeout?: boolean; |
| 20 | +} |
| 21 | + |
| 22 | +/** @internal */ |
| 23 | +export class FindOneOperation<TSchema = any> extends CommandOperation<TSchema> { |
| 24 | + override options: FindOneOptions; |
| 25 | + /** @internal */ |
| 26 | + private namespace: MongoDBNamespace; |
| 27 | + /** @internal */ |
| 28 | + private filter: Document; |
| 29 | + /** @internal */ |
| 30 | + protected deserializationOptions: OnDemandDocumentDeserializeOptions; |
| 31 | + |
| 32 | + constructor(db: Db, collectionName: string, filter: Document, options: FindOneOptions = {}) { |
| 33 | + super(db, options); |
| 34 | + this.namespace = new MongoDBNamespace(db.databaseName, collectionName); |
| 35 | + this.filter = filter; |
| 36 | + this.options = { ...options }; |
| 37 | + this.deserializationOptions = { |
| 38 | + ...pluckBSONSerializeOptions(options), |
| 39 | + validation: { |
| 40 | + utf8: options?.enableUtf8Validation === false ? false : true |
| 41 | + } |
| 42 | + }; |
| 43 | + } |
| 44 | + |
| 45 | + override get commandName() { |
| 46 | + return 'find' as const; |
| 47 | + } |
| 48 | + |
| 49 | + override async execute( |
| 50 | + server: Server, |
| 51 | + session: ClientSession | undefined, |
| 52 | + timeoutContext: TimeoutContext |
| 53 | + ): Promise<TSchema> { |
| 54 | + const command: Document = makeFindCommand(this.namespace, this.filter, this.options); |
| 55 | + // Explicitly set the limit to 1 and singleBatch to true for all commands, per the spec. |
| 56 | + // noCursorTimeout must be unset as well as batchSize. |
| 57 | + // See: https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#findone-api-details |
| 58 | + command.limit = 1; |
| 59 | + command.singleBatch = true; |
| 60 | + if (command.noCursorTimeout != null) { |
| 61 | + delete command.noCursorTimeout; |
| 62 | + } |
| 63 | + if (command.batchSize != null) { |
| 64 | + delete command.batchSize; |
| 65 | + } |
| 66 | + |
| 67 | + const response = await super.executeCommand(server, session, command, timeoutContext); |
| 68 | + // In this case since we are just running a command, the response is a document with |
| 69 | + // a single batch cursor, not an OnDemandDocument. |
| 70 | + const document = response.cursor?.firstBatch?.[0] ?? null; |
| 71 | + return document; |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +defineAspects(FindOneOperation, [Aspect.READ_OPERATION, Aspect.RETRYABLE]); |
0 commit comments