opusdev/vector-similarity-api
1
1import { Admin } from './admin';2import { type BSONSerializeOptions, type Document, resolveBSONOptions } from './bson';3import { ChangeStream, type ChangeStreamDocument, type ChangeStreamOptions } from './change_stream';4import { Collection, type CollectionOptions } from './collection';5import * as CONSTANTS from './constants';6import { AggregationCursor } from './cursor/aggregation_cursor';7import { ListCollectionsCursor } from './cursor/list_collections_cursor';8import { RunCommandCursor, type RunCursorCommandOptions } from './cursor/run_command_cursor';9import { MongoInvalidArgumentError } from './error';10import type { MongoClient, PkFactory } from './mongo_client';11import type { Abortable, TODO_NODE_3286 } from './mongo_types';12import type { AggregateOptions } from './operations/aggregate';13import { type CreateCollectionOptions, createCollections } from './operations/create_collection';14import {15 type DropCollectionOptions,16 dropCollections,17 DropDatabaseOperation,18 type DropDatabaseOptions19} from './operations/drop';20import { executeOperation } from './operations/execute_operation';21import {22 CreateIndexesOperation,23 type CreateIndexesOptions,24 type IndexDescriptionCompact,25 type IndexDescriptionInfo,26 type IndexInformationOptions,27 type IndexSpecification28} from './operations/indexes';29import type { CollectionInfo, ListCollectionsOptions } from './operations/list_collections';30import { ProfilingLevelOperation, type ProfilingLevelOptions } from './operations/profiling_level';31import { RemoveUserOperation, type RemoveUserOptions } from './operations/remove_user';32import { RenameOperation, type RenameOptions } from './operations/rename';33import { RunCommandOperation, type RunCommandOptions } from './operations/run_command';34import {35 type ProfilingLevel,36 SetProfilingLevelOperation,37 type SetProfilingLevelOptions38} from './operations/set_profiling_level';39import { DbStatsOperation, type DbStatsOptions } from './operations/stats';40import { ReadConcern } from './read_concern';41import { ReadPreference, type ReadPreferenceLike } from './read_preference';42import { DEFAULT_PK_FACTORY, filterOptions, MongoDBNamespace, resolveOptions } from './utils';43import { WriteConcern, type WriteConcernOptions } from './write_concern';44 45// Allowed parameters46const DB_OPTIONS_ALLOW_LIST = [47 'writeConcern',48 'readPreference',49 'readPreferenceTags',50 'native_parser',51 'forceServerObjectId',52 'pkFactory',53 'serializeFunctions',54 'raw',55 'authSource',56 'ignoreUndefined',57 'readConcern',58 'retryMiliSeconds',59 'numberOfRetries',60 'useBigInt64',61 'promoteBuffers',62 'promoteLongs',63 'bsonRegExp',64 'enableUtf8Validation',65 'promoteValues',66 'compression',67 'retryWrites',68 'timeoutMS'69];70 71/** @internal */72export interface DbPrivate {73 options?: DbOptions;74 readPreference?: ReadPreference;75 pkFactory: PkFactory;76 readConcern?: ReadConcern;77 bsonOptions: BSONSerializeOptions;78 writeConcern?: WriteConcern;79 namespace: MongoDBNamespace;80}81 82/** @public */83export interface DbOptions extends BSONSerializeOptions, WriteConcernOptions {84 /** If the database authentication is dependent on another databaseName. */85 authSource?: string;86 /** Force server to assign _id values instead of driver. */87 forceServerObjectId?: boolean;88 /** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */89 readPreference?: ReadPreferenceLike;90 /** A primary key factory object for generation of custom _id keys. */91 pkFactory?: PkFactory;92 /** Specify a read concern for the collection. (only MongoDB 3.2 or higher supported) */93 readConcern?: ReadConcern;94 /** Should retry failed writes */95 retryWrites?: boolean;96 /**97 * @experimental98 * Specifies the time an operation will run until it throws a timeout error99 */100 timeoutMS?: number;101}102 103/**104 * The **Db** class is a class that represents a MongoDB Database.105 * @public106 *107 * @example108 * ```ts109 * import { MongoClient } from 'mongodb';110 *111 * interface Pet {112 * name: string;113 * kind: 'dog' | 'cat' | 'fish';114 * }115 *116 * const client = new MongoClient('mongodb://localhost:27017');117 * const db = client.db();118 *119 * // Create a collection that validates our union120 * await db.createCollection<Pet>('pets', {121 * validator: { $expr: { $in: ['$kind', ['dog', 'cat', 'fish']] } }122 * })123 * ```124 */125export class Db {126 /** @internal */127 s: DbPrivate;128 129 /**130 * Gets the MongoClient associated with the Db.131 * @public132 */133 readonly client: MongoClient;134 135 public static SYSTEM_NAMESPACE_COLLECTION = CONSTANTS.SYSTEM_NAMESPACE_COLLECTION;136 public static SYSTEM_INDEX_COLLECTION = CONSTANTS.SYSTEM_INDEX_COLLECTION;137 public static SYSTEM_PROFILE_COLLECTION = CONSTANTS.SYSTEM_PROFILE_COLLECTION;138 public static SYSTEM_USER_COLLECTION = CONSTANTS.SYSTEM_USER_COLLECTION;139 public static SYSTEM_COMMAND_COLLECTION = CONSTANTS.SYSTEM_COMMAND_COLLECTION;140 public static SYSTEM_JS_COLLECTION = CONSTANTS.SYSTEM_JS_COLLECTION;141 142 /**143 * Creates a new Db instance.144 *145 * Db name cannot contain a dot, the server may apply more restrictions when an operation is run.146 *147 * @param client - The MongoClient for the database.148 * @param databaseName - The name of the database this instance represents.149 * @param options - Optional settings for Db construction.150 */151 constructor(client: MongoClient, databaseName: string, options?: DbOptions) {152 options = options ?? {};153 154 // Filter the options155 options = filterOptions(options, DB_OPTIONS_ALLOW_LIST);156 157 // Ensure there are no dots in database name158 if (typeof databaseName === 'string' && databaseName.includes('.')) {159 throw new MongoInvalidArgumentError(`Database names cannot contain the character '.'`);160 }161 162 // Internal state of the db object163 this.s = {164 // Options165 options,166 // Unpack read preference167 readPreference: ReadPreference.fromOptions(options),168 // Merge bson options169 bsonOptions: resolveBSONOptions(options, client),170 // Set up the primary key factory or fallback to ObjectId171 pkFactory: options?.pkFactory ?? DEFAULT_PK_FACTORY,172 // ReadConcern173 readConcern: ReadConcern.fromOptions(options),174 writeConcern: WriteConcern.fromOptions(options),175 // Namespace176 namespace: new MongoDBNamespace(databaseName)177 };178 179 this.client = client;180 }181 182 get databaseName(): string {183 return this.s.namespace.db;184 }185 186 // Options187 get options(): DbOptions | undefined {188 return this.s.options;189 }190 191 /**192 * Check if a secondary can be used (because the read preference is *not* set to primary)193 */194 get secondaryOk(): boolean {195 return this.s.readPreference?.preference !== 'primary' || false;196 }197 198 get readConcern(): ReadConcern | undefined {199 return this.s.readConcern;200 }201 202 /**203 * The current readPreference of the Db. If not explicitly defined for204 * this Db, will be inherited from the parent MongoClient205 */206 get readPreference(): ReadPreference {207 if (this.s.readPreference == null) {208 return this.client.readPreference;209 }210 211 return this.s.readPreference;212 }213 214 get bsonOptions(): BSONSerializeOptions {215 return this.s.bsonOptions;216 }217 218 // get the write Concern219 get writeConcern(): WriteConcern | undefined {220 return this.s.writeConcern;221 }222 223 get namespace(): string {224 return this.s.namespace.toString();225 }226 227 public get timeoutMS(): number | undefined {228 return this.s.options?.timeoutMS;229 }230 231 /**232 * Create a new collection on a server with the specified options. Use this to create capped collections.233 * More information about command options available at https://www.mongodb.com/docs/manual/reference/command/create/234 *235 * Collection namespace validation is performed server-side.236 *237 * @param name - The name of the collection to create238 * @param options - Optional settings for the command239 */240 async createCollection<TSchema extends Document = Document>(241 name: string,242 options?: CreateCollectionOptions243 ): Promise<Collection<TSchema>> {244 options = resolveOptions(this, options);245 return await createCollections<TSchema>(this, name, options);246 }247 248 /**249 * Execute a command250 *251 * @remarks252 * This command does not inherit options from the MongoClient.253 *254 * The driver will ensure the following fields are attached to the command sent to the server:255 * - `lsid` - sourced from an implicit session or options.session256 * - `$readPreference` - defaults to primary or can be configured by options.readPreference257 * - `$db` - sourced from the name of this database258 *259 * If the client has a serverApi setting:260 * - `apiVersion`261 * - `apiStrict`262 * - `apiDeprecationErrors`263 *264 * When in a transaction:265 * - `readConcern` - sourced from readConcern set on the TransactionOptions266 * - `writeConcern` - sourced from writeConcern set on the TransactionOptions267 *268 * Attaching any of the above fields to the command will have no effect as the driver will overwrite the value.269 *270 * @param command - The command to run271 * @param options - Optional settings for the command272 */273 async command(command: Document, options?: RunCommandOptions & Abortable): Promise<Document> {274 // Intentionally, we do not inherit options from parent for this operation.275 return await executeOperation(276 this.client,277 new RunCommandOperation(278 this.s.namespace,279 command,280 resolveOptions(undefined, {281 ...resolveBSONOptions(options),282 timeoutMS: options?.timeoutMS ?? this.timeoutMS,283 session: options?.session,284 readPreference: options?.readPreference,285 signal: options?.signal286 })287 )288 );289 }290 291 /**292 * Execute an aggregation framework pipeline against the database.293 *294 * @param pipeline - An array of aggregation stages to be executed295 * @param options - Optional settings for the command296 */297 aggregate<T extends Document = Document>(298 pipeline: Document[] = [],299 options?: AggregateOptions300 ): AggregationCursor<T> {301 return new AggregationCursor(302 this.client,303 this.s.namespace,304 pipeline,305 resolveOptions(this, options)306 );307 }308 309 /** Return the Admin db instance */310 admin(): Admin {311 return new Admin(this);312 }313 314 /**315 * Returns a reference to a MongoDB Collection. If it does not exist it will be created implicitly.316 *317 * Collection namespace validation is performed server-side.318 *319 * @param name - the collection name we wish to access.320 * @returns return the new Collection instance321 */322 collection<TSchema extends Document = Document>(323 name: string,324 options: CollectionOptions = {}325 ): Collection<TSchema> {326 if (typeof options === 'function') {327 throw new MongoInvalidArgumentError('The callback form of this helper has been removed.');328 }329 return new Collection<TSchema>(this, name, resolveOptions(this, options));330 }331 332 /**333 * Get all the db statistics.334 *335 * @param options - Optional settings for the command336 */337 async stats(options?: DbStatsOptions): Promise<Document> {338 return await executeOperation(339 this.client,340 new DbStatsOperation(this, resolveOptions(this, options))341 );342 }343 344 /**345 * List all collections of this database with optional filter346 *347 * @param filter - Query to filter collections by348 * @param options - Optional settings for the command349 */350 listCollections(351 filter: Document,352 options: Exclude<ListCollectionsOptions, 'nameOnly'> & { nameOnly: true } & Abortable353 ): ListCollectionsCursor<Pick<CollectionInfo, 'name' | 'type'>>;354 listCollections(355 filter: Document,356 options: Exclude<ListCollectionsOptions, 'nameOnly'> & { nameOnly: false } & Abortable357 ): ListCollectionsCursor<CollectionInfo>;358 listCollections<359 T extends Pick<CollectionInfo, 'name' | 'type'> | CollectionInfo =360 | Pick<CollectionInfo, 'name' | 'type'>361 | CollectionInfo362 >(filter?: Document, options?: ListCollectionsOptions & Abortable): ListCollectionsCursor<T>;363 listCollections<364 T extends Pick<CollectionInfo, 'name' | 'type'> | CollectionInfo =365 | Pick<CollectionInfo, 'name' | 'type'>366 | CollectionInfo367 >(368 filter: Document = {},369 options: ListCollectionsOptions & Abortable = {}370 ): ListCollectionsCursor<T> {371 return new ListCollectionsCursor<T>(this, filter, resolveOptions(this, options));372 }373 374 /**375 * Rename a collection.376 *377 * @remarks378 * This operation does not inherit options from the MongoClient.379 *380 * @param fromCollection - Name of current collection to rename381 * @param toCollection - New name of of the collection382 * @param options - Optional settings for the command383 */384 async renameCollection<TSchema extends Document = Document>(385 fromCollection: string,386 toCollection: string,387 options?: RenameOptions388 ): Promise<Collection<TSchema>> {389 // Intentionally, we do not inherit options from parent for this operation.390 return await executeOperation(391 this.client,392 new RenameOperation(393 this.collection<TSchema>(fromCollection) as TODO_NODE_3286,394 toCollection,395 resolveOptions(undefined, {396 ...options,397 new_collection: true,398 readPreference: ReadPreference.primary399 })400 ) as TODO_NODE_3286401 );402 }403 404 /**405 * Drop a collection from the database, removing it permanently. New accesses will create a new collection.406 *407 * @param name - Name of collection to drop408 * @param options - Optional settings for the command409 */410 async dropCollection(name: string, options?: DropCollectionOptions): Promise<boolean> {411 options = resolveOptions(this, options);412 return await dropCollections(this, name, options);413 }414 415 /**416 * Drop a database, removing it permanently from the server.417 *418 * @param options - Optional settings for the command419 */420 async dropDatabase(options?: DropDatabaseOptions): Promise<boolean> {421 return await executeOperation(422 this.client,423 new DropDatabaseOperation(this, resolveOptions(this, options))424 );425 }426 427 /**428 * Fetch all collections for the current db.429 *430 * @param options - Optional settings for the command431 */432 async collections(options?: ListCollectionsOptions): Promise<Collection[]> {433 options = resolveOptions(this, options);434 const collections = await this.listCollections({}, { ...options, nameOnly: true }).toArray();435 436 return collections437 .filter(438 // Filter collections removing any illegal ones439 ({ name }) => !name.includes('$')440 )441 .map(({ name }) => new Collection(this, name, this.s.options));442 }443 444 /**445 * Creates an index on the db and collection.446 *447 * @param name - Name of the collection to create the index on.448 * @param indexSpec - Specify the field to index, or an index specification449 * @param options - Optional settings for the command450 */451 async createIndex(452 name: string,453 indexSpec: IndexSpecification,454 options?: CreateIndexesOptions455 ): Promise<string> {456 const indexes = await executeOperation(457 this.client,458 CreateIndexesOperation.fromIndexSpecification(this, name, indexSpec, options)459 );460 return indexes[0];461 }462 463 /**464 * Remove a user from a database465 *466 * @param username - The username to remove467 * @param options - Optional settings for the command468 */469 async removeUser(username: string, options?: RemoveUserOptions): Promise<boolean> {470 return await executeOperation(471 this.client,472 new RemoveUserOperation(this, username, resolveOptions(this, options))473 );474 }475 476 /**477 * Set the current profiling level of MongoDB478 *479 * @param level - The new profiling level (off, slow_only, all).480 * @param options - Optional settings for the command481 */482 async setProfilingLevel(483 level: ProfilingLevel,484 options?: SetProfilingLevelOptions485 ): Promise<ProfilingLevel> {486 return await executeOperation(487 this.client,488 new SetProfilingLevelOperation(this, level, resolveOptions(this, options))489 );490 }491 492 /**493 * Retrieve the current profiling Level for MongoDB494 *495 * @param options - Optional settings for the command496 */497 async profilingLevel(options?: ProfilingLevelOptions): Promise<string> {498 return await executeOperation(499 this.client,500 new ProfilingLevelOperation(this, resolveOptions(this, options))501 );502 }503 504 /**505 * Retrieves this collections index info.506 *507 * @param name - The name of the collection.508 * @param options - Optional settings for the command509 */510 indexInformation(511 name: string,512 options: IndexInformationOptions & { full: true }513 ): Promise<IndexDescriptionInfo[]>;514 indexInformation(515 name: string,516 options: IndexInformationOptions & { full?: false }517 ): Promise<IndexDescriptionCompact>;518 indexInformation(519 name: string,520 options: IndexInformationOptions521 ): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]>;522 indexInformation(name: string): Promise<IndexDescriptionCompact>;523 async indexInformation(524 name: string,525 options?: IndexInformationOptions526 ): Promise<IndexDescriptionCompact | IndexDescriptionInfo[]> {527 return await this.collection(name).indexInformation(resolveOptions(this, options));528 }529 530 /**531 * Create a new Change Stream, watching for new changes (insertions, updates,532 * replacements, deletions, and invalidations) in this database. Will ignore all533 * changes to system collections.534 *535 * @remarks536 * watch() accepts two generic arguments for distinct use cases:537 * - The first is to provide the schema that may be defined for all the collections within this database538 * - The second is to override the shape of the change stream document entirely, if it is not provided the type will default to ChangeStreamDocument of the first argument539 *540 * @remarks541 * When `timeoutMS` is configured for a change stream, it will have different behaviour depending542 * on whether the change stream is in iterator mode or emitter mode. In both cases, a change543 * stream will time out if it does not receive a change event within `timeoutMS` of the last change544 * event.545 *546 * Note that if a change stream is consistently timing out when watching a collection, database or547 * client that is being changed, then this may be due to the server timing out before it can finish548 * processing the existing oplog. To address this, restart the change stream with a higher549 * `timeoutMS`.550 *551 * If the change stream times out the initial aggregate operation to establish the change stream on552 * the server, then the client will close the change stream. If the getMore calls to the server553 * time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError554 * when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in555 * emitter mode.556 *557 * To determine whether or not the change stream is still open following a timeout, check the558 * {@link ChangeStream.closed} getter.559 *560 * @example561 * In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.562 * The next call can just be retried after this succeeds.563 * ```ts564 * const changeStream = collection.watch([], { timeoutMS: 100 });565 * try {566 * await changeStream.next();567 * } catch (e) {568 * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {569 * await changeStream.next();570 * }571 * throw e;572 * }573 * ```574 *575 * @example576 * In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will577 * emit an error event that returns a MongoOperationTimeoutError, but will not close the change578 * stream unless the resume attempt fails. There is no need to re-establish change listeners as579 * this will automatically continue emitting change events once the resume attempt completes.580 *581 * ```ts582 * const changeStream = collection.watch([], { timeoutMS: 100 });583 * changeStream.on('change', console.log);584 * changeStream.on('error', e => {585 * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {586 * // do nothing587 * } else {588 * changeStream.close();589 * }590 * });591 * ```592 * @param pipeline - An array of {@link https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents. This allows for filtering (using $match) and manipulating the change stream documents.593 * @param options - Optional settings for the command594 * @typeParam TSchema - Type of the data being detected by the change stream595 * @typeParam TChange - Type of the whole change stream document emitted596 */597 watch<598 TSchema extends Document = Document,599 TChange extends Document = ChangeStreamDocument<TSchema>600 >(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStream<TSchema, TChange> {601 // Allow optionally not specifying a pipeline602 if (!Array.isArray(pipeline)) {603 options = pipeline;604 pipeline = [];605 }606 607 return new ChangeStream<TSchema, TChange>(this, pipeline, resolveOptions(this, options));608 }609 610 /**611 * A low level cursor API providing basic driver functionality:612 * - ClientSession management613 * - ReadPreference for server selection614 * - Running getMores automatically when a local batch is exhausted615 *616 * @param command - The command that will start a cursor on the server.617 * @param options - Configurations for running the command, bson options will apply to getMores618 */619 runCursorCommand(command: Document, options?: RunCursorCommandOptions): RunCommandCursor {620 return new RunCommandCursor(this, command, options);621 }622}623 