collection.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Collection = void 0;
  4. const bson_1 = require("./bson");
  5. const ordered_1 = require("./bulk/ordered");
  6. const unordered_1 = require("./bulk/unordered");
  7. const change_stream_1 = require("./change_stream");
  8. const aggregation_cursor_1 = require("./cursor/aggregation_cursor");
  9. const find_cursor_1 = require("./cursor/find_cursor");
  10. const list_indexes_cursor_1 = require("./cursor/list_indexes_cursor");
  11. const list_search_indexes_cursor_1 = require("./cursor/list_search_indexes_cursor");
  12. const error_1 = require("./error");
  13. const count_1 = require("./operations/count");
  14. const delete_1 = require("./operations/delete");
  15. const distinct_1 = require("./operations/distinct");
  16. const estimated_document_count_1 = require("./operations/estimated_document_count");
  17. const execute_operation_1 = require("./operations/execute_operation");
  18. const find_and_modify_1 = require("./operations/find_and_modify");
  19. const indexes_1 = require("./operations/indexes");
  20. const insert_1 = require("./operations/insert");
  21. const rename_1 = require("./operations/rename");
  22. const create_1 = require("./operations/search_indexes/create");
  23. const drop_1 = require("./operations/search_indexes/drop");
  24. const update_1 = require("./operations/search_indexes/update");
  25. const update_2 = require("./operations/update");
  26. const read_concern_1 = require("./read_concern");
  27. const read_preference_1 = require("./read_preference");
  28. const utils_1 = require("./utils");
  29. const write_concern_1 = require("./write_concern");
  30. /**
  31. * The **Collection** class is an internal class that embodies a MongoDB collection
  32. * allowing for insert/find/update/delete and other command operation on that MongoDB collection.
  33. *
  34. * **COLLECTION Cannot directly be instantiated**
  35. * @public
  36. *
  37. * @example
  38. * ```ts
  39. * import { MongoClient } from 'mongodb';
  40. *
  41. * interface Pet {
  42. * name: string;
  43. * kind: 'dog' | 'cat' | 'fish';
  44. * }
  45. *
  46. * const client = new MongoClient('mongodb://localhost:27017');
  47. * const pets = client.db().collection<Pet>('pets');
  48. *
  49. * const petCursor = pets.find();
  50. *
  51. * for await (const pet of petCursor) {
  52. * console.log(`${pet.name} is a ${pet.kind}!`);
  53. * }
  54. * ```
  55. */
  56. class Collection {
  57. /**
  58. * Create a new Collection instance
  59. * @internal
  60. */
  61. constructor(db, name, options) {
  62. this.db = db;
  63. // Internal state
  64. this.s = {
  65. db,
  66. options,
  67. namespace: new utils_1.MongoDBCollectionNamespace(db.databaseName, name),
  68. pkFactory: db.options?.pkFactory ?? utils_1.DEFAULT_PK_FACTORY,
  69. readPreference: read_preference_1.ReadPreference.fromOptions(options),
  70. bsonOptions: (0, bson_1.resolveBSONOptions)(options, db),
  71. readConcern: read_concern_1.ReadConcern.fromOptions(options),
  72. writeConcern: write_concern_1.WriteConcern.fromOptions(options)
  73. };
  74. this.client = db.client;
  75. }
  76. /**
  77. * The name of the database this collection belongs to
  78. */
  79. get dbName() {
  80. return this.s.namespace.db;
  81. }
  82. /**
  83. * The name of this collection
  84. */
  85. get collectionName() {
  86. return this.s.namespace.collection;
  87. }
  88. /**
  89. * The namespace of this collection, in the format `${this.dbName}.${this.collectionName}`
  90. */
  91. get namespace() {
  92. return this.fullNamespace.toString();
  93. }
  94. /**
  95. * @internal
  96. *
  97. * The `MongoDBNamespace` for the collection.
  98. */
  99. get fullNamespace() {
  100. return this.s.namespace;
  101. }
  102. /**
  103. * The current readConcern of the collection. If not explicitly defined for
  104. * this collection, will be inherited from the parent DB
  105. */
  106. get readConcern() {
  107. if (this.s.readConcern == null) {
  108. return this.db.readConcern;
  109. }
  110. return this.s.readConcern;
  111. }
  112. /**
  113. * The current readPreference of the collection. If not explicitly defined for
  114. * this collection, will be inherited from the parent DB
  115. */
  116. get readPreference() {
  117. if (this.s.readPreference == null) {
  118. return this.db.readPreference;
  119. }
  120. return this.s.readPreference;
  121. }
  122. get bsonOptions() {
  123. return this.s.bsonOptions;
  124. }
  125. /**
  126. * The current writeConcern of the collection. If not explicitly defined for
  127. * this collection, will be inherited from the parent DB
  128. */
  129. get writeConcern() {
  130. if (this.s.writeConcern == null) {
  131. return this.db.writeConcern;
  132. }
  133. return this.s.writeConcern;
  134. }
  135. /** The current index hint for the collection */
  136. get hint() {
  137. return this.s.collectionHint;
  138. }
  139. set hint(v) {
  140. this.s.collectionHint = (0, utils_1.normalizeHintField)(v);
  141. }
  142. get timeoutMS() {
  143. return this.s.options.timeoutMS;
  144. }
  145. /**
  146. * Inserts a single document into MongoDB. If documents passed in do not contain the **_id** field,
  147. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  148. * can be overridden by setting the **forceServerObjectId** flag.
  149. *
  150. * @param doc - The document to insert
  151. * @param options - Optional settings for the command
  152. */
  153. async insertOne(doc, options) {
  154. return await (0, execute_operation_1.executeOperation)(this.client, new insert_1.InsertOneOperation(this, doc, (0, utils_1.resolveOptions)(this, options)));
  155. }
  156. /**
  157. * Inserts an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
  158. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  159. * can be overridden by setting the **forceServerObjectId** flag.
  160. *
  161. * @param docs - The documents to insert
  162. * @param options - Optional settings for the command
  163. */
  164. async insertMany(docs, options) {
  165. if (!Array.isArray(docs)) {
  166. throw new error_1.MongoInvalidArgumentError('Argument "docs" must be an array of documents');
  167. }
  168. options = (0, utils_1.resolveOptions)(this, options ?? {});
  169. const acknowledged = write_concern_1.WriteConcern.fromOptions(options)?.w !== 0;
  170. try {
  171. const res = await this.bulkWrite(docs.map(doc => ({ insertOne: { document: doc } })), options);
  172. return {
  173. acknowledged,
  174. insertedCount: res.insertedCount,
  175. insertedIds: res.insertedIds
  176. };
  177. }
  178. catch (err) {
  179. if (err && err.message === 'Operation must be an object with an operation key') {
  180. throw new error_1.MongoInvalidArgumentError('Collection.insertMany() cannot be called with an array that has null/undefined values');
  181. }
  182. throw err;
  183. }
  184. }
  185. /**
  186. * Perform a bulkWrite operation without a fluent API
  187. *
  188. * Legal operation types are
  189. * - `insertOne`
  190. * - `replaceOne`
  191. * - `updateOne`
  192. * - `updateMany`
  193. * - `deleteOne`
  194. * - `deleteMany`
  195. *
  196. * If documents passed in do not contain the **_id** field,
  197. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  198. * can be overridden by setting the **forceServerObjectId** flag.
  199. *
  200. * @param operations - Bulk operations to perform
  201. * @param options - Optional settings for the command
  202. * @throws MongoDriverError if operations is not an array
  203. */
  204. async bulkWrite(operations, options) {
  205. if (!Array.isArray(operations)) {
  206. throw new error_1.MongoInvalidArgumentError('Argument "operations" must be an array of documents');
  207. }
  208. options = (0, utils_1.resolveOptions)(this, options ?? {});
  209. // TODO(NODE-7071): remove once the client doesn't need to be connected to construct
  210. // bulk operations
  211. const isConnected = this.client.topology != null;
  212. if (!isConnected) {
  213. await (0, execute_operation_1.autoConnect)(this.client);
  214. }
  215. // Create the bulk operation
  216. const bulk = options.ordered === false
  217. ? this.initializeUnorderedBulkOp(options)
  218. : this.initializeOrderedBulkOp(options);
  219. // for each op go through and add to the bulk
  220. for (const operation of operations) {
  221. bulk.raw(operation);
  222. }
  223. // Execute the bulk
  224. return await bulk.execute({ ...options });
  225. }
  226. /**
  227. * Update a single document in a collection
  228. *
  229. * The value of `update` can be either:
  230. * - UpdateFilter<TSchema> - A document that contains update operator expressions,
  231. * - Document[] - an aggregation pipeline.
  232. *
  233. * @param filter - The filter used to select the document to update
  234. * @param update - The modifications to apply
  235. * @param options - Optional settings for the command
  236. */
  237. async updateOne(filter, update, options) {
  238. return await (0, execute_operation_1.executeOperation)(this.client, new update_2.UpdateOneOperation(this.s.namespace, filter, update, (0, utils_1.resolveOptions)(this, options)));
  239. }
  240. /**
  241. * Replace a document in a collection with another document
  242. *
  243. * @param filter - The filter used to select the document to replace
  244. * @param replacement - The Document that replaces the matching document
  245. * @param options - Optional settings for the command
  246. */
  247. async replaceOne(filter, replacement, options) {
  248. return await (0, execute_operation_1.executeOperation)(this.client, new update_2.ReplaceOneOperation(this.s.namespace, filter, replacement, (0, utils_1.resolveOptions)(this, options)));
  249. }
  250. /**
  251. * Update multiple documents in a collection
  252. *
  253. * The value of `update` can be either:
  254. * - UpdateFilter<TSchema> - A document that contains update operator expressions,
  255. * - Document[] - an aggregation pipeline.
  256. *
  257. * @param filter - The filter used to select the document to update
  258. * @param update - The modifications to apply
  259. * @param options - Optional settings for the command
  260. */
  261. async updateMany(filter, update, options) {
  262. return await (0, execute_operation_1.executeOperation)(this.client, new update_2.UpdateManyOperation(this.s.namespace, filter, update, (0, utils_1.resolveOptions)(this, options)));
  263. }
  264. /**
  265. * Delete a document from a collection
  266. *
  267. * @param filter - The filter used to select the document to remove
  268. * @param options - Optional settings for the command
  269. */
  270. async deleteOne(filter = {}, options = {}) {
  271. return await (0, execute_operation_1.executeOperation)(this.client, new delete_1.DeleteOneOperation(this.s.namespace, filter, (0, utils_1.resolveOptions)(this, options)));
  272. }
  273. /**
  274. * Delete multiple documents from a collection
  275. *
  276. * @param filter - The filter used to select the documents to remove
  277. * @param options - Optional settings for the command
  278. */
  279. async deleteMany(filter = {}, options = {}) {
  280. return await (0, execute_operation_1.executeOperation)(this.client, new delete_1.DeleteManyOperation(this.s.namespace, filter, (0, utils_1.resolveOptions)(this, options)));
  281. }
  282. /**
  283. * Rename the collection.
  284. *
  285. * @remarks
  286. * This operation does not inherit options from the Db or MongoClient.
  287. *
  288. * @param newName - New name of of the collection.
  289. * @param options - Optional settings for the command
  290. */
  291. async rename(newName, options) {
  292. // Intentionally, we do not inherit options from parent for this operation.
  293. return await (0, execute_operation_1.executeOperation)(this.client, new rename_1.RenameOperation(this, newName, (0, utils_1.resolveOptions)(undefined, {
  294. ...options,
  295. readPreference: read_preference_1.ReadPreference.PRIMARY
  296. })));
  297. }
  298. /**
  299. * Drop the collection from the database, removing it permanently. New accesses will create a new collection.
  300. *
  301. * @param options - Optional settings for the command
  302. */
  303. async drop(options) {
  304. return await this.db.dropCollection(this.collectionName, options);
  305. }
  306. async findOne(filter = {}, options = {}) {
  307. // Explicitly set the limit to 1 and singleBatch to true for all commands, per the spec.
  308. // noCursorTimeout must be unset as well as batchSize.
  309. // See: https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#findone-api-details
  310. const { ...opts } = options;
  311. opts.singleBatch = true;
  312. const cursor = this.find(filter, opts).limit(1);
  313. const result = await cursor.next();
  314. await cursor.close();
  315. return result;
  316. }
  317. find(filter = {}, options = {}) {
  318. return new find_cursor_1.FindCursor(this.client, this.s.namespace, filter, (0, utils_1.resolveOptions)(this, options));
  319. }
  320. /**
  321. * Returns the options of the collection.
  322. *
  323. * @param options - Optional settings for the command
  324. */
  325. async options(options) {
  326. options = (0, utils_1.resolveOptions)(this, options);
  327. const [collection] = await this.db
  328. .listCollections({ name: this.collectionName }, { ...options, nameOnly: false })
  329. .toArray();
  330. if (collection == null || collection.options == null) {
  331. throw new error_1.MongoAPIError(`collection ${this.namespace} not found`);
  332. }
  333. return collection.options;
  334. }
  335. /**
  336. * Returns if the collection is a capped collection
  337. *
  338. * @param options - Optional settings for the command
  339. */
  340. async isCapped(options) {
  341. const { capped } = await this.options(options);
  342. return Boolean(capped);
  343. }
  344. /**
  345. * Creates an index on the db and collection collection.
  346. *
  347. * @param indexSpec - The field name or index specification to create an index for
  348. * @param options - Optional settings for the command
  349. *
  350. * @example
  351. * ```ts
  352. * const collection = client.db('foo').collection('bar');
  353. *
  354. * await collection.createIndex({ a: 1, b: -1 });
  355. *
  356. * // Alternate syntax for { c: 1, d: -1 } that ensures order of indexes
  357. * await collection.createIndex([ [c, 1], [d, -1] ]);
  358. *
  359. * // Equivalent to { e: 1 }
  360. * await collection.createIndex('e');
  361. *
  362. * // Equivalent to { f: 1, g: 1 }
  363. * await collection.createIndex(['f', 'g'])
  364. *
  365. * // Equivalent to { h: 1, i: -1 }
  366. * await collection.createIndex([ { h: 1 }, { i: -1 } ]);
  367. *
  368. * // Equivalent to { j: 1, k: -1, l: 2d }
  369. * await collection.createIndex(['j', ['k', -1], { l: '2d' }])
  370. * ```
  371. */
  372. async createIndex(indexSpec, options) {
  373. const indexes = await (0, execute_operation_1.executeOperation)(this.client, indexes_1.CreateIndexesOperation.fromIndexSpecification(this, this.collectionName, indexSpec, (0, utils_1.resolveOptions)(this, options)));
  374. return indexes[0];
  375. }
  376. /**
  377. * Creates multiple indexes in the collection, this method is only supported for
  378. * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
  379. * error.
  380. *
  381. * **Note**: Unlike {@link Collection#createIndex| createIndex}, this function takes in raw index specifications.
  382. * Index specifications are defined {@link https://www.mongodb.com/docs/manual/reference/command/createIndexes/| here}.
  383. *
  384. * @param indexSpecs - An array of index specifications to be created
  385. * @param options - Optional settings for the command
  386. *
  387. * @example
  388. * ```ts
  389. * const collection = client.db('foo').collection('bar');
  390. * await collection.createIndexes([
  391. * // Simple index on field fizz
  392. * {
  393. * key: { fizz: 1 },
  394. * }
  395. * // wildcard index
  396. * {
  397. * key: { '$**': 1 }
  398. * },
  399. * // named index on darmok and jalad
  400. * {
  401. * key: { darmok: 1, jalad: -1 }
  402. * name: 'tanagra'
  403. * }
  404. * ]);
  405. * ```
  406. */
  407. async createIndexes(indexSpecs, options) {
  408. return await (0, execute_operation_1.executeOperation)(this.client, indexes_1.CreateIndexesOperation.fromIndexDescriptionArray(this, this.collectionName, indexSpecs, (0, utils_1.resolveOptions)(this, { ...options, maxTimeMS: undefined })));
  409. }
  410. /**
  411. * Drops an index from this collection.
  412. *
  413. * @param indexName - Name of the index to drop.
  414. * @param options - Optional settings for the command
  415. */
  416. async dropIndex(indexName, options) {
  417. return await (0, execute_operation_1.executeOperation)(this.client, new indexes_1.DropIndexOperation(this, indexName, {
  418. ...(0, utils_1.resolveOptions)(this, options),
  419. readPreference: read_preference_1.ReadPreference.primary
  420. }));
  421. }
  422. /**
  423. * Drops all indexes from this collection.
  424. *
  425. * @param options - Optional settings for the command
  426. */
  427. async dropIndexes(options) {
  428. try {
  429. await (0, execute_operation_1.executeOperation)(this.client, new indexes_1.DropIndexOperation(this, '*', (0, utils_1.resolveOptions)(this, options)));
  430. return true;
  431. }
  432. catch (error) {
  433. // TODO(NODE-6517): Driver should only filter for namespace not found error. Other errors should be thrown.
  434. if (error instanceof error_1.MongoOperationTimeoutError)
  435. throw error;
  436. return false;
  437. }
  438. }
  439. /**
  440. * Get the list of all indexes information for the collection.
  441. *
  442. * @param options - Optional settings for the command
  443. */
  444. listIndexes(options) {
  445. return new list_indexes_cursor_1.ListIndexesCursor(this, (0, utils_1.resolveOptions)(this, options));
  446. }
  447. /**
  448. * Checks if one or more indexes exist on the collection, fails on first non-existing index
  449. *
  450. * @param indexes - One or more index names to check.
  451. * @param options - Optional settings for the command
  452. */
  453. async indexExists(indexes, options) {
  454. const indexNames = Array.isArray(indexes) ? indexes : [indexes];
  455. const allIndexes = new Set(await this.listIndexes(options)
  456. .map(({ name }) => name)
  457. .toArray());
  458. return indexNames.every(name => allIndexes.has(name));
  459. }
  460. async indexInformation(options) {
  461. return await this.indexes({
  462. ...options,
  463. full: options?.full ?? false
  464. });
  465. }
  466. /**
  467. * Gets an estimate of the count of documents in a collection using collection metadata.
  468. * This will always run a count command on all server versions.
  469. *
  470. * due to an oversight in versions 5.0.0-5.0.8 of MongoDB, the count command,
  471. * which estimatedDocumentCount uses in its implementation, was not included in v1 of
  472. * the Stable API, and so users of the Stable API with estimatedDocumentCount are
  473. * recommended to upgrade their server version to 5.0.9+ or set apiStrict: false to avoid
  474. * encountering errors.
  475. *
  476. * @see {@link https://www.mongodb.com/docs/manual/reference/command/count/#behavior|Count: Behavior}
  477. * @param options - Optional settings for the command
  478. */
  479. async estimatedDocumentCount(options) {
  480. return await (0, execute_operation_1.executeOperation)(this.client, new estimated_document_count_1.EstimatedDocumentCountOperation(this, (0, utils_1.resolveOptions)(this, options)));
  481. }
  482. /**
  483. * Gets the number of documents matching the filter.
  484. * For a fast count of the total documents in a collection see {@link Collection#estimatedDocumentCount| estimatedDocumentCount}.
  485. *
  486. * Due to countDocuments using the $match aggregation pipeline stage, certain query operators cannot be used in countDocuments. This includes the $where and $near query operators, among others. Details can be found in the documentation for the $match aggregation pipeline stage.
  487. *
  488. * **Note**: When migrating from {@link Collection#count| count} to {@link Collection#countDocuments| countDocuments}
  489. * the following query operators must be replaced:
  490. *
  491. * | Operator | Replacement |
  492. * | -------- | ----------- |
  493. * | `$where` | [`$expr`][1] |
  494. * | `$near` | [`$geoWithin`][2] with [`$center`][3] |
  495. * | `$nearSphere` | [`$geoWithin`][2] with [`$centerSphere`][4] |
  496. *
  497. * [1]: https://www.mongodb.com/docs/manual/reference/operator/query/expr/
  498. * [2]: https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/
  499. * [3]: https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center
  500. * [4]: https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere
  501. *
  502. * @param filter - The filter for the count
  503. * @param options - Optional settings for the command
  504. *
  505. * @see https://www.mongodb.com/docs/manual/reference/operator/query/expr/
  506. * @see https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/
  507. * @see https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center
  508. * @see https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere
  509. */
  510. async countDocuments(filter = {}, options = {}) {
  511. const pipeline = [];
  512. pipeline.push({ $match: filter });
  513. if (typeof options.skip === 'number') {
  514. pipeline.push({ $skip: options.skip });
  515. }
  516. if (typeof options.limit === 'number') {
  517. pipeline.push({ $limit: options.limit });
  518. }
  519. pipeline.push({ $group: { _id: 1, n: { $sum: 1 } } });
  520. const cursor = this.aggregate(pipeline, options);
  521. const doc = await cursor.next();
  522. await cursor.close();
  523. return doc?.n ?? 0;
  524. }
  525. async distinct(key, filter = {}, options = {}) {
  526. return await (0, execute_operation_1.executeOperation)(this.client, new distinct_1.DistinctOperation(this, key, filter, (0, utils_1.resolveOptions)(this, options)));
  527. }
  528. async indexes(options) {
  529. const indexes = await this.listIndexes(options).toArray();
  530. const full = options?.full ?? true;
  531. if (full) {
  532. return indexes;
  533. }
  534. const object = Object.fromEntries(indexes.map(({ name, key }) => [name, Object.entries(key)]));
  535. return object;
  536. }
  537. async findOneAndDelete(filter, options) {
  538. return await (0, execute_operation_1.executeOperation)(this.client, new find_and_modify_1.FindOneAndDeleteOperation(this, filter, (0, utils_1.resolveOptions)(this, options)));
  539. }
  540. async findOneAndReplace(filter, replacement, options) {
  541. return await (0, execute_operation_1.executeOperation)(this.client, new find_and_modify_1.FindOneAndReplaceOperation(this, filter, replacement, (0, utils_1.resolveOptions)(this, options)));
  542. }
  543. async findOneAndUpdate(filter, update, options) {
  544. return await (0, execute_operation_1.executeOperation)(this.client, new find_and_modify_1.FindOneAndUpdateOperation(this, filter, update, (0, utils_1.resolveOptions)(this, options)));
  545. }
  546. /**
  547. * Execute an aggregation framework pipeline against the collection, needs MongoDB \>= 2.2
  548. *
  549. * @param pipeline - An array of aggregation pipelines to execute
  550. * @param options - Optional settings for the command
  551. */
  552. aggregate(pipeline = [], options) {
  553. if (!Array.isArray(pipeline)) {
  554. throw new error_1.MongoInvalidArgumentError('Argument "pipeline" must be an array of aggregation stages');
  555. }
  556. return new aggregation_cursor_1.AggregationCursor(this.client, this.s.namespace, pipeline, (0, utils_1.resolveOptions)(this, options));
  557. }
  558. /**
  559. * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this collection.
  560. *
  561. * @remarks
  562. * watch() accepts two generic arguments for distinct use cases:
  563. * - The first is to override the schema that may be defined for this specific collection
  564. * - 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
  565. * @example
  566. * By just providing the first argument I can type the change to be `ChangeStreamDocument<{ _id: number }>`
  567. * ```ts
  568. * collection.watch<{ _id: number }>()
  569. * .on('change', change => console.log(change._id.toFixed(4)));
  570. * ```
  571. *
  572. * @example
  573. * Passing a second argument provides a way to reflect the type changes caused by an advanced pipeline.
  574. * Here, we are using a pipeline to have MongoDB filter for insert changes only and add a comment.
  575. * No need start from scratch on the ChangeStreamInsertDocument type!
  576. * By using an intersection we can save time and ensure defaults remain the same type!
  577. * ```ts
  578. * collection
  579. * .watch<Schema, ChangeStreamInsertDocument<Schema> & { comment: string }>([
  580. * { $addFields: { comment: 'big changes' } },
  581. * { $match: { operationType: 'insert' } }
  582. * ])
  583. * .on('change', change => {
  584. * change.comment.startsWith('big');
  585. * change.operationType === 'insert';
  586. * // No need to narrow in code because the generics did that for us!
  587. * expectType<Schema>(change.fullDocument);
  588. * });
  589. * ```
  590. *
  591. * @remarks
  592. * When `timeoutMS` is configured for a change stream, it will have different behaviour depending
  593. * on whether the change stream is in iterator mode or emitter mode. In both cases, a change
  594. * stream will time out if it does not receive a change event within `timeoutMS` of the last change
  595. * event.
  596. *
  597. * Note that if a change stream is consistently timing out when watching a collection, database or
  598. * client that is being changed, then this may be due to the server timing out before it can finish
  599. * processing the existing oplog. To address this, restart the change stream with a higher
  600. * `timeoutMS`.
  601. *
  602. * If the change stream times out the initial aggregate operation to establish the change stream on
  603. * the server, then the client will close the change stream. If the getMore calls to the server
  604. * time out, then the change stream will be left open, but will throw a MongoOperationTimeoutError
  605. * when in iterator mode and emit an error event that returns a MongoOperationTimeoutError in
  606. * emitter mode.
  607. *
  608. * To determine whether or not the change stream is still open following a timeout, check the
  609. * {@link ChangeStream.closed} getter.
  610. *
  611. * @example
  612. * In iterator mode, if a next() call throws a timeout error, it will attempt to resume the change stream.
  613. * The next call can just be retried after this succeeds.
  614. * ```ts
  615. * const changeStream = collection.watch([], { timeoutMS: 100 });
  616. * try {
  617. * await changeStream.next();
  618. * } catch (e) {
  619. * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
  620. * await changeStream.next();
  621. * }
  622. * throw e;
  623. * }
  624. * ```
  625. *
  626. * @example
  627. * In emitter mode, if the change stream goes `timeoutMS` without emitting a change event, it will
  628. * emit an error event that returns a MongoOperationTimeoutError, but will not close the change
  629. * stream unless the resume attempt fails. There is no need to re-establish change listeners as
  630. * this will automatically continue emitting change events once the resume attempt completes.
  631. *
  632. * ```ts
  633. * const changeStream = collection.watch([], { timeoutMS: 100 });
  634. * changeStream.on('change', console.log);
  635. * changeStream.on('error', e => {
  636. * if (e instanceof MongoOperationTimeoutError && !changeStream.closed) {
  637. * // do nothing
  638. * } else {
  639. * changeStream.close();
  640. * }
  641. * });
  642. * ```
  643. *
  644. * @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.
  645. * @param options - Optional settings for the command
  646. * @typeParam TLocal - Type of the data being detected by the change stream
  647. * @typeParam TChange - Type of the whole change stream document emitted
  648. */
  649. watch(pipeline = [], options = {}) {
  650. // Allow optionally not specifying a pipeline
  651. if (!Array.isArray(pipeline)) {
  652. options = pipeline;
  653. pipeline = [];
  654. }
  655. return new change_stream_1.ChangeStream(this, pipeline, (0, utils_1.resolveOptions)(this, options));
  656. }
  657. /**
  658. * Initiate an Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
  659. *
  660. * @throws MongoNotConnectedError
  661. * @remarks
  662. * **NOTE:** MongoClient must be connected prior to calling this method due to a known limitation in this legacy implementation.
  663. * However, `collection.bulkWrite()` provides an equivalent API that does not require prior connecting.
  664. */
  665. initializeUnorderedBulkOp(options) {
  666. return new unordered_1.UnorderedBulkOperation(this, (0, utils_1.resolveOptions)(this, options));
  667. }
  668. /**
  669. * Initiate an In order bulk write operation. Operations will be serially executed in the order they are added, creating a new operation for each switch in types.
  670. *
  671. * @throws MongoNotConnectedError
  672. * @remarks
  673. * **NOTE:** MongoClient must be connected prior to calling this method due to a known limitation in this legacy implementation.
  674. * However, `collection.bulkWrite()` provides an equivalent API that does not require prior connecting.
  675. */
  676. initializeOrderedBulkOp(options) {
  677. return new ordered_1.OrderedBulkOperation(this, (0, utils_1.resolveOptions)(this, options));
  678. }
  679. /**
  680. * An estimated count of matching documents in the db to a filter.
  681. *
  682. * **NOTE:** This method has been deprecated, since it does not provide an accurate count of the documents
  683. * in a collection. To obtain an accurate count of documents in the collection, use {@link Collection#countDocuments| countDocuments}.
  684. * To obtain an estimated count of all documents in the collection, use {@link Collection#estimatedDocumentCount| estimatedDocumentCount}.
  685. *
  686. * @deprecated use {@link Collection#countDocuments| countDocuments} or {@link Collection#estimatedDocumentCount| estimatedDocumentCount} instead
  687. *
  688. * @param filter - The filter for the count.
  689. * @param options - Optional settings for the command
  690. */
  691. async count(filter = {}, options = {}) {
  692. return await (0, execute_operation_1.executeOperation)(this.client, new count_1.CountOperation(this.fullNamespace, filter, (0, utils_1.resolveOptions)(this, options)));
  693. }
  694. listSearchIndexes(indexNameOrOptions, options) {
  695. options =
  696. typeof indexNameOrOptions === 'object' ? indexNameOrOptions : options == null ? {} : options;
  697. const indexName = indexNameOrOptions == null
  698. ? null
  699. : typeof indexNameOrOptions === 'object'
  700. ? null
  701. : indexNameOrOptions;
  702. return new list_search_indexes_cursor_1.ListSearchIndexesCursor(this, indexName, options);
  703. }
  704. /**
  705. * Creates a single search index for the collection.
  706. *
  707. * @param description - The index description for the new search index.
  708. * @returns A promise that resolves to the name of the new search index.
  709. *
  710. * @remarks Only available when used against a 7.0+ Atlas cluster.
  711. */
  712. async createSearchIndex(description) {
  713. const [index] = await this.createSearchIndexes([description]);
  714. return index;
  715. }
  716. /**
  717. * Creates multiple search indexes for the current collection.
  718. *
  719. * @param descriptions - An array of `SearchIndexDescription`s for the new search indexes.
  720. * @returns A promise that resolves to an array of the newly created search index names.
  721. *
  722. * @remarks Only available when used against a 7.0+ Atlas cluster.
  723. * @returns
  724. */
  725. async createSearchIndexes(descriptions) {
  726. return await (0, execute_operation_1.executeOperation)(this.client, new create_1.CreateSearchIndexesOperation(this, descriptions));
  727. }
  728. /**
  729. * Deletes a search index by index name.
  730. *
  731. * @param name - The name of the search index to be deleted.
  732. *
  733. * @remarks Only available when used against a 7.0+ Atlas cluster.
  734. */
  735. async dropSearchIndex(name) {
  736. return await (0, execute_operation_1.executeOperation)(this.client, new drop_1.DropSearchIndexOperation(this, name));
  737. }
  738. /**
  739. * Updates a search index by replacing the existing index definition with the provided definition.
  740. *
  741. * @param name - The name of the search index to update.
  742. * @param definition - The new search index definition.
  743. *
  744. * @remarks Only available when used against a 7.0+ Atlas cluster.
  745. */
  746. async updateSearchIndex(name, definition) {
  747. return await (0, execute_operation_1.executeOperation)(this.client, new update_1.UpdateSearchIndexOperation(this, name, definition));
  748. }
  749. }
  750. exports.Collection = Collection;
  751. //# sourceMappingURL=collection.js.map