123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- import { Admin } from './admin';
- import { type BSONSerializeOptions, type Document, resolveBSONOptions } from './bson';
- import { ChangeStream, type ChangeStreamDocument, type ChangeStreamOptions } from './change_stream';
- import { Collection, type CollectionOptions } from './collection';
- import * as CONSTANTS from './constants';
- import { AggregationCursor } from './cursor/aggregation_cursor';
- import { ListCollectionsCursor } from './cursor/list_collections_cursor';
- import { RunCommandCursor, type RunCursorCommandOptions } from './cursor/run_command_cursor';
- import { MongoAPIError, MongoInvalidArgumentError } from './error';
- import type { MongoClient, PkFactory } from './mongo_client';
- import type { TODO_NODE_3286 } from './mongo_types';
- import { AddUserOperation, type AddUserOptions } from './operations/add_user';
- import type { AggregateOptions } from './operations/aggregate';
- import { CollectionsOperation } from './operations/collections';
- import type { IndexInformationOptions } from './operations/common_functions';
- import {
- CreateCollectionOperation,
- type CreateCollectionOptions
- } from './operations/create_collection';
- import {
- DropCollectionOperation,
- type DropCollectionOptions,
- DropDatabaseOperation,
- type DropDatabaseOptions
- } from './operations/drop';
- import { executeOperation } from './operations/execute_operation';
- import {
- type CreateIndexesOptions,
- CreateIndexOperation,
- IndexInformationOperation,
- type IndexSpecification
- } from './operations/indexes';
- import type { CollectionInfo, ListCollectionsOptions } from './operations/list_collections';
- import { ProfilingLevelOperation, type ProfilingLevelOptions } from './operations/profiling_level';
- import { RemoveUserOperation, type RemoveUserOptions } from './operations/remove_user';
- import { RenameOperation, type RenameOptions } from './operations/rename';
- import { RunCommandOperation, type RunCommandOptions } from './operations/run_command';
- import {
- type ProfilingLevel,
- SetProfilingLevelOperation,
- type SetProfilingLevelOptions
- } from './operations/set_profiling_level';
- import { DbStatsOperation, type DbStatsOptions } from './operations/stats';
- import { ReadConcern } from './read_concern';
- import { ReadPreference, type ReadPreferenceLike } from './read_preference';
- import { DEFAULT_PK_FACTORY, filterOptions, MongoDBNamespace, resolveOptions } from './utils';
- import { WriteConcern, type WriteConcernOptions } from './write_concern';
- // Allowed parameters
- const DB_OPTIONS_ALLOW_LIST = [
- 'writeConcern',
- 'readPreference',
- 'readPreferenceTags',
- 'native_parser',
- 'forceServerObjectId',
- 'pkFactory',
- 'serializeFunctions',
- 'raw',
- 'authSource',
- 'ignoreUndefined',
- 'readConcern',
- 'retryMiliSeconds',
- 'numberOfRetries',
- 'useBigInt64',
- 'promoteBuffers',
- 'promoteLongs',
- 'bsonRegExp',
- 'enableUtf8Validation',
- 'promoteValues',
- 'compression',
- 'retryWrites'
- ];
- /** @internal */
- export interface DbPrivate {
- options?: DbOptions;
- readPreference?: ReadPreference;
- pkFactory: PkFactory;
- readConcern?: ReadConcern;
- bsonOptions: BSONSerializeOptions;
- writeConcern?: WriteConcern;
- namespace: MongoDBNamespace;
- }
- /** @public */
- export interface DbOptions extends BSONSerializeOptions, WriteConcernOptions {
- /** If the database authentication is dependent on another databaseName. */
- authSource?: string;
- /** Force server to assign _id values instead of driver. */
- forceServerObjectId?: boolean;
- /** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */
- readPreference?: ReadPreferenceLike;
- /** A primary key factory object for generation of custom _id keys. */
- pkFactory?: PkFactory;
- /** Specify a read concern for the collection. (only MongoDB 3.2 or higher supported) */
- readConcern?: ReadConcern;
- /** Should retry failed writes */
- retryWrites?: boolean;
- }
- /**
- * The **Db** class is a class that represents a MongoDB Database.
- * @public
- *
- * @example
- * ```ts
- * import { MongoClient } from 'mongodb';
- *
- * interface Pet {
- * name: string;
- * kind: 'dog' | 'cat' | 'fish';
- * }
- *
- * const client = new MongoClient('mongodb://localhost:27017');
- * const db = client.db();
- *
- * // Create a collection that validates our union
- * await db.createCollection<Pet>('pets', {
- * validator: { $expr: { $in: ['$kind', ['dog', 'cat', 'fish']] } }
- * })
- * ```
- */
- export class Db {
- /** @internal */
- s: DbPrivate;
- /** @internal */
- readonly client: MongoClient;
- public static SYSTEM_NAMESPACE_COLLECTION = CONSTANTS.SYSTEM_NAMESPACE_COLLECTION;
- public static SYSTEM_INDEX_COLLECTION = CONSTANTS.SYSTEM_INDEX_COLLECTION;
- public static SYSTEM_PROFILE_COLLECTION = CONSTANTS.SYSTEM_PROFILE_COLLECTION;
- public static SYSTEM_USER_COLLECTION = CONSTANTS.SYSTEM_USER_COLLECTION;
- public static SYSTEM_COMMAND_COLLECTION = CONSTANTS.SYSTEM_COMMAND_COLLECTION;
- public static SYSTEM_JS_COLLECTION = CONSTANTS.SYSTEM_JS_COLLECTION;
- /**
- * Creates a new Db instance
- *
- * @param client - The MongoClient for the database.
- * @param databaseName - The name of the database this instance represents.
- * @param options - Optional settings for Db construction
- */
- constructor(client: MongoClient, databaseName: string, options?: DbOptions) {
- options = options ?? {};
- // Filter the options
- options = filterOptions(options, DB_OPTIONS_ALLOW_LIST);
- // Ensure we have a valid db name
- validateDatabaseName(databaseName);
- // Internal state of the db object
- this.s = {
- // Options
- options,
- // Unpack read preference
- readPreference: ReadPreference.fromOptions(options),
- // Merge bson options
- bsonOptions: resolveBSONOptions(options, client),
- // Set up the primary key factory or fallback to ObjectId
- pkFactory: options?.pkFactory ?? DEFAULT_PK_FACTORY,
- // ReadConcern
- readConcern: ReadConcern.fromOptions(options),
- writeConcern: WriteConcern.fromOptions(options),
- // Namespace
- namespace: new MongoDBNamespace(databaseName)
- };
- this.client = client;
- }
- get databaseName(): string {
- return this.s.namespace.db;
- }
- // Options
- get options(): DbOptions | undefined {
- return this.s.options;
- }
- /**
- * Check if a secondary can be used (because the read preference is *not* set to primary)
- */
- get secondaryOk(): boolean {
- return this.s.readPreference?.preference !== 'primary' || false;
- }
- get readConcern(): ReadConcern | undefined {
- return this.s.readConcern;
- }
- /**
- * The current readPreference of the Db. If not explicitly defined for
- * this Db, will be inherited from the parent MongoClient
- */
- get readPreference(): ReadPreference {
- if (this.s.readPreference == null) {
- return this.client.readPreference;
- }
- return this.s.readPreference;
- }
- get bsonOptions(): BSONSerializeOptions {
- return this.s.bsonOptions;
- }
- // get the write Concern
- get writeConcern(): WriteConcern | undefined {
- return this.s.writeConcern;
- }
- get namespace(): string {
- return this.s.namespace.toString();
- }
- /**
- * Create a new collection on a server with the specified options. Use this to create capped collections.
- * More information about command options available at https://www.mongodb.com/docs/manual/reference/command/create/
- *
- * @param name - The name of the collection to create
- * @param options - Optional settings for the command
- */
- async createCollection<TSchema extends Document = Document>(
- name: string,
- options?: CreateCollectionOptions
- ): Promise<Collection<TSchema>> {
- return executeOperation(
- this.client,
- new CreateCollectionOperation(this, name, resolveOptions(this, options)) as TODO_NODE_3286
- );
- }
- /**
- * Execute a command
- *
- * @remarks
- * This command does not inherit options from the MongoClient.
- *
- * The driver will ensure the following fields are attached to the command sent to the server:
- * - `lsid` - sourced from an implicit session or options.session
- * - `$readPreference` - defaults to primary or can be configured by options.readPreference
- * - `$db` - sourced from the name of this database
- *
- * If the client has a serverApi setting:
- * - `apiVersion`
- * - `apiStrict`
- * - `apiDeprecationErrors`
- *
- * When in a transaction:
- * - `readConcern` - sourced from readConcern set on the TransactionOptions
- * - `writeConcern` - sourced from writeConcern set on the TransactionOptions
- *
- * Attaching any of the above fields to the command will have no effect as the driver will overwrite the value.
- *
- * @param command - The command to run
- * @param options - Optional settings for the command
- */
- async command(command: Document, options?: RunCommandOptions): Promise<Document> {
- // Intentionally, we do not inherit options from parent for this operation.
- return executeOperation(this.client, new RunCommandOperation(this, command, options));
- }
- /**
- * Execute an aggregation framework pipeline against the database, needs MongoDB \>= 3.6
- *
- * @param pipeline - An array of aggregation stages to be executed
- * @param options - Optional settings for the command
- */
- aggregate<T extends Document = Document>(
- pipeline: Document[] = [],
- options?: AggregateOptions
- ): AggregationCursor<T> {
- return new AggregationCursor(
- this.client,
- this.s.namespace,
- pipeline,
- resolveOptions(this, options)
- );
- }
- /** Return the Admin db instance */
- admin(): Admin {
- return new Admin(this);
- }
- /**
- * Returns a reference to a MongoDB Collection. If it does not exist it will be created implicitly.
- *
- * @param name - the collection name we wish to access.
- * @returns return the new Collection instance
- */
- collection<TSchema extends Document = Document>(
- name: string,
- options: CollectionOptions = {}
- ): Collection<TSchema> {
- if (typeof options === 'function') {
- throw new MongoInvalidArgumentError('The callback form of this helper has been removed.');
- }
- return new Collection<TSchema>(this, name, resolveOptions(this, options));
- }
- /**
- * Get all the db statistics.
- *
- * @param options - Optional settings for the command
- */
- async stats(options?: DbStatsOptions): Promise<Document> {
- return executeOperation(this.client, new DbStatsOperation(this, resolveOptions(this, options)));
- }
- /**
- * List all collections of this database with optional filter
- *
- * @param filter - Query to filter collections by
- * @param options - Optional settings for the command
- */
- listCollections(
- filter: Document,
- options: Exclude<ListCollectionsOptions, 'nameOnly'> & { nameOnly: true }
- ): ListCollectionsCursor<Pick<CollectionInfo, 'name' | 'type'>>;
- listCollections(
- filter: Document,
- options: Exclude<ListCollectionsOptions, 'nameOnly'> & { nameOnly: false }
- ): ListCollectionsCursor<CollectionInfo>;
- listCollections<
- T extends Pick<CollectionInfo, 'name' | 'type'> | CollectionInfo =
- | Pick<CollectionInfo, 'name' | 'type'>
- | CollectionInfo
- >(filter?: Document, options?: ListCollectionsOptions): ListCollectionsCursor<T>;
- listCollections<
- T extends Pick<CollectionInfo, 'name' | 'type'> | CollectionInfo =
- | Pick<CollectionInfo, 'name' | 'type'>
- | CollectionInfo
- >(filter: Document = {}, options: ListCollectionsOptions = {}): ListCollectionsCursor<T> {
- return new ListCollectionsCursor<T>(this, filter, resolveOptions(this, options));
- }
- /**
- * Rename a collection.
- *
- * @remarks
- * This operation does not inherit options from the MongoClient.
- *
- * @param fromCollection - Name of current collection to rename
- * @param toCollection - New name of of the collection
- * @param options - Optional settings for the command
- */
- async renameCollection<TSchema extends Document = Document>(
- fromCollection: string,
- toCollection: string,
- options?: RenameOptions
- ): Promise<Collection<TSchema>> {
- // Intentionally, we do not inherit options from parent for this operation.
- return executeOperation(
- this.client,
- new RenameOperation(
- this.collection<TSchema>(fromCollection) as TODO_NODE_3286,
- toCollection,
- { ...options, new_collection: true, readPreference: ReadPreference.primary }
- ) as TODO_NODE_3286
- );
- }
- /**
- * Drop a collection from the database, removing it permanently. New accesses will create a new collection.
- *
- * @param name - Name of collection to drop
- * @param options - Optional settings for the command
- */
- async dropCollection(name: string, options?: DropCollectionOptions): Promise<boolean> {
- return executeOperation(
- this.client,
- new DropCollectionOperation(this, name, resolveOptions(this, options))
- );
- }
- /**
- * Drop a database, removing it permanently from the server.
- *
- * @param options - Optional settings for the command
- */
- async dropDatabase(options?: DropDatabaseOptions): Promise<boolean> {
- return executeOperation(
- this.client,
- new DropDatabaseOperation(this, resolveOptions(this, options))
- );
- }
- /**
- * Fetch all collections for the current db.
- *
- * @param options - Optional settings for the command
- */
- async collections(options?: ListCollectionsOptions): Promise<Collection[]> {
- return executeOperation(
- this.client,
- new CollectionsOperation(this, resolveOptions(this, options))
- );
- }
- /**
- * Creates an index on the db and collection.
- *
- * @param name - Name of the collection to create the index on.
- * @param indexSpec - Specify the field to index, or an index specification
- * @param options - Optional settings for the command
- */
- async createIndex(
- name: string,
- indexSpec: IndexSpecification,
- options?: CreateIndexesOptions
- ): Promise<string> {
- return executeOperation(
- this.client,
- new CreateIndexOperation(this, name, indexSpec, resolveOptions(this, options))
- );
- }
- /**
- * Add a user to the database
- *
- * @param username - The username for the new user
- * @param passwordOrOptions - An optional password for the new user, or the options for the command
- * @param options - Optional settings for the command
- * @deprecated Use the createUser command in `db.command()` instead.
- * @see https://www.mongodb.com/docs/manual/reference/command/createUser/
- */
- async addUser(
- username: string,
- passwordOrOptions?: string | AddUserOptions,
- options?: AddUserOptions
- ): Promise<Document> {
- options =
- options != null && typeof options === 'object'
- ? options
- : passwordOrOptions != null && typeof passwordOrOptions === 'object'
- ? passwordOrOptions
- : undefined;
- const password = typeof passwordOrOptions === 'string' ? passwordOrOptions : undefined;
- return executeOperation(
- this.client,
- new AddUserOperation(this, username, password, resolveOptions(this, options))
- );
- }
- /**
- * Remove a user from a database
- *
- * @param username - The username to remove
- * @param options - Optional settings for the command
- */
- async removeUser(username: string, options?: RemoveUserOptions): Promise<boolean> {
- return executeOperation(
- this.client,
- new RemoveUserOperation(this, username, resolveOptions(this, options))
- );
- }
- /**
- * Set the current profiling level of MongoDB
- *
- * @param level - The new profiling level (off, slow_only, all).
- * @param options - Optional settings for the command
- */
- async setProfilingLevel(
- level: ProfilingLevel,
- options?: SetProfilingLevelOptions
- ): Promise<ProfilingLevel> {
- return executeOperation(
- this.client,
- new SetProfilingLevelOperation(this, level, resolveOptions(this, options))
- );
- }
- /**
- * Retrieve the current profiling Level for MongoDB
- *
- * @param options - Optional settings for the command
- */
- async profilingLevel(options?: ProfilingLevelOptions): Promise<string> {
- return executeOperation(
- this.client,
- new ProfilingLevelOperation(this, resolveOptions(this, options))
- );
- }
- /**
- * Retrieves this collections index info.
- *
- * @param name - The name of the collection.
- * @param options - Optional settings for the command
- */
- async indexInformation(name: string, options?: IndexInformationOptions): Promise<Document> {
- return executeOperation(
- this.client,
- new IndexInformationOperation(this, name, resolveOptions(this, options))
- );
- }
- /**
- * Create a new Change Stream, watching for new changes (insertions, updates,
- * replacements, deletions, and invalidations) in this database. Will ignore all
- * changes to system collections.
- *
- * @remarks
- * watch() accepts two generic arguments for distinct use cases:
- * - The first is to provide the schema that may be defined for all the collections within this database
- * - 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 argument
- *
- * @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.
- * @param options - Optional settings for the command
- * @typeParam TSchema - Type of the data being detected by the change stream
- * @typeParam TChange - Type of the whole change stream document emitted
- */
- watch<
- TSchema extends Document = Document,
- TChange extends Document = ChangeStreamDocument<TSchema>
- >(pipeline: Document[] = [], options: ChangeStreamOptions = {}): ChangeStream<TSchema, TChange> {
- // Allow optionally not specifying a pipeline
- if (!Array.isArray(pipeline)) {
- options = pipeline;
- pipeline = [];
- }
- return new ChangeStream<TSchema, TChange>(this, pipeline, resolveOptions(this, options));
- }
- /**
- * A low level cursor API providing basic driver functionality:
- * - ClientSession management
- * - ReadPreference for server selection
- * - Running getMores automatically when a local batch is exhausted
- *
- * @param command - The command that will start a cursor on the server.
- * @param options - Configurations for running the command, bson options will apply to getMores
- */
- runCursorCommand(command: Document, options?: RunCursorCommandOptions): RunCommandCursor {
- return new RunCommandCursor(this, command, options);
- }
- }
- // TODO(NODE-3484): Refactor into MongoDBNamespace
- // Validate the database name
- function validateDatabaseName(databaseName: string) {
- if (typeof databaseName !== 'string')
- throw new MongoInvalidArgumentError('Database name must be a string');
- if (databaseName.length === 0)
- throw new MongoInvalidArgumentError('Database name cannot be the empty string');
- if (databaseName === '$external') return;
- const invalidChars = [' ', '.', '$', '/', '\\'];
- for (let i = 0; i < invalidChars.length; i++) {
- if (databaseName.indexOf(invalidChars[i]) !== -1)
- throw new MongoAPIError(`database names cannot contain the character '${invalidChars[i]}'`);
- }
- }
|