PageRenderTime 72ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/node_modules/mongoose/node_modules/mongodb/lib/collection.js

https://bitbucket.org/coleman333/smartsite
JavaScript | 3143 lines | 1548 code | 426 blank | 1169 comment | 525 complexity | cd07cd466b16d64808c290bfbd7f811e MD5 | raw file
Possible License(s): Apache-2.0, JSON, BSD-3-Clause, 0BSD, MIT, CC-BY-SA-3.0
  1. 'use strict';
  2. var checkCollectionName = require('./utils').checkCollectionName,
  3. ObjectID = require('mongodb-core').BSON.ObjectID,
  4. Long = require('mongodb-core').BSON.Long,
  5. Code = require('mongodb-core').BSON.Code,
  6. f = require('util').format,
  7. AggregationCursor = require('./aggregation_cursor'),
  8. MongoError = require('mongodb-core').MongoError,
  9. shallowClone = require('./utils').shallowClone,
  10. isObject = require('./utils').isObject,
  11. toError = require('./utils').toError,
  12. normalizeHintField = require('./utils').normalizeHintField,
  13. handleCallback = require('./utils').handleCallback,
  14. decorateCommand = require('./utils').decorateCommand,
  15. formattedOrderClause = require('./utils').formattedOrderClause,
  16. ReadPreference = require('mongodb-core').ReadPreference,
  17. CommandCursor = require('./command_cursor'),
  18. Define = require('./metadata'),
  19. Cursor = require('./cursor'),
  20. unordered = require('./bulk/unordered'),
  21. ordered = require('./bulk/ordered'),
  22. ChangeStream = require('./change_stream'),
  23. executeOperation = require('./utils').executeOperation;
  24. /**
  25. * @fileOverview The **Collection** class is an internal class that embodies a MongoDB collection
  26. * allowing for insert/update/remove/find and other command operation on that MongoDB collection.
  27. *
  28. * **COLLECTION Cannot directly be instantiated**
  29. * @example
  30. * const MongoClient = require('mongodb').MongoClient;
  31. * const test = require('assert');
  32. * // Connection url
  33. * const url = 'mongodb://localhost:27017';
  34. * // Database Name
  35. * const dbName = 'test';
  36. * // Connect using MongoClient
  37. * MongoClient.connect(url, function(err, client) {
  38. * // Create a collection we want to drop later
  39. * const col = client.db(dbName).collection('createIndexExample1');
  40. * // Show that duplicate records got dropped
  41. * col.find({}).toArray(function(err, items) {
  42. * test.equal(null, err);
  43. * test.equal(4, items.length);
  44. * client.close();
  45. * });
  46. * });
  47. */
  48. var mergeKeys = ['readPreference', 'ignoreUndefined'];
  49. /**
  50. * Create a new Collection instance (INTERNAL TYPE, do not instantiate directly)
  51. * @class
  52. * @property {string} collectionName Get the collection name.
  53. * @property {string} namespace Get the full collection namespace.
  54. * @property {object} writeConcern The current write concern values.
  55. * @property {object} readConcern The current read concern values.
  56. * @property {object} hint Get current index hint for collection.
  57. * @return {Collection} a Collection instance.
  58. */
  59. var Collection = function(db, topology, dbName, name, pkFactory, options) {
  60. checkCollectionName(name);
  61. // Unpack variables
  62. var internalHint = null;
  63. var slaveOk = options == null || options.slaveOk == null ? db.slaveOk : options.slaveOk;
  64. var serializeFunctions =
  65. options == null || options.serializeFunctions == null
  66. ? db.s.options.serializeFunctions
  67. : options.serializeFunctions;
  68. var raw = options == null || options.raw == null ? db.s.options.raw : options.raw;
  69. var promoteLongs =
  70. options == null || options.promoteLongs == null
  71. ? db.s.options.promoteLongs
  72. : options.promoteLongs;
  73. var promoteValues =
  74. options == null || options.promoteValues == null
  75. ? db.s.options.promoteValues
  76. : options.promoteValues;
  77. var promoteBuffers =
  78. options == null || options.promoteBuffers == null
  79. ? db.s.options.promoteBuffers
  80. : options.promoteBuffers;
  81. var readPreference = null;
  82. var collectionHint = null;
  83. var namespace = f('%s.%s', dbName, name);
  84. // Get the promiseLibrary
  85. var promiseLibrary = options.promiseLibrary || Promise;
  86. // Assign the right collection level readPreference
  87. if (options && options.readPreference) {
  88. readPreference = options.readPreference;
  89. } else if (db.options.readPreference) {
  90. readPreference = db.options.readPreference;
  91. }
  92. // Set custom primary key factory if provided
  93. pkFactory = pkFactory == null ? ObjectID : pkFactory;
  94. // Internal state
  95. this.s = {
  96. // Set custom primary key factory if provided
  97. pkFactory: pkFactory,
  98. // Db
  99. db: db,
  100. // Topology
  101. topology: topology,
  102. // dbName
  103. dbName: dbName,
  104. // Options
  105. options: options,
  106. // Namespace
  107. namespace: namespace,
  108. // Read preference
  109. readPreference: readPreference,
  110. // SlaveOK
  111. slaveOk: slaveOk,
  112. // Serialize functions
  113. serializeFunctions: serializeFunctions,
  114. // Raw
  115. raw: raw,
  116. // promoteLongs
  117. promoteLongs: promoteLongs,
  118. // promoteValues
  119. promoteValues: promoteValues,
  120. // promoteBuffers
  121. promoteBuffers: promoteBuffers,
  122. // internalHint
  123. internalHint: internalHint,
  124. // collectionHint
  125. collectionHint: collectionHint,
  126. // Name
  127. name: name,
  128. // Promise library
  129. promiseLibrary: promiseLibrary,
  130. // Read Concern
  131. readConcern: options.readConcern
  132. };
  133. };
  134. var define = (Collection.define = new Define('Collection', Collection, false));
  135. Object.defineProperty(Collection.prototype, 'dbName', {
  136. enumerable: true,
  137. get: function() {
  138. return this.s.dbName;
  139. }
  140. });
  141. Object.defineProperty(Collection.prototype, 'collectionName', {
  142. enumerable: true,
  143. get: function() {
  144. return this.s.name;
  145. }
  146. });
  147. Object.defineProperty(Collection.prototype, 'namespace', {
  148. enumerable: true,
  149. get: function() {
  150. return this.s.namespace;
  151. }
  152. });
  153. Object.defineProperty(Collection.prototype, 'readConcern', {
  154. enumerable: true,
  155. get: function() {
  156. return this.s.readConcern || { level: 'local' };
  157. }
  158. });
  159. Object.defineProperty(Collection.prototype, 'writeConcern', {
  160. enumerable: true,
  161. get: function() {
  162. var ops = {};
  163. if (this.s.options.w != null) ops.w = this.s.options.w;
  164. if (this.s.options.j != null) ops.j = this.s.options.j;
  165. if (this.s.options.fsync != null) ops.fsync = this.s.options.fsync;
  166. if (this.s.options.wtimeout != null) ops.wtimeout = this.s.options.wtimeout;
  167. return ops;
  168. }
  169. });
  170. /**
  171. * @ignore
  172. */
  173. Object.defineProperty(Collection.prototype, 'hint', {
  174. enumerable: true,
  175. get: function() {
  176. return this.s.collectionHint;
  177. },
  178. set: function(v) {
  179. this.s.collectionHint = normalizeHintField(v);
  180. }
  181. });
  182. /**
  183. * Creates a cursor for a query that can be used to iterate over results from MongoDB
  184. * @method
  185. * @param {object} [query={}] The cursor query object.
  186. * @param {object} [options=null] Optional settings.
  187. * @param {number} [options.limit=0] Sets the limit of documents returned in the query.
  188. * @param {(array|object)} [options.sort=null] Set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
  189. * @param {object} [options.projection=null] The fields to return in the query. Object of fields to include or exclude (not both), {'a':1}
  190. * @param {object} [options.fields=null] **Deprecated** Use `options.projection` instead
  191. * @param {number} [options.skip=0] Set to skip N documents ahead in your query (useful for pagination).
  192. * @param {Object} [options.hint=null] Tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1}
  193. * @param {boolean} [options.explain=false] Explain the query instead of returning the data.
  194. * @param {boolean} [options.snapshot=false] Snapshot query.
  195. * @param {boolean} [options.timeout=false] Specify if the cursor can timeout.
  196. * @param {boolean} [options.tailable=false] Specify if the cursor is tailable.
  197. * @param {number} [options.batchSize=0] Set the batchSize for the getMoreCommand when iterating over the query results.
  198. * @param {boolean} [options.returnKey=false] Only return the index key.
  199. * @param {number} [options.maxScan=null] Limit the number of items to scan.
  200. * @param {number} [options.min=null] Set index bounds.
  201. * @param {number} [options.max=null] Set index bounds.
  202. * @param {boolean} [options.showDiskLoc=false] Show disk location of results.
  203. * @param {string} [options.comment=null] You can put a $comment field on a query to make looking in the profiler logs simpler.
  204. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  205. * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
  206. * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
  207. * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
  208. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  209. * @param {boolean} [options.partial=false] Specify if the cursor should return partial results when querying against a sharded system
  210. * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
  211. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  212. * @param {ClientSession} [options.session] optional session to use for this operation
  213. * @throws {MongoError}
  214. * @return {Cursor}
  215. */
  216. Collection.prototype.find = function(query, options, callback) {
  217. let selector = query;
  218. // figuring out arguments
  219. if (typeof callback !== 'function') {
  220. if (typeof options === 'function') {
  221. callback = options;
  222. options = undefined;
  223. } else if (options == null) {
  224. callback = typeof selector === 'function' ? selector : undefined;
  225. selector = typeof selector === 'object' ? selector : undefined;
  226. }
  227. }
  228. // Ensure selector is not null
  229. selector = selector == null ? {} : selector;
  230. // Validate correctness off the selector
  231. var object = selector;
  232. if (Buffer.isBuffer(object)) {
  233. var object_size = object[0] | (object[1] << 8) | (object[2] << 16) | (object[3] << 24);
  234. if (object_size !== object.length) {
  235. var error = new Error(
  236. 'query selector raw message size does not match message header size [' +
  237. object.length +
  238. '] != [' +
  239. object_size +
  240. ']'
  241. );
  242. error.name = 'MongoError';
  243. throw error;
  244. }
  245. }
  246. // Check special case where we are using an objectId
  247. if (selector != null && selector._bsontype === 'ObjectID') {
  248. selector = { _id: selector };
  249. }
  250. if (!options) options = {};
  251. let projection = options.projection || options.fields;
  252. if (projection && !Buffer.isBuffer(projection) && Array.isArray(projection)) {
  253. projection = projection.length
  254. ? projection.reduce((result, field) => {
  255. result[field] = 1;
  256. return result;
  257. }, {})
  258. : { _id: 1 };
  259. }
  260. var newOptions = {};
  261. // Make a shallow copy of the collection options
  262. for (var key in this.s.options) {
  263. if (mergeKeys.indexOf(key) !== -1) {
  264. newOptions[key] = this.s.options[key];
  265. }
  266. }
  267. // Make a shallow copy of options
  268. for (var optKey in options) {
  269. newOptions[optKey] = options[optKey];
  270. }
  271. // Unpack options
  272. newOptions.skip = options.skip ? options.skip : 0;
  273. newOptions.limit = options.limit ? options.limit : 0;
  274. newOptions.raw = typeof options.raw === 'boolean' ? options.raw : this.s.raw;
  275. newOptions.hint = options.hint != null ? normalizeHintField(options.hint) : this.s.collectionHint;
  276. newOptions.timeout = typeof options.timeout === 'undefined' ? undefined : options.timeout;
  277. // // If we have overridden slaveOk otherwise use the default db setting
  278. newOptions.slaveOk = options.slaveOk != null ? options.slaveOk : this.s.db.slaveOk;
  279. // Add read preference if needed
  280. newOptions = getReadPreference(this, newOptions, this.s.db);
  281. // Set slave ok to true if read preference different from primary
  282. if (
  283. newOptions.readPreference != null &&
  284. (newOptions.readPreference !== 'primary' || newOptions.readPreference.mode !== 'primary')
  285. ) {
  286. newOptions.slaveOk = true;
  287. }
  288. // Ensure the query is an object
  289. if (selector != null && typeof selector !== 'object') {
  290. throw MongoError.create({ message: 'query selector must be an object', driver: true });
  291. }
  292. // Build the find command
  293. var findCommand = {
  294. find: this.s.namespace,
  295. limit: newOptions.limit,
  296. skip: newOptions.skip,
  297. query: selector
  298. };
  299. // Ensure we use the right await data option
  300. if (typeof newOptions.awaitdata === 'boolean') {
  301. newOptions.awaitData = newOptions.awaitdata;
  302. }
  303. // Translate to new command option noCursorTimeout
  304. if (typeof newOptions.timeout === 'boolean') newOptions.noCursorTimeout = newOptions.timeout;
  305. // Merge in options to command
  306. for (var name in newOptions) {
  307. if (newOptions[name] != null && name !== 'session') {
  308. findCommand[name] = newOptions[name];
  309. }
  310. }
  311. if (projection) findCommand.fields = projection;
  312. // Add db object to the new options
  313. newOptions.db = this.s.db;
  314. // Add the promise library
  315. newOptions.promiseLibrary = this.s.promiseLibrary;
  316. // Set raw if available at collection level
  317. if (newOptions.raw == null && typeof this.s.raw === 'boolean') newOptions.raw = this.s.raw;
  318. // Set promoteLongs if available at collection level
  319. if (newOptions.promoteLongs == null && typeof this.s.promoteLongs === 'boolean')
  320. newOptions.promoteLongs = this.s.promoteLongs;
  321. if (newOptions.promoteValues == null && typeof this.s.promoteValues === 'boolean')
  322. newOptions.promoteValues = this.s.promoteValues;
  323. if (newOptions.promoteBuffers == null && typeof this.s.promoteBuffers === 'boolean')
  324. newOptions.promoteBuffers = this.s.promoteBuffers;
  325. // Sort options
  326. if (findCommand.sort) {
  327. findCommand.sort = formattedOrderClause(findCommand.sort);
  328. }
  329. // Set the readConcern
  330. decorateWithReadConcern(findCommand, this, options);
  331. // Decorate find command with collation options
  332. decorateWithCollation(findCommand, this, options);
  333. // Create the cursor
  334. if (typeof callback === 'function')
  335. return handleCallback(
  336. callback,
  337. null,
  338. this.s.topology.cursor(this.s.namespace, findCommand, newOptions)
  339. );
  340. return this.s.topology.cursor(this.s.namespace, findCommand, newOptions);
  341. };
  342. define.classMethod('find', { callback: false, promise: false, returns: [Cursor] });
  343. /**
  344. * Inserts a single document into MongoDB. If documents passed in do not contain the **_id** field,
  345. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  346. * can be overridden by setting the **forceServerObjectId** flag.
  347. *
  348. * @method
  349. * @param {object} doc Document to insert.
  350. * @param {object} [options=null] Optional settings.
  351. * @param {(number|string)} [options.w=null] The write concern.
  352. * @param {number} [options.wtimeout=null] The write concern timeout.
  353. * @param {boolean} [options.j=false] Specify a journal write concern.
  354. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  355. * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
  356. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  357. * @param {ClientSession} [options.session] optional session to use for this operation
  358. * @param {Collection~insertOneWriteOpCallback} [callback] The command result callback
  359. * @return {Promise} returns Promise if no callback passed
  360. */
  361. Collection.prototype.insertOne = function(doc, options, callback) {
  362. if (typeof options === 'function') (callback = options), (options = {});
  363. options = options || {};
  364. // Add ignoreUndfined
  365. if (this.s.options.ignoreUndefined) {
  366. options = shallowClone(options);
  367. options.ignoreUndefined = this.s.options.ignoreUndefined;
  368. }
  369. return executeOperation(this.s.topology, insertOne, [this, doc, options, callback]);
  370. };
  371. var insertOne = function(self, doc, options, callback) {
  372. if (Array.isArray(doc)) {
  373. return callback(
  374. MongoError.create({ message: 'doc parameter must be an object', driver: true })
  375. );
  376. }
  377. insertDocuments(self, [doc], options, function(err, r) {
  378. if (callback == null) return;
  379. if (err && callback) return callback(err);
  380. // Workaround for pre 2.6 servers
  381. if (r == null) return callback(null, { result: { ok: 1 } });
  382. // Add values to top level to ensure crud spec compatibility
  383. r.insertedCount = r.result.n;
  384. r.insertedId = doc._id;
  385. if (callback) callback(null, r);
  386. });
  387. };
  388. var mapInserManyResults = function(docs, r) {
  389. var finalResult = {
  390. result: { ok: 1, n: r.insertedCount },
  391. ops: docs,
  392. insertedCount: r.insertedCount,
  393. insertedIds: r.insertedIds
  394. };
  395. if (r.getLastOp()) {
  396. finalResult.result.opTime = r.getLastOp();
  397. }
  398. return finalResult;
  399. };
  400. define.classMethod('insertOne', { callback: true, promise: true });
  401. /**
  402. * Inserts an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
  403. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  404. * can be overridden by setting the **forceServerObjectId** flag.
  405. *
  406. * @method
  407. * @param {object[]} docs Documents to insert.
  408. * @param {object} [options=null] Optional settings.
  409. * @param {(number|string)} [options.w=null] The write concern.
  410. * @param {number} [options.wtimeout=null] The write concern timeout.
  411. * @param {boolean} [options.j=false] Specify a journal write concern.
  412. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  413. * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
  414. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  415. * @param {boolean} [options.ordered=true] If true, when an insert fails, don't execute the remaining writes. If false, continue with remaining inserts when one fails.
  416. * @param {ClientSession} [options.session] optional session to use for this operation
  417. * @param {Collection~insertWriteOpCallback} [callback] The command result callback
  418. * @return {Promise} returns Promise if no callback passed
  419. */
  420. Collection.prototype.insertMany = function(docs, options, callback) {
  421. var self = this;
  422. if (typeof options === 'function') (callback = options), (options = {});
  423. options = options ? shallowClone(options) : { ordered: true };
  424. if (!Array.isArray(docs) && typeof callback === 'function') {
  425. return callback(
  426. MongoError.create({ message: 'docs parameter must be an array of documents', driver: true })
  427. );
  428. } else if (!Array.isArray(docs)) {
  429. return new this.s.promiseLibrary(function(resolve, reject) {
  430. reject(
  431. MongoError.create({ message: 'docs parameter must be an array of documents', driver: true })
  432. );
  433. });
  434. }
  435. // If keep going set unordered
  436. options['serializeFunctions'] = options['serializeFunctions'] || self.s.serializeFunctions;
  437. // Set up the force server object id
  438. var forceServerObjectId =
  439. typeof options.forceServerObjectId === 'boolean'
  440. ? options.forceServerObjectId
  441. : self.s.db.options.forceServerObjectId;
  442. // Do we want to force the server to assign the _id key
  443. if (forceServerObjectId !== true) {
  444. // Add _id if not specified
  445. for (var i = 0; i < docs.length; i++) {
  446. if (docs[i]._id == null) docs[i]._id = self.s.pkFactory.createPk();
  447. }
  448. }
  449. // Generate the bulk write operations
  450. var operations = [
  451. {
  452. insertMany: docs
  453. }
  454. ];
  455. return executeOperation(this.s.topology, bulkWrite, [this, operations, options, callback], {
  456. resultMutator: result => mapInserManyResults(docs, result)
  457. });
  458. };
  459. define.classMethod('insertMany', { callback: true, promise: true });
  460. /**
  461. * @typedef {Object} Collection~BulkWriteOpResult
  462. * @property {number} insertedCount Number of documents inserted.
  463. * @property {number} matchedCount Number of documents matched for update.
  464. * @property {number} modifiedCount Number of documents modified.
  465. * @property {number} deletedCount Number of documents deleted.
  466. * @property {number} upsertedCount Number of documents upserted.
  467. * @property {object} insertedIds Inserted document generated Id's, hash key is the index of the originating operation
  468. * @property {object} upsertedIds Upserted document generated Id's, hash key is the index of the originating operation
  469. * @property {object} result The command result object.
  470. */
  471. /**
  472. * The callback format for inserts
  473. * @callback Collection~bulkWriteOpCallback
  474. * @param {BulkWriteError} error An error instance representing the error during the execution.
  475. * @param {Collection~BulkWriteOpResult} result The result object if the command was executed successfully.
  476. */
  477. /**
  478. * Perform a bulkWrite operation without a fluent API
  479. *
  480. * Legal operation types are
  481. *
  482. * { insertOne: { document: { a: 1 } } }
  483. *
  484. * { updateOne: { filter: {a:2}, update: {$set: {a:2}}, upsert:true } }
  485. *
  486. * { updateMany: { filter: {a:2}, update: {$set: {a:2}}, upsert:true } }
  487. *
  488. * { deleteOne: { filter: {c:1} } }
  489. *
  490. * { deleteMany: { filter: {c:1} } }
  491. *
  492. * { replaceOne: { filter: {c:3}, replacement: {c:4}, upsert:true}}
  493. *
  494. * If documents passed in do not contain the **_id** field,
  495. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  496. * can be overridden by setting the **forceServerObjectId** flag.
  497. *
  498. * @method
  499. * @param {object[]} operations Bulk operations to perform.
  500. * @param {object} [options=null] Optional settings.
  501. * @param {(number|string)} [options.w=null] The write concern.
  502. * @param {number} [options.wtimeout=null] The write concern timeout.
  503. * @param {boolean} [options.j=false] Specify a journal write concern.
  504. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  505. * @param {boolean} [options.ordered=true] Execute write operation in ordered or unordered fashion.
  506. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  507. * @param {ClientSession} [options.session] optional session to use for this operation
  508. * @param {Collection~bulkWriteOpCallback} [callback] The command result callback
  509. * @return {Promise} returns Promise if no callback passed
  510. */
  511. Collection.prototype.bulkWrite = function(operations, options, callback) {
  512. if (typeof options === 'function') (callback = options), (options = {});
  513. options = options || { ordered: true };
  514. if (!Array.isArray(operations)) {
  515. throw MongoError.create({ message: 'operations must be an array of documents', driver: true });
  516. }
  517. return executeOperation(this.s.topology, bulkWrite, [this, operations, options, callback]);
  518. };
  519. var bulkWrite = function(self, operations, options, callback) {
  520. // Add ignoreUndfined
  521. if (self.s.options.ignoreUndefined) {
  522. options = shallowClone(options);
  523. options.ignoreUndefined = self.s.options.ignoreUndefined;
  524. }
  525. // Create the bulk operation
  526. var bulk =
  527. options.ordered === true || options.ordered == null
  528. ? self.initializeOrderedBulkOp(options)
  529. : self.initializeUnorderedBulkOp(options);
  530. // Do we have a collation
  531. var collation = false;
  532. // for each op go through and add to the bulk
  533. try {
  534. for (var i = 0; i < operations.length; i++) {
  535. // Get the operation type
  536. var key = Object.keys(operations[i])[0];
  537. // Check if we have a collation
  538. if (operations[i][key].collation) {
  539. collation = true;
  540. }
  541. // Pass to the raw bulk
  542. bulk.raw(operations[i]);
  543. }
  544. } catch (err) {
  545. return callback(err, null);
  546. }
  547. // Final options for write concern
  548. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  549. var writeCon = finalOptions.writeConcern ? finalOptions.writeConcern : {};
  550. var capabilities = self.s.topology.capabilities();
  551. // Did the user pass in a collation, check if our write server supports it
  552. if (collation && capabilities && !capabilities.commandsTakeCollation) {
  553. return callback(new MongoError(f('server/primary/mongos does not support collation')));
  554. }
  555. // Execute the bulk
  556. bulk.execute(writeCon, finalOptions, function(err, r) {
  557. // We have connection level error
  558. if (!r && err) {
  559. return callback(err, null);
  560. }
  561. r.insertedCount = r.nInserted;
  562. r.matchedCount = r.nMatched;
  563. r.modifiedCount = r.nModified || 0;
  564. r.deletedCount = r.nRemoved;
  565. r.upsertedCount = r.getUpsertedIds().length;
  566. r.upsertedIds = {};
  567. r.insertedIds = {};
  568. // Update the n
  569. r.n = r.insertedCount;
  570. // Inserted documents
  571. var inserted = r.getInsertedIds();
  572. // Map inserted ids
  573. for (var i = 0; i < inserted.length; i++) {
  574. r.insertedIds[inserted[i].index] = inserted[i]._id;
  575. }
  576. // Upserted documents
  577. var upserted = r.getUpsertedIds();
  578. // Map upserted ids
  579. for (i = 0; i < upserted.length; i++) {
  580. r.upsertedIds[upserted[i].index] = upserted[i]._id;
  581. }
  582. // Return the results
  583. callback(null, r);
  584. });
  585. };
  586. var insertDocuments = function(self, docs, options, callback) {
  587. if (typeof options === 'function') (callback = options), (options = {});
  588. options = options || {};
  589. // Ensure we are operating on an array op docs
  590. docs = Array.isArray(docs) ? docs : [docs];
  591. // Get the write concern options
  592. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  593. // If keep going set unordered
  594. if (finalOptions.keepGoing === true) finalOptions.ordered = false;
  595. finalOptions['serializeFunctions'] = options['serializeFunctions'] || self.s.serializeFunctions;
  596. // Set up the force server object id
  597. var forceServerObjectId =
  598. typeof options.forceServerObjectId === 'boolean'
  599. ? options.forceServerObjectId
  600. : self.s.db.options.forceServerObjectId;
  601. // Add _id if not specified
  602. if (forceServerObjectId !== true) {
  603. for (var i = 0; i < docs.length; i++) {
  604. if (docs[i]._id === void 0) docs[i]._id = self.s.pkFactory.createPk();
  605. }
  606. }
  607. // File inserts
  608. self.s.topology.insert(self.s.namespace, docs, finalOptions, function(err, result) {
  609. if (callback == null) return;
  610. if (err) return handleCallback(callback, err);
  611. if (result == null) return handleCallback(callback, null, null);
  612. if (result.result.code) return handleCallback(callback, toError(result.result));
  613. if (result.result.writeErrors)
  614. return handleCallback(callback, toError(result.result.writeErrors[0]));
  615. // Add docs to the list
  616. result.ops = docs;
  617. // Return the results
  618. handleCallback(callback, null, result);
  619. });
  620. };
  621. define.classMethod('bulkWrite', { callback: true, promise: true });
  622. /**
  623. * @typedef {Object} Collection~WriteOpResult
  624. * @property {object[]} ops All the documents inserted using insertOne/insertMany/replaceOne. Documents contain the _id field if forceServerObjectId == false for insertOne/insertMany
  625. * @property {object} connection The connection object used for the operation.
  626. * @property {object} result The command result object.
  627. */
  628. /**
  629. * The callback format for inserts
  630. * @callback Collection~writeOpCallback
  631. * @param {MongoError} error An error instance representing the error during the execution.
  632. * @param {Collection~WriteOpResult} result The result object if the command was executed successfully.
  633. */
  634. /**
  635. * @typedef {Object} Collection~insertWriteOpResult
  636. * @property {Number} insertedCount The total amount of documents inserted.
  637. * @property {object[]} ops All the documents inserted using insertOne/insertMany/replaceOne. Documents contain the _id field if forceServerObjectId == false for insertOne/insertMany
  638. * @property {Object.<Number, ObjectId>} insertedIds Map of the index of the inserted document to the id of the inserted document.
  639. * @property {object} connection The connection object used for the operation.
  640. * @property {object} result The raw command result object returned from MongoDB (content might vary by server version).
  641. * @property {Number} result.ok Is 1 if the command executed correctly.
  642. * @property {Number} result.n The total count of documents inserted.
  643. */
  644. /**
  645. * @typedef {Object} Collection~insertOneWriteOpResult
  646. * @property {Number} insertedCount The total amount of documents inserted.
  647. * @property {object[]} ops All the documents inserted using insertOne/insertMany/replaceOne. Documents contain the _id field if forceServerObjectId == false for insertOne/insertMany
  648. * @property {ObjectId} insertedId The driver generated ObjectId for the insert operation.
  649. * @property {object} connection The connection object used for the operation.
  650. * @property {object} result The raw command result object returned from MongoDB (content might vary by server version).
  651. * @property {Number} result.ok Is 1 if the command executed correctly.
  652. * @property {Number} result.n The total count of documents inserted.
  653. */
  654. /**
  655. * The callback format for inserts
  656. * @callback Collection~insertWriteOpCallback
  657. * @param {MongoError} error An error instance representing the error during the execution.
  658. * @param {Collection~insertWriteOpResult} result The result object if the command was executed successfully.
  659. */
  660. /**
  661. * The callback format for inserts
  662. * @callback Collection~insertOneWriteOpCallback
  663. * @param {MongoError} error An error instance representing the error during the execution.
  664. * @param {Collection~insertOneWriteOpResult} result The result object if the command was executed successfully.
  665. */
  666. /**
  667. * Inserts a single document or a an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
  668. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  669. * can be overridden by setting the **forceServerObjectId** flag.
  670. *
  671. * @method
  672. * @param {(object|object[])} docs Documents to insert.
  673. * @param {object} [options=null] Optional settings.
  674. * @param {(number|string)} [options.w=null] The write concern.
  675. * @param {number} [options.wtimeout=null] The write concern timeout.
  676. * @param {boolean} [options.j=false] Specify a journal write concern.
  677. * @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
  678. * @param {boolean} [options.forceServerObjectId=false] Force server to assign _id values instead of driver.
  679. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  680. * @param {ClientSession} [options.session] optional session to use for this operation
  681. * @param {Collection~insertWriteOpCallback} [callback] The command result callback
  682. * @return {Promise} returns Promise if no callback passed
  683. * @deprecated Use insertOne, insertMany or bulkWrite
  684. */
  685. Collection.prototype.insert = function(docs, options, callback) {
  686. if (typeof options === 'function') (callback = options), (options = {});
  687. options = options || { ordered: false };
  688. docs = !Array.isArray(docs) ? [docs] : docs;
  689. if (options.keepGoing === true) {
  690. options.ordered = false;
  691. }
  692. return this.insertMany(docs, options, callback);
  693. };
  694. define.classMethod('insert', { callback: true, promise: true });
  695. /**
  696. * @typedef {Object} Collection~updateWriteOpResult
  697. * @property {Object} result The raw result returned from MongoDB, field will vary depending on server version.
  698. * @property {Number} result.ok Is 1 if the command executed correctly.
  699. * @property {Number} result.n The total count of documents scanned.
  700. * @property {Number} result.nModified The total count of documents modified.
  701. * @property {Object} connection The connection object used for the operation.
  702. * @property {Number} matchedCount The number of documents that matched the filter.
  703. * @property {Number} modifiedCount The number of documents that were modified.
  704. * @property {Number} upsertedCount The number of documents upserted.
  705. * @property {Object} upsertedId The upserted id.
  706. * @property {ObjectId} upsertedId._id The upserted _id returned from the server.
  707. */
  708. /**
  709. * The callback format for inserts
  710. * @callback Collection~updateWriteOpCallback
  711. * @param {MongoError} error An error instance representing the error during the execution.
  712. * @param {Collection~updateWriteOpResult} result The result object if the command was executed successfully.
  713. */
  714. /**
  715. * Update a single document on MongoDB
  716. * @method
  717. * @param {object} filter The Filter used to select the document to update
  718. * @param {object} update The update operations to be applied to the document
  719. * @param {object} [options=null] Optional settings.
  720. * @param {boolean} [options.upsert=false] Update operation is an upsert.
  721. * @param {(number|string)} [options.w=null] The write concern.
  722. * @param {number} [options.wtimeout=null] The write concern timeout.
  723. * @param {boolean} [options.j=false] Specify a journal write concern.
  724. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  725. * @param {Array} [options.arrayFilters=null] optional list of array filters referenced in filtered positional operators
  726. * @param {ClientSession} [options.session] optional session to use for this operation
  727. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  728. * @return {Promise} returns Promise if no callback passed
  729. */
  730. Collection.prototype.updateOne = function(filter, update, options, callback) {
  731. if (typeof options === 'function') (callback = options), (options = {});
  732. options = options || {};
  733. var err = checkForAtomicOperators(update);
  734. if (err) {
  735. if (typeof callback === 'function') return callback(err);
  736. return this.s.promiseLibrary.reject(err);
  737. }
  738. options = shallowClone(options);
  739. // Add ignoreUndfined
  740. if (this.s.options.ignoreUndefined) {
  741. options = shallowClone(options);
  742. options.ignoreUndefined = this.s.options.ignoreUndefined;
  743. }
  744. return executeOperation(this.s.topology, updateOne, [this, filter, update, options, callback]);
  745. };
  746. var checkForAtomicOperators = function(update) {
  747. var keys = Object.keys(update);
  748. // same errors as the server would give for update doc lacking atomic operators
  749. if (keys.length === 0) {
  750. return toError('The update operation document must contain at least one atomic operator.');
  751. }
  752. if (keys[0][0] !== '$') {
  753. return toError('the update operation document must contain atomic operators.');
  754. }
  755. };
  756. var updateOne = function(self, filter, update, options, callback) {
  757. // Set single document update
  758. options.multi = false;
  759. // Execute update
  760. updateDocuments(self, filter, update, options, function(err, r) {
  761. if (callback == null) return;
  762. if (err && callback) return callback(err);
  763. if (r == null) return callback(null, { result: { ok: 1 } });
  764. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  765. r.upsertedId =
  766. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  767. ? r.result.upserted[0]
  768. : null;
  769. r.upsertedCount =
  770. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  771. r.matchedCount =
  772. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  773. if (callback) callback(null, r);
  774. });
  775. };
  776. define.classMethod('updateOne', { callback: true, promise: true });
  777. /**
  778. * Replace a document on MongoDB
  779. * @method
  780. * @param {object} filter The Filter used to select the document to update
  781. * @param {object} doc The Document that replaces the matching document
  782. * @param {object} [options=null] Optional settings.
  783. * @param {boolean} [options.upsert=false] Update operation is an upsert.
  784. * @param {(number|string)} [options.w=null] The write concern.
  785. * @param {number} [options.wtimeout=null] The write concern timeout.
  786. * @param {boolean} [options.j=false] Specify a journal write concern.
  787. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  788. * @param {ClientSession} [options.session] optional session to use for this operation
  789. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  790. * @return {Promise} returns Promise if no callback passed
  791. */
  792. Collection.prototype.replaceOne = function(filter, doc, options, callback) {
  793. if (typeof options === 'function') (callback = options), (options = {});
  794. options = shallowClone(options);
  795. // Add ignoreUndfined
  796. if (this.s.options.ignoreUndefined) {
  797. options = shallowClone(options);
  798. options.ignoreUndefined = this.s.options.ignoreUndefined;
  799. }
  800. return executeOperation(this.s.topology, replaceOne, [this, filter, doc, options, callback]);
  801. };
  802. var replaceOne = function(self, filter, doc, options, callback) {
  803. // Set single document update
  804. options.multi = false;
  805. // Execute update
  806. updateDocuments(self, filter, doc, options, function(err, r) {
  807. if (callback == null) return;
  808. if (err && callback) return callback(err);
  809. if (r == null) return callback(null, { result: { ok: 1 } });
  810. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  811. r.upsertedId =
  812. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  813. ? r.result.upserted[0]
  814. : null;
  815. r.upsertedCount =
  816. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  817. r.matchedCount =
  818. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  819. r.ops = [doc];
  820. if (callback) callback(null, r);
  821. });
  822. };
  823. define.classMethod('replaceOne', { callback: true, promise: true });
  824. /**
  825. * Update multiple documents on MongoDB
  826. * @method
  827. * @param {object} filter The Filter used to select the documents to update
  828. * @param {object} update The update operations to be applied to the document
  829. * @param {object} [options=null] Optional settings.
  830. * @param {boolean} [options.upsert=false] Update operation is an upsert.
  831. * @param {(number|string)} [options.w=null] The write concern.
  832. * @param {number} [options.wtimeout=null] The write concern timeout.
  833. * @param {boolean} [options.j=false] Specify a journal write concern.
  834. * @param {Array} [options.arrayFilters=null] optional list of array filters referenced in filtered positional operators
  835. * @param {ClientSession} [options.session] optional session to use for this operation
  836. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  837. * @return {Promise} returns Promise if no callback passed
  838. */
  839. Collection.prototype.updateMany = function(filter, update, options, callback) {
  840. if (typeof options === 'function') (callback = options), (options = {});
  841. options = options || {};
  842. var err = checkForAtomicOperators(update);
  843. if (err) {
  844. if (typeof callback === 'function') return callback(err);
  845. return this.s.promiseLibrary.reject(err);
  846. }
  847. options = shallowClone(options);
  848. // Add ignoreUndfined
  849. if (this.s.options.ignoreUndefined) {
  850. options = shallowClone(options);
  851. options.ignoreUndefined = this.s.options.ignoreUndefined;
  852. }
  853. return executeOperation(this.s.topology, updateMany, [this, filter, update, options, callback]);
  854. };
  855. var updateMany = function(self, filter, update, options, callback) {
  856. // Set single document update
  857. options.multi = true;
  858. // Execute update
  859. updateDocuments(self, filter, update, options, function(err, r) {
  860. if (callback == null) return;
  861. if (err && callback) return callback(err);
  862. if (r == null) return callback(null, { result: { ok: 1 } });
  863. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  864. r.upsertedId =
  865. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  866. ? r.result.upserted[0]
  867. : null;
  868. r.upsertedCount =
  869. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  870. r.matchedCount =
  871. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  872. if (callback) callback(null, r);
  873. });
  874. };
  875. define.classMethod('updateMany', { callback: true, promise: true });
  876. var updateDocuments = function(self, selector, document, options, callback) {
  877. if ('function' === typeof options) (callback = options), (options = null);
  878. if (options == null) options = {};
  879. if (!('function' === typeof callback)) callback = null;
  880. // If we are not providing a selector or document throw
  881. if (selector == null || typeof selector !== 'object')
  882. return callback(toError('selector must be a valid JavaScript object'));
  883. if (document == null || typeof document !== 'object')
  884. return callback(toError('document must be a valid JavaScript object'));
  885. // Get the write concern options
  886. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  887. // Do we return the actual result document
  888. // Either use override on the function, or go back to default on either the collection
  889. // level or db
  890. finalOptions['serializeFunctions'] = options['serializeFunctions'] || self.s.serializeFunctions;
  891. // Execute the operation
  892. var op = { q: selector, u: document };
  893. op.upsert = options.upsert !== void 0 ? !!options.upsert : false;
  894. op.multi = options.multi !== void 0 ? !!options.multi : false;
  895. if (finalOptions.arrayFilters) {
  896. op.arrayFilters = finalOptions.arrayFilters;
  897. delete finalOptions.arrayFilters;
  898. }
  899. // Have we specified collation
  900. decorateWithCollation(finalOptions, self, options);
  901. // Update options
  902. self.s.topology.update(self.s.namespace, [op], finalOptions, function(err, result) {
  903. if (callback == null) return;
  904. if (err) return handleCallback(callback, err, null);
  905. if (result == null) return handleCallback(callback, null, null);
  906. if (result.result.code) return handleCallback(callback, toError(result.result));
  907. if (result.result.writeErrors)
  908. return handleCallback(callback, toError(result.result.writeErrors[0]));
  909. // Return the results
  910. handleCallback(callback, null, result);
  911. });
  912. };
  913. /**
  914. * Updates documents.
  915. * @method
  916. * @param {object} selector The selector for the update operation.
  917. * @param {object} document The update document.
  918. * @param {object} [options=null] Optional settings.
  919. * @param {(number|string)} [options.w=null] The write concern.
  920. * @param {number} [options.wtimeout=null] The write concern timeout.
  921. * @param {boolean} [options.j=false] Specify a journal write concern.
  922. * @param {boolean} [options.upsert=false] Update operation is an upsert.
  923. * @param {boolean} [options.multi=false] Update one/all documents with operation.
  924. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  925. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  926. * @param {Array} [options.arrayFilters=null] optional list of array filters referenced in filtered positional operators
  927. * @param {ClientSession} [options.session] optional session to use for this operation
  928. * @param {Collection~writeOpCallback} [callback] The command result callback
  929. * @throws {MongoError}
  930. * @return {Promise} returns Promise if no callback passed
  931. * @deprecated use updateOne, updateMany or bulkWrite
  932. */
  933. Collection.prototype.update = function(selector, document, options, callback) {
  934. if (typeof options === 'function') (callback = options), (options = {});
  935. options = options || {};
  936. // Add ignoreUndfined
  937. if (this.s.options.ignoreUndefined) {
  938. options = shallowClone(options);
  939. options.ignoreUndefined = this.s.options.ignoreUndefined;
  940. }
  941. return executeOperation(this.s.topology, updateDocuments, [
  942. this,
  943. selector,
  944. document,
  945. options,
  946. callback
  947. ]);
  948. };
  949. define.classMethod('update', { callback: true, promise: true });
  950. /**
  951. * @typedef {Object} Collection~deleteWriteOpResult
  952. * @property {Object} result The raw result returned from MongoDB, field will vary depending on server version.
  953. * @property {Number} result.ok Is 1 if the command executed correctly.
  954. * @property {Number} result.n The total count of documents deleted.
  955. * @property {Object} connection The connection object used for the operation.
  956. * @property {Number} deletedCount The number of documents deleted.
  957. */
  958. /**
  959. * The callback format for inserts
  960. * @callback Collection~deleteWriteOpCallback
  961. * @param {MongoError} error An error instance representing the error during the execution.
  962. * @param {Collection~deleteWriteOpResult} result The result object if the command was executed successfully.
  963. */
  964. /**
  965. * Delete a document on MongoDB
  966. * @method
  967. * @param {object} filter The Filter used to select the document to remove
  968. * @param {object} [options=null] Optional settings.
  969. * @param {(number|string)} [options.w=null] The write concern.
  970. * @param {number} [options.wtimeout=null] The write concern timeout.
  971. * @param {boolean} [options.j=false] Specify a journal write concern.
  972. * @param {ClientSession} [options.session] optional session to use for this operation
  973. * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
  974. * @return {Promise} returns Promise if no callback passed
  975. */
  976. Collection.prototype.deleteOne = function(filter, options, callback) {
  977. if (typeof options === 'function') (callback = options), (options = {});
  978. options = shallowClone(options);
  979. // Add ignoreUndfined
  980. if (this.s.options.ignoreUndefined) {
  981. options = shallowClone(options);
  982. options.ignoreUndefined = this.s.options.ignoreUndefined;
  983. }
  984. return executeOperation(this.s.topology, deleteOne, [this, filter, options, callback]);
  985. };
  986. var deleteOne = function(self, filter, options, callback) {
  987. options.single = true;
  988. removeDocuments(self, filter, options, function(err, r) {
  989. if (callback == null) return;
  990. if (err && callback) return callback(err);
  991. if (r == null) return callback(null, { result: { ok: 1 } });
  992. r.deletedCount = r.result.n;
  993. if (callback) callback(null, r);
  994. });
  995. };
  996. define.classMethod('deleteOne', { callback: true, promise: true });
  997. Collection.prototype.removeOne = Collection.prototype.deleteOne;
  998. define.classMethod('removeOne', { callback: true, promise: true });
  999. /**
  1000. * Delete multiple documents on MongoDB
  1001. * @method
  1002. * @param {object} filter The Filter used to select the documents to remove
  1003. * @param {object} [options=null] Optional settings.
  1004. * @param {(number|string)} [options.w=null] The write concern.
  1005. * @param {number} [options.wtimeout=null] The write concern timeout.
  1006. * @param {boolean} [options.j=false] Specify a journal write concern.
  1007. * @param {ClientSession} [options.session] optional session to use for this operation
  1008. * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
  1009. * @return {Promise} returns Promise if no callback passed
  1010. */
  1011. Collection.prototype.deleteMany = function(filter, options, callback) {
  1012. if (typeof options === 'function') (callback = options), (options = {});
  1013. options = shallowClone(options);
  1014. // Add ignoreUndfined
  1015. if (this.s.options.ignoreUndefined) {
  1016. options = shallowClone(options);
  1017. options.ignoreUndefined = this.s.options.ignoreUndefined;
  1018. }
  1019. return executeOperation(this.s.topology, deleteMany, [this, filter, options, callback]);
  1020. };
  1021. var deleteMany = function(self, filter, options, callback) {
  1022. options.single = false;
  1023. removeDocuments(self, filter, options, function(err, r) {
  1024. if (callback == null) return;
  1025. if (err && callback) return callback(err);
  1026. if (r == null) return callback(null, { result: { ok: 1 } });
  1027. r.deletedCount = r.result.n;
  1028. if (callback) callback(null, r);
  1029. });
  1030. };
  1031. var removeDocuments = function(self, selector, options, callback) {
  1032. if (typeof options === 'function') {
  1033. (callback = options), (options = {});
  1034. } else if (typeof selector === 'function') {
  1035. callback = selector;
  1036. options = {};
  1037. selector = {};
  1038. }
  1039. // Create an empty options object if the provided one is null
  1040. options = options || {};
  1041. // Get the write concern options
  1042. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  1043. // If selector is null set empty
  1044. if (selector == null) selector = {};
  1045. // Build the op
  1046. var op = { q: selector, limit: 0 };
  1047. if (options.single) op.limit = 1;
  1048. // Have we specified collation
  1049. decorateWithCollation(finalOptions, self, options);
  1050. // Execute the remove
  1051. self.s.topology.remove(self.s.namespace, [op], finalOptions, function(err, result) {
  1052. if (callback == null) return;
  1053. if (err) return handleCallback(callback, err, null);
  1054. if (result == null) return handleCallback(callback, null, null);
  1055. if (result.result.code) return handleCallback(callback, toError(result.result));
  1056. if (result.result.writeErrors)
  1057. return handleCallback(callback, toError(result.result.writeErrors[0]));
  1058. // Return the results
  1059. handleCallback(callback, null, result);
  1060. });
  1061. };
  1062. define.classMethod('deleteMany', { callback: true, promise: true });
  1063. Collection.prototype.removeMany = Collection.prototype.deleteMany;
  1064. define.classMethod('removeMany', { callback: true, promise: true });
  1065. /**
  1066. * Remove documents.
  1067. * @method
  1068. * @param {object} selector The selector for the update operation.
  1069. * @param {object} [options=null] Optional settings.
  1070. * @param {(number|string)} [options.w=null] The write concern.
  1071. * @param {number} [options.wtimeout=null] The write concern timeout.
  1072. * @param {boolean} [options.j=false] Specify a journal write concern.
  1073. * @param {boolean} [options.single=false] Removes the first document found.
  1074. * @param {ClientSession} [options.session] optional session to use for this operation
  1075. * @param {Collection~writeOpCallback} [callback] The command result callback
  1076. * @return {Promise} returns Promise if no callback passed
  1077. * @deprecated use deleteOne, deleteMany or bulkWrite
  1078. */
  1079. Collection.prototype.remove = function(selector, options, callback) {
  1080. if (typeof options === 'function') (callback = options), (options = {});
  1081. options = options || {};
  1082. // Add ignoreUndfined
  1083. if (this.s.options.ignoreUndefined) {
  1084. options = shallowClone(options);
  1085. options.ignoreUndefined = this.s.options.ignoreUndefined;
  1086. }
  1087. return executeOperation(this.s.topology, removeDocuments, [this, selector, options, callback]);
  1088. };
  1089. define.classMethod('remove', { callback: true, promise: true });
  1090. /**
  1091. * Save a document. Simple full document replacement function. Not recommended for efficiency, use atomic
  1092. * operators and update instead for more efficient operations.
  1093. * @method
  1094. * @param {object} doc Document to save
  1095. * @param {object} [options=null] Optional settings.
  1096. * @param {(number|string)} [options.w=null] The write concern.
  1097. * @param {number} [options.wtimeout=null] The write concern timeout.
  1098. * @param {boolean} [options.j=false] Specify a journal write concern.
  1099. * @param {ClientSession} [options.session] optional session to use for this operation
  1100. * @param {Collection~writeOpCallback} [callback] The command result callback
  1101. * @return {Promise} returns Promise if no callback passed
  1102. * @deprecated use insertOne, insertMany, updateOne or updateMany
  1103. */
  1104. Collection.prototype.save = function(doc, options, callback) {
  1105. if (typeof options === 'function') (callback = options), (options = {});
  1106. options = options || {};
  1107. // Add ignoreUndfined
  1108. if (this.s.options.ignoreUndefined) {
  1109. options = shallowClone(options);
  1110. options.ignoreUndefined = this.s.options.ignoreUndefined;
  1111. }
  1112. return executeOperation(this.s.topology, save, [this, doc, options, callback]);
  1113. };
  1114. var save = function(self, doc, options, callback) {
  1115. // Get the write concern options
  1116. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  1117. // Establish if we need to perform an insert or update
  1118. if (doc._id != null) {
  1119. finalOptions.upsert = true;
  1120. return updateDocuments(self, { _id: doc._id }, doc, finalOptions, callback);
  1121. }
  1122. // Insert the document
  1123. insertDocuments(self, [doc], finalOptions, function(err, r) {
  1124. if (callback == null) return;
  1125. if (doc == null) return handleCallback(callback, null, null);
  1126. if (err) return handleCallback(callback, err, null);
  1127. handleCallback(callback, null, r);
  1128. });
  1129. };
  1130. define.classMethod('save', { callback: true, promise: true });
  1131. /**
  1132. * The callback format for results
  1133. * @callback Collection~resultCallback
  1134. * @param {MongoError} error An error instance representing the error during the execution.
  1135. * @param {object} result The result object if the command was executed successfully.
  1136. */
  1137. /**
  1138. * The callback format for an aggregation call
  1139. * @callback Collection~aggregationCallback
  1140. * @param {MongoError} error An error instance representing the error during the execution.
  1141. * @param {AggregationCursor} cursor The cursor if the aggregation command was executed successfully.
  1142. */
  1143. /**
  1144. * Fetches the first document that matches the query
  1145. * @method
  1146. * @param {object} query Query for find Operation
  1147. * @param {object} [options=null] Optional settings.
  1148. * @param {number} [options.limit=0] Sets the limit of documents returned in the query.
  1149. * @param {(array|object)} [options.sort=null] Set to sort the documents coming back from the query. Array of indexes, [['a', 1]] etc.
  1150. * @param {object} [options.projection=null] The fields to return in the query. Object of fields to include or exclude (not both), {'a':1}
  1151. * @param {object} [options.fields=null] **Deprecated** Use `options.projection` instead
  1152. * @param {number} [options.skip=0] Set to skip N documents ahead in your query (useful for pagination).
  1153. * @param {Object} [options.hint=null] Tell the query to use specific indexes in the query. Object of indexes to use, {'_id':1}
  1154. * @param {boolean} [options.explain=false] Explain the query instead of returning the data.
  1155. * @param {boolean} [options.snapshot=false] Snapshot query.
  1156. * @param {boolean} [options.timeout=false] Specify if the cursor can timeout.
  1157. * @param {boolean} [options.tailable=false] Specify if the cursor is tailable.
  1158. * @param {number} [options.batchSize=0] Set the batchSize for the getMoreCommand when iterating over the query results.
  1159. * @param {boolean} [options.returnKey=false] Only return the index key.
  1160. * @param {number} [options.maxScan=null] Limit the number of items to scan.
  1161. * @param {number} [options.min=null] Set index bounds.
  1162. * @param {number} [options.max=null] Set index bounds.
  1163. * @param {boolean} [options.showDiskLoc=false] Show disk location of results.
  1164. * @param {string} [options.comment=null] You can put a $comment field on a query to make looking in the profiler logs simpler.
  1165. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  1166. * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
  1167. * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
  1168. * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
  1169. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  1170. * @param {boolean} [options.partial=false] Specify if the cursor should return partial results when querying against a sharded system
  1171. * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
  1172. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  1173. * @param {ClientSession} [options.session] optional session to use for this operation
  1174. * @param {Collection~resultCallback} [callback] The command result callback
  1175. * @return {Promise} returns Promise if no callback passed
  1176. */
  1177. Collection.prototype.findOne = function(query, options, callback) {
  1178. if (typeof query === 'function') (callback = query), (query = {}), (options = {});
  1179. if (typeof options === 'function') (callback = options), (options = {});
  1180. query = query || {};
  1181. options = options || {};
  1182. return executeOperation(this.s.topology, findOne, [this, query, options, callback]);
  1183. };
  1184. var findOne = function(self, query, options, callback) {
  1185. const cursor = self
  1186. .find(query, options)
  1187. .limit(-1)
  1188. .batchSize(1);
  1189. // Return the item
  1190. cursor.next(function(err, item) {
  1191. if (err != null) return handleCallback(callback, toError(err), null);
  1192. handleCallback(callback, null, item);
  1193. });
  1194. };
  1195. define.classMethod('findOne', { callback: true, promise: true });
  1196. /**
  1197. * The callback format for the collection method, must be used if strict is specified
  1198. * @callback Collection~collectionResultCallback
  1199. * @param {MongoError} error An error instance representing the error during the execution.
  1200. * @param {Collection} collection The collection instance.
  1201. */
  1202. /**
  1203. * Rename the collection.
  1204. *
  1205. * @method
  1206. * @param {string} newName New name of of the collection.
  1207. * @param {object} [options=null] Optional settings.
  1208. * @param {boolean} [options.dropTarget=false] Drop the target name collection if it previously exists.
  1209. * @param {ClientSession} [options.session] optional session to use for this operation
  1210. * @param {Collection~collectionResultCallback} [callback] The results callback
  1211. * @return {Promise} returns Promise if no callback passed
  1212. */
  1213. Collection.prototype.rename = function(newName, options, callback) {
  1214. if (typeof options === 'function') (callback = options), (options = {});
  1215. options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
  1216. return executeOperation(this.s.topology, rename, [this, newName, options, callback]);
  1217. };
  1218. var rename = function(self, newName, options, callback) {
  1219. // Check the collection name
  1220. checkCollectionName(newName);
  1221. // Build the command
  1222. var renameCollection = f('%s.%s', self.s.dbName, self.s.name);
  1223. var toCollection = f('%s.%s', self.s.dbName, newName);
  1224. var dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
  1225. var cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
  1226. // Decorate command with writeConcern if supported
  1227. decorateWithWriteConcern(cmd, self, options);
  1228. // Execute against admin
  1229. self.s.db.admin().command(cmd, options, function(err, doc) {
  1230. if (err) return handleCallback(callback, err, null);
  1231. // We have an error
  1232. if (doc.errmsg) return handleCallback(callback, toError(doc), null);
  1233. try {
  1234. return handleCallback(
  1235. callback,
  1236. null,
  1237. new Collection(
  1238. self.s.db,
  1239. self.s.topology,
  1240. self.s.dbName,
  1241. newName,
  1242. self.s.pkFactory,
  1243. self.s.options
  1244. )
  1245. );
  1246. } catch (err) {
  1247. return handleCallback(callback, toError(err), null);
  1248. }
  1249. });
  1250. };
  1251. define.classMethod('rename', { callback: true, promise: true });
  1252. /**
  1253. * Drop the collection from the database, removing it permanently. New accesses will create a new collection.
  1254. *
  1255. * @method
  1256. * @param {object} [options=null] Optional settings.
  1257. * @param {ClientSession} [options.session] optional session to use for this operation
  1258. * @param {Collection~resultCallback} [callback] The results callback
  1259. * @return {Promise} returns Promise if no callback passed
  1260. */
  1261. Collection.prototype.drop = function(options, callback) {
  1262. if (typeof options === 'function') (callback = options), (options = {});
  1263. options = options || {};
  1264. return executeOperation(this.s.topology, this.s.db.dropCollection.bind(this.s.db), [
  1265. this.s.name,
  1266. options,
  1267. callback
  1268. ]);
  1269. };
  1270. define.classMethod('drop', { callback: true, promise: true });
  1271. /**
  1272. * Returns the options of the collection.
  1273. *
  1274. * @method
  1275. * @param {Object} [options] Optional settings
  1276. * @param {ClientSession} [options.session] optional session to use for this operation
  1277. * @param {Collection~resultCallback} [callback] The results callback
  1278. * @return {Promise} returns Promise if no callback passed
  1279. */
  1280. Collection.prototype.options = function(opts, callback) {
  1281. if (typeof opts === 'function') (callback = opts), (opts = {});
  1282. opts = opts || {};
  1283. return executeOperation(this.s.topology, options, [this, opts, callback]);
  1284. };
  1285. var options = function(self, opts, callback) {
  1286. self.s.db.listCollections({ name: self.s.name }, opts).toArray(function(err, collections) {
  1287. if (err) return handleCallback(callback, err);
  1288. if (collections.length === 0) {
  1289. return handleCallback(
  1290. callback,
  1291. MongoError.create({ message: f('collection %s not found', self.s.namespace), driver: true })
  1292. );
  1293. }
  1294. handleCallback(callback, err, collections[0].options || null);
  1295. });
  1296. };
  1297. define.classMethod('options', { callback: true, promise: true });
  1298. /**
  1299. * Returns if the collection is a capped collection
  1300. *
  1301. * @method
  1302. * @param {Object} [options] Optional settings
  1303. * @param {ClientSession} [options.session] optional session to use for this operation
  1304. * @param {Collection~resultCallback} [callback] The results callback
  1305. * @return {Promise} returns Promise if no callback passed
  1306. */
  1307. Collection.prototype.isCapped = function(options, callback) {
  1308. if (typeof options === 'function') (callback = options), (options = {});
  1309. options = options || {};
  1310. return executeOperation(this.s.topology, isCapped, [this, options, callback]);
  1311. };
  1312. var isCapped = function(self, options, callback) {
  1313. self.options(options, function(err, document) {
  1314. if (err) return handleCallback(callback, err);
  1315. handleCallback(callback, null, document && document.capped);
  1316. });
  1317. };
  1318. define.classMethod('isCapped', { callback: true, promise: true });
  1319. /**
  1320. * Creates an index on the db and collection collection.
  1321. * @method
  1322. * @param {(string|object)} fieldOrSpec Defines the index.
  1323. * @param {object} [options=null] Optional settings.
  1324. * @param {(number|string)} [options.w=null] The write concern.
  1325. * @param {number} [options.wtimeout=null] The write concern timeout.
  1326. * @param {boolean} [options.j=false] Specify a journal write concern.
  1327. * @param {boolean} [options.unique=false] Creates an unique index.
  1328. * @param {boolean} [options.sparse=false] Creates a sparse index.
  1329. * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
  1330. * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
  1331. * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
  1332. * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
  1333. * @param {number} [options.v=null] Specify the format version of the indexes.
  1334. * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
  1335. * @param {string} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
  1336. * @param {object} [options.partialFilterExpression=null] Creates a partial index based on the given filter object (MongoDB 3.2 or higher)
  1337. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  1338. * @param {ClientSession} [options.session] optional session to use for this operation
  1339. * @param {Collection~resultCallback} [callback] The command result callback
  1340. * @return {Promise} returns Promise if no callback passed
  1341. */
  1342. Collection.prototype.createIndex = function(fieldOrSpec, options, callback) {
  1343. if (typeof options === 'function') (callback = options), (options = {});
  1344. options = options || {};
  1345. return executeOperation(this.s.topology, createIndex, [this, fieldOrSpec, options, callback]);
  1346. };
  1347. var createIndex = function(self, fieldOrSpec, options, callback) {
  1348. self.s.db.createIndex(self.s.name, fieldOrSpec, options, callback);
  1349. };
  1350. define.classMethod('createIndex', { callback: true, promise: true });
  1351. /**
  1352. * Creates multiple indexes in the collection, this method is only supported for
  1353. * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
  1354. * error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
  1355. * @method
  1356. * @param {array} indexSpecs An array of index specifications to be created
  1357. * @param {Object} [options] Optional settings
  1358. * @param {ClientSession} [options.session] optional session to use for this operation
  1359. * @param {Collection~resultCallback} [callback] The command result callback
  1360. * @return {Promise} returns Promise if no callback passed
  1361. */
  1362. Collection.prototype.createIndexes = function(indexSpecs, options, callback) {
  1363. if (typeof options === 'function') (callback = options), (options = {});
  1364. options = options ? shallowClone(options) : {};
  1365. if (typeof options.maxTimeMS !== 'number') delete options.maxTimeMS;
  1366. return executeOperation(this.s.topology, createIndexes, [this, indexSpecs, options, callback]);
  1367. };
  1368. var createIndexes = function(self, indexSpecs, options, callback) {
  1369. var capabilities = self.s.topology.capabilities();
  1370. // Ensure we generate the correct name if the parameter is not set
  1371. for (var i = 0; i < indexSpecs.length; i++) {
  1372. if (indexSpecs[i].name == null) {
  1373. var keys = [];
  1374. // Did the user pass in a collation, check if our write server supports it
  1375. if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
  1376. return callback(new MongoError(f('server/primary/mongos does not support collation')));
  1377. }
  1378. for (var name in indexSpecs[i].key) {
  1379. keys.push(f('%s_%s', name, indexSpecs[i].key[name]));
  1380. }
  1381. // Set the name
  1382. indexSpecs[i].name = keys.join('_');
  1383. }
  1384. }
  1385. options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
  1386. // Execute the index
  1387. self.s.db.command(
  1388. {
  1389. createIndexes: self.s.name,
  1390. indexes: indexSpecs
  1391. },
  1392. options,
  1393. callback
  1394. );
  1395. };
  1396. define.classMethod('createIndexes', { callback: true, promise: true });
  1397. /**
  1398. * Drops an index from this collection.
  1399. * @method
  1400. * @param {string} indexName Name of the index to drop.
  1401. * @param {object} [options=null] Optional settings.
  1402. * @param {(number|string)} [options.w=null] The write concern.
  1403. * @param {number} [options.wtimeout=null] The write concern timeout.
  1404. * @param {boolean} [options.j=false] Specify a journal write concern.
  1405. * @param {ClientSession} [options.session] optional session to use for this operation
  1406. * @param {number} [options.maxTimeMS] Number of miliseconds to wait before aborting the query.
  1407. * @param {Collection~resultCallback} [callback] The command result callback
  1408. * @return {Promise} returns Promise if no callback passed
  1409. */
  1410. Collection.prototype.dropIndex = function(indexName, options, callback) {
  1411. var args = Array.prototype.slice.call(arguments, 1);
  1412. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1413. options = args.length ? args.shift() || {} : {};
  1414. // Run only against primary
  1415. options.readPreference = ReadPreference.PRIMARY;
  1416. return executeOperation(this.s.topology, dropIndex, [this, indexName, options, callback]);
  1417. };
  1418. var dropIndex = function(self, indexName, options, callback) {
  1419. // Delete index command
  1420. var cmd = { dropIndexes: self.s.name, index: indexName };
  1421. // Decorate command with writeConcern if supported
  1422. decorateWithWriteConcern(cmd, self, options);
  1423. // Execute command
  1424. self.s.db.command(cmd, options, function(err, result) {
  1425. if (typeof callback !== 'function') return;
  1426. if (err) return handleCallback(callback, err, null);
  1427. handleCallback(callback, null, result);
  1428. });
  1429. };
  1430. define.classMethod('dropIndex', { callback: true, promise: true });
  1431. /**
  1432. * Drops all indexes from this collection.
  1433. * @method
  1434. * @param {Object} [options] Optional settings
  1435. * @param {ClientSession} [options.session] optional session to use for this operation
  1436. * @param {number} [options.maxTimeMS] Number of miliseconds to wait before aborting the query.
  1437. * @param {Collection~resultCallback} [callback] The command result callback
  1438. * @return {Promise} returns Promise if no callback passed
  1439. */
  1440. Collection.prototype.dropIndexes = function(options, callback) {
  1441. if (typeof options === 'function') (callback = options), (options = {});
  1442. options = options ? shallowClone(options) : {};
  1443. if (typeof options.maxTimeMS !== 'number') delete options.maxTimeMS;
  1444. return executeOperation(this.s.topology, dropIndexes, [this, options, callback]);
  1445. };
  1446. var dropIndexes = function(self, options, callback) {
  1447. self.dropIndex('*', options, function(err) {
  1448. if (err) return handleCallback(callback, err, false);
  1449. handleCallback(callback, null, true);
  1450. });
  1451. };
  1452. define.classMethod('dropIndexes', { callback: true, promise: true });
  1453. /**
  1454. * Drops all indexes from this collection.
  1455. * @method
  1456. * @deprecated use dropIndexes
  1457. * @param {Collection~resultCallback} callback The command result callback
  1458. * @return {Promise} returns Promise if no [callback] passed
  1459. */
  1460. Collection.prototype.dropAllIndexes = Collection.prototype.dropIndexes;
  1461. define.classMethod('dropAllIndexes', { callback: true, promise: true });
  1462. /**
  1463. * Reindex all indexes on the collection
  1464. * Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
  1465. * @method
  1466. * @param {Object} [options] Optional settings
  1467. * @param {ClientSession} [options.session] optional session to use for this operation
  1468. * @param {Collection~resultCallback} [callback] The command result callback
  1469. * @return {Promise} returns Promise if no callback passed
  1470. */
  1471. Collection.prototype.reIndex = function(options, callback) {
  1472. if (typeof options === 'function') (callback = options), (options = {});
  1473. options = options || {};
  1474. return executeOperation(this.s.topology, reIndex, [this, options, callback]);
  1475. };
  1476. var reIndex = function(self, options, callback) {
  1477. // Reindex
  1478. var cmd = { reIndex: self.s.name };
  1479. // Execute the command
  1480. self.s.db.command(cmd, options, function(err, result) {
  1481. if (callback == null) return;
  1482. if (err) return handleCallback(callback, err, null);
  1483. handleCallback(callback, null, result.ok ? true : false);
  1484. });
  1485. };
  1486. define.classMethod('reIndex', { callback: true, promise: true });
  1487. /**
  1488. * Get the list of all indexes information for the collection.
  1489. *
  1490. * @method
  1491. * @param {object} [options=null] Optional settings.
  1492. * @param {number} [options.batchSize=null] The batchSize for the returned command cursor or if pre 2.8 the systems batch collection
  1493. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  1494. * @param {ClientSession} [options.session] optional session to use for this operation
  1495. * @return {CommandCursor}
  1496. */
  1497. Collection.prototype.listIndexes = function(options) {
  1498. options = options || {};
  1499. // Clone the options
  1500. options = shallowClone(options);
  1501. // Determine the read preference in the options.
  1502. options = getReadPreference(this, options, this.s.db, this);
  1503. // Set the CommandCursor constructor
  1504. options.cursorFactory = CommandCursor;
  1505. // Set the promiseLibrary
  1506. options.promiseLibrary = this.s.promiseLibrary;
  1507. if (!this.s.topology.capabilities()) {
  1508. throw new MongoError('cannot connect to server');
  1509. }
  1510. // We have a list collections command
  1511. if (this.s.topology.capabilities().hasListIndexesCommand) {
  1512. // Cursor options
  1513. var cursor = options.batchSize ? { batchSize: options.batchSize } : {};
  1514. // Build the command
  1515. var command = { listIndexes: this.s.name, cursor: cursor };
  1516. // Execute the cursor
  1517. cursor = this.s.topology.cursor(f('%s.$cmd', this.s.dbName), command, options);
  1518. // Do we have a readPreference, apply it
  1519. if (options.readPreference) cursor.setReadPreference(options.readPreference);
  1520. // Return the cursor
  1521. return cursor;
  1522. }
  1523. // Get the namespace
  1524. var ns = f('%s.system.indexes', this.s.dbName);
  1525. // Get the query
  1526. cursor = this.s.topology.cursor(ns, { find: ns, query: { ns: this.s.namespace } }, options);
  1527. // Do we have a readPreference, apply it
  1528. if (options.readPreference) cursor.setReadPreference(options.readPreference);
  1529. // Set the passed in batch size if one was provided
  1530. if (options.batchSize) cursor = cursor.batchSize(options.batchSize);
  1531. // Return the cursor
  1532. return cursor;
  1533. };
  1534. define.classMethod('listIndexes', { callback: false, promise: false, returns: [CommandCursor] });
  1535. /**
  1536. * Ensures that an index exists, if it does not it creates it
  1537. * @method
  1538. * @deprecated use createIndexes instead
  1539. * @param {(string|object)} fieldOrSpec Defines the index.
  1540. * @param {object} [options=null] Optional settings.
  1541. * @param {(number|string)} [options.w=null] The write concern.
  1542. * @param {number} [options.wtimeout=null] The write concern timeout.
  1543. * @param {boolean} [options.j=false] Specify a journal write concern.
  1544. * @param {boolean} [options.unique=false] Creates an unique index.
  1545. * @param {boolean} [options.sparse=false] Creates a sparse index.
  1546. * @param {boolean} [options.background=false] Creates the index in the background, yielding whenever possible.
  1547. * @param {boolean} [options.dropDups=false] A unique index cannot be created on a key that has pre-existing duplicate values. If you would like to create the index anyway, keeping the first document the database indexes and deleting all subsequent documents that have duplicate value
  1548. * @param {number} [options.min=null] For geospatial indexes set the lower bound for the co-ordinates.
  1549. * @param {number} [options.max=null] For geospatial indexes set the high bound for the co-ordinates.
  1550. * @param {number} [options.v=null] Specify the format version of the indexes.
  1551. * @param {number} [options.expireAfterSeconds=null] Allows you to expire data on indexes applied to a data (MongoDB 2.2 or higher)
  1552. * @param {number} [options.name=null] Override the autogenerated index name (useful if the resulting name is larger than 128 bytes)
  1553. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  1554. * @param {ClientSession} [options.session] optional session to use for this operation
  1555. * @param {Collection~resultCallback} [callback] The command result callback
  1556. * @return {Promise} returns Promise if no callback passed
  1557. */
  1558. Collection.prototype.ensureIndex = function(fieldOrSpec, options, callback) {
  1559. if (typeof options === 'function') (callback = options), (options = {});
  1560. options = options || {};
  1561. return executeOperation(this.s.topology, ensureIndex, [this, fieldOrSpec, options, callback]);
  1562. };
  1563. var ensureIndex = function(self, fieldOrSpec, options, callback) {
  1564. self.s.db.ensureIndex(self.s.name, fieldOrSpec, options, callback);
  1565. };
  1566. define.classMethod('ensureIndex', { callback: true, promise: true });
  1567. /**
  1568. * Checks if one or more indexes exist on the collection, fails on first non-existing index
  1569. * @method
  1570. * @param {(string|array)} indexes One or more index names to check.
  1571. * @param {Object} [options] Optional settings
  1572. * @param {ClientSession} [options.session] optional session to use for this operation
  1573. * @param {Collection~resultCallback} [callback] The command result callback
  1574. * @return {Promise} returns Promise if no callback passed
  1575. */
  1576. Collection.prototype.indexExists = function(indexes, options, callback) {
  1577. if (typeof options === 'function') (callback = options), (options = {});
  1578. options = options || {};
  1579. return executeOperation(this.s.topology, indexExists, [this, indexes, options, callback]);
  1580. };
  1581. var indexExists = function(self, indexes, options, callback) {
  1582. self.indexInformation(options, function(err, indexInformation) {
  1583. // If we have an error return
  1584. if (err != null) return handleCallback(callback, err, null);
  1585. // Let's check for the index names
  1586. if (!Array.isArray(indexes))
  1587. return handleCallback(callback, null, indexInformation[indexes] != null);
  1588. // Check in list of indexes
  1589. for (var i = 0; i < indexes.length; i++) {
  1590. if (indexInformation[indexes[i]] == null) {
  1591. return handleCallback(callback, null, false);
  1592. }
  1593. }
  1594. // All keys found return true
  1595. return handleCallback(callback, null, true);
  1596. });
  1597. };
  1598. define.classMethod('indexExists', { callback: true, promise: true });
  1599. /**
  1600. * Retrieves this collections index info.
  1601. * @method
  1602. * @param {object} [options=null] Optional settings.
  1603. * @param {boolean} [options.full=false] Returns the full raw index information.
  1604. * @param {ClientSession} [options.session] optional session to use for this operation
  1605. * @param {Collection~resultCallback} [callback] The command result callback
  1606. * @return {Promise} returns Promise if no callback passed
  1607. */
  1608. Collection.prototype.indexInformation = function(options, callback) {
  1609. var args = Array.prototype.slice.call(arguments, 0);
  1610. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1611. options = args.length ? args.shift() || {} : {};
  1612. return executeOperation(this.s.topology, indexInformation, [this, options, callback]);
  1613. };
  1614. var indexInformation = function(self, options, callback) {
  1615. self.s.db.indexInformation(self.s.name, options, callback);
  1616. };
  1617. define.classMethod('indexInformation', { callback: true, promise: true });
  1618. /**
  1619. * The callback format for results
  1620. * @callback Collection~countCallback
  1621. * @param {MongoError} error An error instance representing the error during the execution.
  1622. * @param {number} result The count of documents that matched the query.
  1623. */
  1624. /**
  1625. * Count number of matching documents in the db to a query.
  1626. * @method
  1627. * @param {object} query The query for the count.
  1628. * @param {object} [options=null] Optional settings.
  1629. * @param {boolean} [options.limit=null] The limit of documents to count.
  1630. * @param {boolean} [options.skip=null] The number of documents to skip for the count.
  1631. * @param {string} [options.hint=null] An index name hint for the query.
  1632. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  1633. * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
  1634. * @param {ClientSession} [options.session] optional session to use for this operation
  1635. * @param {Collection~countCallback} [callback] The command result callback
  1636. * @return {Promise} returns Promise if no callback passed
  1637. */
  1638. Collection.prototype.count = function(query, options, callback) {
  1639. var args = Array.prototype.slice.call(arguments, 0);
  1640. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1641. query = args.length ? args.shift() || {} : {};
  1642. options = args.length ? args.shift() || {} : {};
  1643. return executeOperation(this.s.topology, count, [this, query, options, callback]);
  1644. };
  1645. var count = function(self, query, options, callback) {
  1646. var skip = options.skip;
  1647. var limit = options.limit;
  1648. var hint = options.hint;
  1649. var maxTimeMS = options.maxTimeMS;
  1650. // Final query
  1651. var cmd = {
  1652. count: self.s.name,
  1653. query: query
  1654. };
  1655. // Add limit, skip and maxTimeMS if defined
  1656. if (typeof skip === 'number') cmd.skip = skip;
  1657. if (typeof limit === 'number') cmd.limit = limit;
  1658. if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
  1659. if (hint) cmd.hint = hint;
  1660. options = shallowClone(options);
  1661. // Ensure we have the right read preference inheritance
  1662. options = getReadPreference(self, options, self.s.db);
  1663. // Do we have a readConcern specified
  1664. decorateWithReadConcern(cmd, self, options);
  1665. // Have we specified collation
  1666. decorateWithCollation(cmd, self, options);
  1667. // Execute command
  1668. self.s.db.command(cmd, options, function(err, result) {
  1669. if (err) return handleCallback(callback, err);
  1670. handleCallback(callback, null, result.n);
  1671. });
  1672. };
  1673. define.classMethod('count', { callback: true, promise: true });
  1674. /**
  1675. * The distinct command returns returns a list of distinct values for the given key across a collection.
  1676. * @method
  1677. * @param {string} key Field of the document to find distinct values for.
  1678. * @param {object} query The query for filtering the set of documents to which we apply the distinct filter.
  1679. * @param {object} [options=null] Optional settings.
  1680. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  1681. * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
  1682. * @param {ClientSession} [options.session] optional session to use for this operation
  1683. * @param {Collection~resultCallback} [callback] The command result callback
  1684. * @return {Promise} returns Promise if no callback passed
  1685. */
  1686. Collection.prototype.distinct = function(key, query, options, callback) {
  1687. var args = Array.prototype.slice.call(arguments, 1);
  1688. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1689. var queryOption = args.length ? args.shift() || {} : {};
  1690. var optionsOption = args.length ? args.shift() || {} : {};
  1691. return executeOperation(this.s.topology, distinct, [
  1692. this,
  1693. key,
  1694. queryOption,
  1695. optionsOption,
  1696. callback
  1697. ]);
  1698. };
  1699. var distinct = function(self, key, query, options, callback) {
  1700. // maxTimeMS option
  1701. var maxTimeMS = options.maxTimeMS;
  1702. // Distinct command
  1703. var cmd = {
  1704. distinct: self.s.name,
  1705. key: key,
  1706. query: query
  1707. };
  1708. options = shallowClone(options);
  1709. // Ensure we have the right read preference inheritance
  1710. options = getReadPreference(self, options, self.s.db, self);
  1711. // Add maxTimeMS if defined
  1712. if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
  1713. // Do we have a readConcern specified
  1714. decorateWithReadConcern(cmd, self, options);
  1715. // Have we specified collation
  1716. decorateWithCollation(cmd, self, options);
  1717. // Execute the command
  1718. self.s.db.command(cmd, options, function(err, result) {
  1719. if (err) return handleCallback(callback, err);
  1720. handleCallback(callback, null, result.values);
  1721. });
  1722. };
  1723. define.classMethod('distinct', { callback: true, promise: true });
  1724. /**
  1725. * Retrieve all the indexes on the collection.
  1726. * @method
  1727. * @param {Object} [options] Optional settings
  1728. * @param {ClientSession} [options.session] optional session to use for this operation
  1729. * @param {Collection~resultCallback} [callback] The command result callback
  1730. * @return {Promise} returns Promise if no callback passed
  1731. */
  1732. Collection.prototype.indexes = function(options, callback) {
  1733. if (typeof options === 'function') (callback = options), (options = {});
  1734. options = options || {};
  1735. return executeOperation(this.s.topology, indexes, [this, options, callback]);
  1736. };
  1737. var indexes = function(self, options, callback) {
  1738. options = Object.assign({}, { full: true }, options);
  1739. self.s.db.indexInformation(self.s.name, options, callback);
  1740. };
  1741. define.classMethod('indexes', { callback: true, promise: true });
  1742. /**
  1743. * Get all the collection statistics.
  1744. *
  1745. * @method
  1746. * @param {object} [options=null] Optional settings.
  1747. * @param {number} [options.scale=null] Divide the returned sizes by scale value.
  1748. * @param {ClientSession} [options.session] optional session to use for this operation
  1749. * @param {Collection~resultCallback} [callback] The collection result callback
  1750. * @return {Promise} returns Promise if no callback passed
  1751. */
  1752. Collection.prototype.stats = function(options, callback) {
  1753. var args = Array.prototype.slice.call(arguments, 0);
  1754. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1755. options = args.length ? args.shift() || {} : {};
  1756. return executeOperation(this.s.topology, stats, [this, options, callback]);
  1757. };
  1758. var stats = function(self, options, callback) {
  1759. // Build command object
  1760. var commandObject = {
  1761. collStats: self.s.name
  1762. };
  1763. // Check if we have the scale value
  1764. if (options['scale'] != null) commandObject['scale'] = options['scale'];
  1765. options = shallowClone(options);
  1766. // Ensure we have the right read preference inheritance
  1767. options = getReadPreference(self, options, self.s.db, self);
  1768. // Execute the command
  1769. self.s.db.command(commandObject, options, callback);
  1770. };
  1771. define.classMethod('stats', { callback: true, promise: true });
  1772. /**
  1773. * @typedef {Object} Collection~findAndModifyWriteOpResult
  1774. * @property {object} value Document returned from findAndModify command.
  1775. * @property {object} lastErrorObject The raw lastErrorObject returned from the command.
  1776. * @property {Number} ok Is 1 if the command executed correctly.
  1777. */
  1778. /**
  1779. * The callback format for inserts
  1780. * @callback Collection~findAndModifyCallback
  1781. * @param {MongoError} error An error instance representing the error during the execution.
  1782. * @param {Collection~findAndModifyWriteOpResult} result The result object if the command was executed successfully.
  1783. */
  1784. /**
  1785. * Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation.
  1786. *
  1787. * @method
  1788. * @param {object} filter Document selection filter.
  1789. * @param {object} [options=null] Optional settings.
  1790. * @param {object} [options.projection=null] Limits the fields to return for all matching documents.
  1791. * @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents.
  1792. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run.
  1793. * @param {ClientSession} [options.session] optional session to use for this operation
  1794. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  1795. * @return {Promise} returns Promise if no callback passed
  1796. */
  1797. Collection.prototype.findOneAndDelete = function(filter, options, callback) {
  1798. if (typeof options === 'function') (callback = options), (options = {});
  1799. options = options || {};
  1800. // Basic validation
  1801. if (filter == null || typeof filter !== 'object')
  1802. throw toError('filter parameter must be an object');
  1803. return executeOperation(this.s.topology, findOneAndDelete, [this, filter, options, callback]);
  1804. };
  1805. var findOneAndDelete = function(self, filter, options, callback) {
  1806. // Final options
  1807. var finalOptions = shallowClone(options);
  1808. finalOptions['fields'] = options.projection;
  1809. finalOptions['remove'] = true;
  1810. // Execute find and Modify
  1811. self.findAndModify(filter, options.sort, null, finalOptions, callback);
  1812. };
  1813. define.classMethod('findOneAndDelete', { callback: true, promise: true });
  1814. /**
  1815. * Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation.
  1816. *
  1817. * @method
  1818. * @param {object} filter Document selection filter.
  1819. * @param {object} replacement Document replacing the matching document.
  1820. * @param {object} [options=null] Optional settings.
  1821. * @param {object} [options.projection=null] Limits the fields to return for all matching documents.
  1822. * @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents.
  1823. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run.
  1824. * @param {boolean} [options.upsert=false] Upsert the document if it does not exist.
  1825. * @param {boolean} [options.returnOriginal=true] When false, returns the updated document rather than the original. The default is true.
  1826. * @param {ClientSession} [options.session] optional session to use for this operation
  1827. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  1828. * @return {Promise} returns Promise if no callback passed
  1829. */
  1830. Collection.prototype.findOneAndReplace = function(filter, replacement, options, callback) {
  1831. if (typeof options === 'function') (callback = options), (options = {});
  1832. options = options || {};
  1833. // Basic validation
  1834. if (filter == null || typeof filter !== 'object')
  1835. throw toError('filter parameter must be an object');
  1836. if (replacement == null || typeof replacement !== 'object')
  1837. throw toError('replacement parameter must be an object');
  1838. return executeOperation(this.s.topology, findOneAndReplace, [
  1839. this,
  1840. filter,
  1841. replacement,
  1842. options,
  1843. callback
  1844. ]);
  1845. };
  1846. var findOneAndReplace = function(self, filter, replacement, options, callback) {
  1847. // Final options
  1848. var finalOptions = shallowClone(options);
  1849. finalOptions['fields'] = options.projection;
  1850. finalOptions['update'] = true;
  1851. finalOptions['new'] = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
  1852. finalOptions['upsert'] = options.upsert !== void 0 ? !!options.upsert : false;
  1853. // Execute findAndModify
  1854. self.findAndModify(filter, options.sort, replacement, finalOptions, callback);
  1855. };
  1856. define.classMethod('findOneAndReplace', { callback: true, promise: true });
  1857. /**
  1858. * Find a document and update it in one atomic operation, requires a write lock for the duration of the operation.
  1859. *
  1860. * @method
  1861. * @param {object} filter Document selection filter.
  1862. * @param {object} update Update operations to be performed on the document
  1863. * @param {object} [options=null] Optional settings.
  1864. * @param {object} [options.projection=null] Limits the fields to return for all matching documents.
  1865. * @param {object} [options.sort=null] Determines which document the operation modifies if the query selects multiple documents.
  1866. * @param {number} [options.maxTimeMS=null] The maximum amount of time to allow the query to run.
  1867. * @param {boolean} [options.upsert=false] Upsert the document if it does not exist.
  1868. * @param {boolean} [options.returnOriginal=true] When false, returns the updated document rather than the original. The default is true.
  1869. * @param {ClientSession} [options.session] optional session to use for this operation
  1870. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  1871. * @return {Promise} returns Promise if no callback passed
  1872. */
  1873. Collection.prototype.findOneAndUpdate = function(filter, update, options, callback) {
  1874. if (typeof options === 'function') (callback = options), (options = {});
  1875. options = options || {};
  1876. // Basic validation
  1877. if (filter == null || typeof filter !== 'object')
  1878. throw toError('filter parameter must be an object');
  1879. if (update == null || typeof update !== 'object')
  1880. throw toError('update parameter must be an object');
  1881. return executeOperation(this.s.topology, findOneAndUpdate, [
  1882. this,
  1883. filter,
  1884. update,
  1885. options,
  1886. callback
  1887. ]);
  1888. };
  1889. var findOneAndUpdate = function(self, filter, update, options, callback) {
  1890. // Final options
  1891. var finalOptions = shallowClone(options);
  1892. finalOptions['fields'] = options.projection;
  1893. finalOptions['update'] = true;
  1894. finalOptions['new'] =
  1895. typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
  1896. finalOptions['upsert'] = typeof options.upsert === 'boolean' ? options.upsert : false;
  1897. // Execute findAndModify
  1898. self.findAndModify(filter, options.sort, update, finalOptions, callback);
  1899. };
  1900. define.classMethod('findOneAndUpdate', { callback: true, promise: true });
  1901. /**
  1902. * Find and update a document.
  1903. * @method
  1904. * @param {object} query Query object to locate the object to modify.
  1905. * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
  1906. * @param {object} doc The fields/vals to be updated.
  1907. * @param {object} [options=null] Optional settings.
  1908. * @param {(number|string)} [options.w=null] The write concern.
  1909. * @param {number} [options.wtimeout=null] The write concern timeout.
  1910. * @param {boolean} [options.j=false] Specify a journal write concern.
  1911. * @param {boolean} [options.remove=false] Set to true to remove the object before returning.
  1912. * @param {boolean} [options.upsert=false] Perform an upsert operation.
  1913. * @param {boolean} [options.new=false] Set to true if you want to return the modified object rather than the original. Ignored for remove.
  1914. * @param {object} [options.projection=null] Object containing the field projection for the result returned from the operation.
  1915. * @param {object} [options.fields=null] **Deprecated** Use `options.projection` instead
  1916. * @param {ClientSession} [options.session] optional session to use for this operation
  1917. * @param {Collection~findAndModifyCallback} [callback] The command result callback
  1918. * @return {Promise} returns Promise if no callback passed
  1919. * @deprecated use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
  1920. */
  1921. Collection.prototype.findAndModify = function(query, sort, doc, options, callback) {
  1922. var args = Array.prototype.slice.call(arguments, 1);
  1923. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  1924. sort = args.length ? args.shift() || [] : [];
  1925. doc = args.length ? args.shift() : null;
  1926. options = args.length ? args.shift() || {} : {};
  1927. // Clone options
  1928. options = shallowClone(options);
  1929. // Force read preference primary
  1930. options.readPreference = ReadPreference.PRIMARY;
  1931. return executeOperation(this.s.topology, findAndModify, [
  1932. this,
  1933. query,
  1934. sort,
  1935. doc,
  1936. options,
  1937. callback
  1938. ]);
  1939. };
  1940. var findAndModify = function(self, query, sort, doc, options, callback) {
  1941. // Create findAndModify command object
  1942. var queryObject = {
  1943. findandmodify: self.s.name,
  1944. query: query
  1945. };
  1946. sort = formattedOrderClause(sort);
  1947. if (sort) {
  1948. queryObject.sort = sort;
  1949. }
  1950. queryObject.new = options.new ? true : false;
  1951. queryObject.remove = options.remove ? true : false;
  1952. queryObject.upsert = options.upsert ? true : false;
  1953. const projection = options.projection || options.fields;
  1954. if (projection) {
  1955. queryObject.fields = projection;
  1956. }
  1957. if (options.arrayFilters) {
  1958. queryObject.arrayFilters = options.arrayFilters;
  1959. delete options.arrayFilters;
  1960. }
  1961. if (doc && !options.remove) {
  1962. queryObject.update = doc;
  1963. }
  1964. if (options.maxTimeMS) queryObject.maxTimeMS = options.maxTimeMS;
  1965. // Either use override on the function, or go back to default on either the collection
  1966. // level or db
  1967. if (options['serializeFunctions'] != null) {
  1968. options['serializeFunctions'] = options['serializeFunctions'];
  1969. } else {
  1970. options['serializeFunctions'] = self.s.serializeFunctions;
  1971. }
  1972. // No check on the documents
  1973. options.checkKeys = false;
  1974. // Get the write concern settings
  1975. var finalOptions = writeConcern(options, self.s.db, self, options);
  1976. // Decorate the findAndModify command with the write Concern
  1977. if (finalOptions.writeConcern) {
  1978. queryObject.writeConcern = finalOptions.writeConcern;
  1979. }
  1980. // Have we specified bypassDocumentValidation
  1981. if (typeof finalOptions.bypassDocumentValidation === 'boolean') {
  1982. queryObject.bypassDocumentValidation = finalOptions.bypassDocumentValidation;
  1983. }
  1984. // Have we specified collation
  1985. decorateWithCollation(queryObject, self, finalOptions);
  1986. // Execute the command
  1987. self.s.db.command(queryObject, finalOptions, function(err, result) {
  1988. if (err) return handleCallback(callback, err, null);
  1989. return handleCallback(callback, null, result);
  1990. });
  1991. };
  1992. define.classMethod('findAndModify', { callback: true, promise: true });
  1993. /**
  1994. * Find and remove a document.
  1995. * @method
  1996. * @param {object} query Query object to locate the object to modify.
  1997. * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
  1998. * @param {object} [options=null] Optional settings.
  1999. * @param {(number|string)} [options.w=null] The write concern.
  2000. * @param {number} [options.wtimeout=null] The write concern timeout.
  2001. * @param {boolean} [options.j=false] Specify a journal write concern.
  2002. * @param {ClientSession} [options.session] optional session to use for this operation
  2003. * @param {Collection~resultCallback} [callback] The command result callback
  2004. * @return {Promise} returns Promise if no callback passed
  2005. * @deprecated use findOneAndDelete instead
  2006. */
  2007. Collection.prototype.findAndRemove = function(query, sort, options, callback) {
  2008. var args = Array.prototype.slice.call(arguments, 1);
  2009. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  2010. sort = args.length ? args.shift() || [] : [];
  2011. options = args.length ? args.shift() || {} : {};
  2012. return executeOperation(this.s.topology, findAndRemove, [this, query, sort, options, callback]);
  2013. };
  2014. var findAndRemove = function(self, query, sort, options, callback) {
  2015. // Add the remove option
  2016. options['remove'] = true;
  2017. // Execute the callback
  2018. self.findAndModify(query, sort, null, options, callback);
  2019. };
  2020. define.classMethod('findAndRemove', { callback: true, promise: true });
  2021. function decorateWithWriteConcern(command, self, options) {
  2022. // Do we support collation 3.4 and higher
  2023. var capabilities = self.s.topology.capabilities();
  2024. // Do we support write concerns 3.4 and higher
  2025. if (capabilities && capabilities.commandsTakeWriteConcern) {
  2026. // Get the write concern settings
  2027. var finalOptions = writeConcern(shallowClone(options), self.s.db, self, options);
  2028. // Add the write concern to the command
  2029. if (finalOptions.writeConcern) {
  2030. command.writeConcern = finalOptions.writeConcern;
  2031. }
  2032. }
  2033. }
  2034. function decorateWithCollation(command, self, options) {
  2035. // Do we support collation 3.4 and higher
  2036. var capabilities = self.s.topology.capabilities();
  2037. // Do we support write concerns 3.4 and higher
  2038. if (capabilities && capabilities.commandsTakeCollation) {
  2039. if (options.collation && typeof options.collation === 'object') {
  2040. command.collation = options.collation;
  2041. }
  2042. }
  2043. }
  2044. function decorateWithReadConcern(command, self, options) {
  2045. let readConcern = Object.assign({}, command.readConcern || {});
  2046. if (self.s.readConcern) {
  2047. Object.assign(readConcern, self.s.readConcern);
  2048. }
  2049. if (
  2050. options.session &&
  2051. options.session.supports.causalConsistency &&
  2052. options.session.operationTime
  2053. ) {
  2054. Object.assign(readConcern, { afterClusterTime: options.session.operationTime });
  2055. }
  2056. if (Object.keys(readConcern).length > 0) {
  2057. Object.assign(command, { readConcern: readConcern });
  2058. }
  2059. }
  2060. /**
  2061. * Execute an aggregation framework pipeline against the collection, needs MongoDB >= 2.2
  2062. * @method
  2063. * @param {object} pipeline Array containing all the aggregation framework commands for the execution.
  2064. * @param {object} [options=null] Optional settings.
  2065. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  2066. * @param {object} [options.cursor=null] Return the query as cursor, on 2.6 > it returns as a real cursor on pre 2.6 it returns as an emulated cursor.
  2067. * @param {number} [options.cursor.batchSize=null] The batchSize for the cursor
  2068. * @param {boolean} [options.explain=false] Explain returns the aggregation execution plan (requires mongodb 2.6 >).
  2069. * @param {boolean} [options.allowDiskUse=false] allowDiskUse lets the server know if it can use disk to store temporary results for the aggregation (requires mongodb 2.6 >).
  2070. * @param {number} [options.maxTimeMS=null] maxTimeMS specifies a cumulative time limit in milliseconds for processing operations on the cursor. MongoDB interrupts the operation at the earliest following interrupt point.
  2071. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  2072. * @param {boolean} [options.raw=false] Return document results as raw BSON buffers.
  2073. * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
  2074. * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
  2075. * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
  2076. * @param {object} [options.collation=null] Specify collation (MongoDB 3.4 or higher) settings for update operation (see 3.4 documentation for available fields).
  2077. * @param {string} [options.comment] Add a comment to an aggregation command
  2078. * @param {ClientSession} [options.session] optional session to use for this operation
  2079. * @param {Collection~aggregationCallback} callback The command result callback
  2080. * @return {(null|AggregationCursor)}
  2081. */
  2082. Collection.prototype.aggregate = function(pipeline, options, callback) {
  2083. var self = this;
  2084. if (Array.isArray(pipeline)) {
  2085. // Set up callback if one is provided
  2086. if (typeof options === 'function') {
  2087. callback = options;
  2088. options = {};
  2089. }
  2090. // If we have no options or callback we are doing
  2091. // a cursor based aggregation
  2092. if (options == null && callback == null) {
  2093. options = {};
  2094. }
  2095. } else {
  2096. // Aggregation pipeline passed as arguments on the method
  2097. var args = Array.prototype.slice.call(arguments, 0);
  2098. // Get the callback
  2099. callback = args.pop();
  2100. // Get the possible options object
  2101. var opts = args[args.length - 1];
  2102. // If it contains any of the admissible options pop it of the args
  2103. options =
  2104. opts &&
  2105. (opts.readPreference ||
  2106. opts.explain ||
  2107. opts.cursor ||
  2108. opts.out ||
  2109. opts.maxTimeMS ||
  2110. opts.hint ||
  2111. opts.allowDiskUse)
  2112. ? args.pop()
  2113. : {};
  2114. // Left over arguments is the pipeline
  2115. pipeline = args;
  2116. }
  2117. // Ignore readConcern option
  2118. var ignoreReadConcern = false;
  2119. // Build the command
  2120. var command = { aggregate: this.s.name, pipeline: pipeline };
  2121. // If out was specified
  2122. if (typeof options.out === 'string') {
  2123. pipeline.push({ $out: options.out });
  2124. // Ignore read concern
  2125. ignoreReadConcern = true;
  2126. } else if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out']) {
  2127. ignoreReadConcern = true;
  2128. }
  2129. // Decorate command with writeConcern if out has been specified
  2130. if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out']) {
  2131. decorateWithWriteConcern(command, self, options);
  2132. }
  2133. // Have we specified collation
  2134. decorateWithCollation(command, self, options);
  2135. // If we have bypassDocumentValidation set
  2136. if (typeof options.bypassDocumentValidation === 'boolean') {
  2137. command.bypassDocumentValidation = options.bypassDocumentValidation;
  2138. }
  2139. // Do we have a readConcern specified
  2140. if (!ignoreReadConcern) {
  2141. decorateWithReadConcern(command, self, options);
  2142. }
  2143. // If we have allowDiskUse defined
  2144. if (options.allowDiskUse) command.allowDiskUse = options.allowDiskUse;
  2145. if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
  2146. // If we are giving a hint
  2147. if (options.hint) command.hint = options.hint;
  2148. options = shallowClone(options);
  2149. // Ensure we have the right read preference inheritance
  2150. options = getReadPreference(this, options, this.s.db, this);
  2151. // If explain has been specified add it
  2152. if (options.explain) {
  2153. if (command.readConcern || command.writeConcern) {
  2154. throw toError('"explain" cannot be used on an aggregate call with readConcern/writeConcern');
  2155. }
  2156. command.explain = options.explain;
  2157. }
  2158. if (typeof options.comment === 'string') command.comment = options.comment;
  2159. // Validate that cursor options is valid
  2160. if (options.cursor != null && typeof options.cursor !== 'object') {
  2161. throw toError('cursor options must be an object');
  2162. }
  2163. options.cursor = options.cursor || { batchSize: 1000 };
  2164. command.cursor = options.cursor;
  2165. // promiseLibrary
  2166. options.promiseLibrary = this.s.promiseLibrary;
  2167. // Set the AggregationCursor constructor
  2168. options.cursorFactory = AggregationCursor;
  2169. if (typeof callback !== 'function') {
  2170. if (!this.s.topology.capabilities()) {
  2171. throw new MongoError('cannot connect to server');
  2172. }
  2173. // Allow disk usage command
  2174. if (typeof options.allowDiskUse === 'boolean') command.allowDiskUse = options.allowDiskUse;
  2175. if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
  2176. // Execute the cursor
  2177. return this.s.topology.cursor(this.s.namespace, command, options);
  2178. }
  2179. return handleCallback(callback, null, this.s.topology.cursor(this.s.namespace, command, options));
  2180. };
  2181. define.classMethod('aggregate', { callback: true, promise: false });
  2182. /**
  2183. * Create a new Change Stream, watching for new changes (insertions, updates, replacements, deletions, and invalidations) in this collection.
  2184. * @method
  2185. * @since 3.0.0
  2186. * @param {Array} [pipeline=null] An array of {@link https://docs.mongodb.com/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.
  2187. * @param {object} [options=null] Optional settings
  2188. * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
  2189. * @param {object} [options.resumeAfter=null] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
  2190. * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
  2191. * @param {number} [options.batchSize=null] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
  2192. * @param {object} [options.collation=null] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
  2193. * @param {ReadPreference} [options.readPreference=null] The read preference. Defaults to the read preference of the database or collection. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
  2194. * @param {ClientSession} [options.session] optional session to use for this operation
  2195. * @return {ChangeStream} a ChangeStream instance.
  2196. */
  2197. Collection.prototype.watch = function(pipeline, options) {
  2198. pipeline = pipeline || [];
  2199. options = options || {};
  2200. // Allow optionally not specifying a pipeline
  2201. if (!Array.isArray(pipeline)) {
  2202. options = pipeline;
  2203. pipeline = [];
  2204. }
  2205. return new ChangeStream(this, pipeline, options);
  2206. };
  2207. define.classMethod('watch', { callback: false, promise: false });
  2208. /**
  2209. * The callback format for results
  2210. * @callback Collection~parallelCollectionScanCallback
  2211. * @param {MongoError} error An error instance representing the error during the execution.
  2212. * @param {Cursor[]} cursors A list of cursors returned allowing for parallel reading of collection.
  2213. */
  2214. /**
  2215. * Return N number of parallel cursors for a collection allowing parallel reading of entire collection. There are
  2216. * no ordering guarantees for returned results.
  2217. * @method
  2218. * @param {object} [options=null] Optional settings.
  2219. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  2220. * @param {number} [options.batchSize=null] Set the batchSize for the getMoreCommand when iterating over the query results.
  2221. * @param {number} [options.numCursors=1] The maximum number of parallel command cursors to return (the number of returned cursors will be in the range 1:numCursors)
  2222. * @param {boolean} [options.raw=false] Return all BSON documents as Raw Buffer documents.
  2223. * @param {ClientSession} [options.session] optional session to use for this operation
  2224. * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
  2225. * @return {Promise} returns Promise if no callback passed
  2226. */
  2227. Collection.prototype.parallelCollectionScan = function(options, callback) {
  2228. if (typeof options === 'function') (callback = options), (options = { numCursors: 1 });
  2229. // Set number of cursors to 1
  2230. options.numCursors = options.numCursors || 1;
  2231. options.batchSize = options.batchSize || 1000;
  2232. options = shallowClone(options);
  2233. // Ensure we have the right read preference inheritance
  2234. options = getReadPreference(this, options, this.s.db, this);
  2235. // Add a promiseLibrary
  2236. options.promiseLibrary = this.s.promiseLibrary;
  2237. return executeOperation(this.s.topology, parallelCollectionScan, [this, options, callback], {
  2238. returnsCursor: true
  2239. });
  2240. };
  2241. var parallelCollectionScan = function(self, options, callback) {
  2242. // Create command object
  2243. var commandObject = {
  2244. parallelCollectionScan: self.s.name,
  2245. numCursors: options.numCursors
  2246. };
  2247. // Do we have a readConcern specified
  2248. decorateWithReadConcern(commandObject, self, options);
  2249. // Store the raw value
  2250. var raw = options.raw;
  2251. delete options['raw'];
  2252. // Execute the command
  2253. self.s.db.command(commandObject, options, function(err, result) {
  2254. if (err) return handleCallback(callback, err, null);
  2255. if (result == null)
  2256. return handleCallback(
  2257. callback,
  2258. new Error('no result returned for parallelCollectionScan'),
  2259. null
  2260. );
  2261. var cursors = [];
  2262. // Add the raw back to the option
  2263. if (raw) options.raw = raw;
  2264. // Create command cursors for each item
  2265. for (var i = 0; i < result.cursors.length; i++) {
  2266. var rawId = result.cursors[i].cursor.id;
  2267. // Convert cursorId to Long if needed
  2268. var cursorId = typeof rawId === 'number' ? Long.fromNumber(rawId) : rawId;
  2269. // Add a command cursor
  2270. cursors.push(self.s.topology.cursor(self.s.namespace, cursorId, options));
  2271. }
  2272. handleCallback(callback, null, cursors);
  2273. });
  2274. };
  2275. define.classMethod('parallelCollectionScan', { callback: true, promise: true });
  2276. /**
  2277. * Execute a geo search using a geo haystack index on a collection.
  2278. *
  2279. * @method
  2280. * @param {number} x Point to search on the x axis, ensure the indexes are ordered in the same order.
  2281. * @param {number} y Point to search on the y axis, ensure the indexes are ordered in the same order.
  2282. * @param {object} [options=null] Optional settings.
  2283. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  2284. * @param {number} [options.maxDistance=null] Include results up to maxDistance from the point.
  2285. * @param {object} [options.search=null] Filter the results by a query.
  2286. * @param {number} [options.limit=false] Max number of results to return.
  2287. * @param {ClientSession} [options.session] optional session to use for this operation
  2288. * @param {Collection~resultCallback} [callback] The command result callback
  2289. * @return {Promise} returns Promise if no callback passed
  2290. */
  2291. Collection.prototype.geoHaystackSearch = function(x, y, options, callback) {
  2292. var args = Array.prototype.slice.call(arguments, 2);
  2293. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  2294. options = args.length ? args.shift() || {} : {};
  2295. return executeOperation(this.s.topology, geoHaystackSearch, [this, x, y, options, callback]);
  2296. };
  2297. var geoHaystackSearch = function(self, x, y, options, callback) {
  2298. // Build command object
  2299. var commandObject = {
  2300. geoSearch: self.s.name,
  2301. near: [x, y]
  2302. };
  2303. // Remove read preference from hash if it exists
  2304. commandObject = decorateCommand(commandObject, options, { readPreference: true, session: true });
  2305. options = shallowClone(options);
  2306. // Ensure we have the right read preference inheritance
  2307. options = getReadPreference(self, options, self.s.db, self);
  2308. // Do we have a readConcern specified
  2309. decorateWithReadConcern(commandObject, self, options);
  2310. // Execute the command
  2311. self.s.db.command(commandObject, options, function(err, res) {
  2312. if (err) return handleCallback(callback, err);
  2313. if (res.err || res.errmsg) handleCallback(callback, toError(res));
  2314. // should we only be returning res.results here? Not sure if the user
  2315. // should see the other return information
  2316. handleCallback(callback, null, res);
  2317. });
  2318. };
  2319. define.classMethod('geoHaystackSearch', { callback: true, promise: true });
  2320. /**
  2321. * Group function helper
  2322. * @ignore
  2323. */
  2324. // var groupFunction = function () {
  2325. // var c = db[ns].find(condition);
  2326. // var map = new Map();
  2327. // var reduce_function = reduce;
  2328. //
  2329. // while (c.hasNext()) {
  2330. // var obj = c.next();
  2331. // var key = {};
  2332. //
  2333. // for (var i = 0, len = keys.length; i < len; ++i) {
  2334. // var k = keys[i];
  2335. // key[k] = obj[k];
  2336. // }
  2337. //
  2338. // var aggObj = map.get(key);
  2339. //
  2340. // if (aggObj == null) {
  2341. // var newObj = Object.extend({}, key);
  2342. // aggObj = Object.extend(newObj, initial);
  2343. // map.put(key, aggObj);
  2344. // }
  2345. //
  2346. // reduce_function(obj, aggObj);
  2347. // }
  2348. //
  2349. // return { "result": map.values() };
  2350. // }.toString();
  2351. var groupFunction =
  2352. 'function () {\nvar c = db[ns].find(condition);\nvar map = new Map();\nvar reduce_function = reduce;\n\nwhile (c.hasNext()) {\nvar obj = c.next();\nvar key = {};\n\nfor (var i = 0, len = keys.length; i < len; ++i) {\nvar k = keys[i];\nkey[k] = obj[k];\n}\n\nvar aggObj = map.get(key);\n\nif (aggObj == null) {\nvar newObj = Object.extend({}, key);\naggObj = Object.extend(newObj, initial);\nmap.put(key, aggObj);\n}\n\nreduce_function(obj, aggObj);\n}\n\nreturn { "result": map.values() };\n}';
  2353. /**
  2354. * Run a group command across a collection
  2355. *
  2356. * @method
  2357. * @param {(object|array|function|code)} keys An object, array or function expressing the keys to group by.
  2358. * @param {object} condition An optional condition that must be true for a row to be considered.
  2359. * @param {object} initial Initial value of the aggregation counter object.
  2360. * @param {(function|Code)} reduce The reduce function aggregates (reduces) the objects iterated
  2361. * @param {(function|Code)} finalize An optional function to be run on each item in the result set just before the item is returned.
  2362. * @param {boolean} command Specify if you wish to run using the internal group command or using eval, default is true.
  2363. * @param {object} [options=null] Optional settings.
  2364. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  2365. * @param {ClientSession} [options.session] optional session to use for this operation
  2366. * @param {Collection~resultCallback} [callback] The command result callback
  2367. * @return {Promise} returns Promise if no callback passed
  2368. * @deprecated MongoDB 3.6 or higher will no longer support the group command. We recommend rewriting using the aggregation framework.
  2369. */
  2370. Collection.prototype.group = function(
  2371. keys,
  2372. condition,
  2373. initial,
  2374. reduce,
  2375. finalize,
  2376. command,
  2377. options,
  2378. callback
  2379. ) {
  2380. var args = Array.prototype.slice.call(arguments, 3);
  2381. callback = typeof args[args.length - 1] === 'function' ? args.pop() : undefined;
  2382. reduce = args.length ? args.shift() : null;
  2383. finalize = args.length ? args.shift() : null;
  2384. command = args.length ? args.shift() : null;
  2385. options = args.length ? args.shift() || {} : {};
  2386. // Make sure we are backward compatible
  2387. if (!(typeof finalize === 'function')) {
  2388. command = finalize;
  2389. finalize = null;
  2390. }
  2391. if (
  2392. !Array.isArray(keys) &&
  2393. keys instanceof Object &&
  2394. typeof keys !== 'function' &&
  2395. !(keys._bsontype === 'Code')
  2396. ) {
  2397. keys = Object.keys(keys);
  2398. }
  2399. if (typeof reduce === 'function') {
  2400. reduce = reduce.toString();
  2401. }
  2402. if (typeof finalize === 'function') {
  2403. finalize = finalize.toString();
  2404. }
  2405. // Set up the command as default
  2406. command = command == null ? true : command;
  2407. return executeOperation(this.s.topology, group, [
  2408. this,
  2409. keys,
  2410. condition,
  2411. initial,
  2412. reduce,
  2413. finalize,
  2414. command,
  2415. options,
  2416. callback
  2417. ]);
  2418. };
  2419. var group = function(self, keys, condition, initial, reduce, finalize, command, options, callback) {
  2420. // Execute using the command
  2421. if (command) {
  2422. var reduceFunction = reduce && reduce._bsontype === 'Code' ? reduce : new Code(reduce);
  2423. var selector = {
  2424. group: {
  2425. ns: self.s.name,
  2426. $reduce: reduceFunction,
  2427. cond: condition,
  2428. initial: initial,
  2429. out: 'inline'
  2430. }
  2431. };
  2432. // if finalize is defined
  2433. if (finalize != null) selector.group['finalize'] = finalize;
  2434. // Set up group selector
  2435. if ('function' === typeof keys || (keys && keys._bsontype === 'Code')) {
  2436. selector.group.$keyf = keys && keys._bsontype === 'Code' ? keys : new Code(keys);
  2437. } else {
  2438. var hash = {};
  2439. keys.forEach(function(key) {
  2440. hash[key] = 1;
  2441. });
  2442. selector.group.key = hash;
  2443. }
  2444. options = shallowClone(options);
  2445. // Ensure we have the right read preference inheritance
  2446. options = getReadPreference(self, options, self.s.db, self);
  2447. // Do we have a readConcern specified
  2448. decorateWithReadConcern(selector, self, options);
  2449. // Have we specified collation
  2450. decorateWithCollation(selector, self, options);
  2451. // Execute command
  2452. self.s.db.command(selector, options, function(err, result) {
  2453. if (err) return handleCallback(callback, err, null);
  2454. handleCallback(callback, null, result.retval);
  2455. });
  2456. } else {
  2457. // Create execution scope
  2458. var scope = reduce != null && reduce._bsontype === 'Code' ? reduce.scope : {};
  2459. scope.ns = self.s.name;
  2460. scope.keys = keys;
  2461. scope.condition = condition;
  2462. scope.initial = initial;
  2463. // Pass in the function text to execute within mongodb.
  2464. var groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
  2465. self.s.db.eval(new Code(groupfn, scope), null, options, function(err, results) {
  2466. if (err) return handleCallback(callback, err, null);
  2467. handleCallback(callback, null, results.result || results);
  2468. });
  2469. }
  2470. };
  2471. define.classMethod('group', { callback: true, promise: true });
  2472. /**
  2473. * Functions that are passed as scope args must
  2474. * be converted to Code instances.
  2475. * @ignore
  2476. */
  2477. function processScope(scope) {
  2478. if (!isObject(scope) || scope._bsontype === 'ObjectID') {
  2479. return scope;
  2480. }
  2481. var keys = Object.keys(scope);
  2482. var i = keys.length;
  2483. var key;
  2484. var new_scope = {};
  2485. while (i--) {
  2486. key = keys[i];
  2487. if ('function' === typeof scope[key]) {
  2488. new_scope[key] = new Code(String(scope[key]));
  2489. } else {
  2490. new_scope[key] = processScope(scope[key]);
  2491. }
  2492. }
  2493. return new_scope;
  2494. }
  2495. /**
  2496. * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
  2497. *
  2498. * @method
  2499. * @param {(function|string)} map The mapping function.
  2500. * @param {(function|string)} reduce The reduce function.
  2501. * @param {object} [options=null] Optional settings.
  2502. * @param {(ReadPreference|string)} [options.readPreference=null] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
  2503. * @param {object} [options.out=null] Sets the output target for the map reduce job. *{inline:1} | {replace:'collectionName'} | {merge:'collectionName'} | {reduce:'collectionName'}*
  2504. * @param {object} [options.query=null] Query filter object.
  2505. * @param {object} [options.sort=null] Sorts the input objects using this key. Useful for optimization, like sorting by the emit key for fewer reduces.
  2506. * @param {number} [options.limit=null] Number of objects to return from collection.
  2507. * @param {boolean} [options.keeptemp=false] Keep temporary data.
  2508. * @param {(function|string)} [options.finalize=null] Finalize function.
  2509. * @param {object} [options.scope=null] Can pass in variables that can be access from map/reduce/finalize.
  2510. * @param {boolean} [options.jsMode=false] It is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X.
  2511. * @param {boolean} [options.verbose=false] Provide statistics on job execution time.
  2512. * @param {boolean} [options.bypassDocumentValidation=false] Allow driver to bypass schema validation in MongoDB 3.2 or higher.
  2513. * @param {ClientSession} [options.session] optional session to use for this operation
  2514. * @param {Collection~resultCallback} [callback] The command result callback
  2515. * @throws {MongoError}
  2516. * @return {Promise} returns Promise if no callback passed
  2517. */
  2518. Collection.prototype.mapReduce = function(map, reduce, options, callback) {
  2519. if ('function' === typeof options) (callback = options), (options = {});
  2520. // Out must allways be defined (make sure we don't break weirdly on pre 1.8+ servers)
  2521. if (null == options.out) {
  2522. throw new Error(
  2523. 'the out option parameter must be defined, see mongodb docs for possible values'
  2524. );
  2525. }
  2526. if ('function' === typeof map) {
  2527. map = map.toString();
  2528. }
  2529. if ('function' === typeof reduce) {
  2530. reduce = reduce.toString();
  2531. }
  2532. if ('function' === typeof options.finalize) {
  2533. options.finalize = options.finalize.toString();
  2534. }
  2535. return executeOperation(this.s.topology, mapReduce, [this, map, reduce, options, callback]);
  2536. };
  2537. var mapReduce = function(self, map, reduce, options, callback) {
  2538. var mapCommandHash = {
  2539. mapreduce: self.s.name,
  2540. map: map,
  2541. reduce: reduce
  2542. };
  2543. // Exclusion list
  2544. var exclusionList = ['readPreference', 'session'];
  2545. // Add any other options passed in
  2546. for (var n in options) {
  2547. if ('scope' === n) {
  2548. mapCommandHash[n] = processScope(options[n]);
  2549. } else {
  2550. // Only include if not in exclusion list
  2551. if (exclusionList.indexOf(n) === -1) {
  2552. mapCommandHash[n] = options[n];
  2553. }
  2554. }
  2555. }
  2556. options = shallowClone(options);
  2557. // Ensure we have the right read preference inheritance
  2558. options = getReadPreference(self, options, self.s.db, self);
  2559. // If we have a read preference and inline is not set as output fail hard
  2560. if (
  2561. options.readPreference !== false &&
  2562. options.readPreference !== 'primary' &&
  2563. options['out'] &&
  2564. (options['out'].inline !== 1 && options['out'] !== 'inline')
  2565. ) {
  2566. // Force readPreference to primary
  2567. options.readPreference = 'primary';
  2568. // Decorate command with writeConcern if supported
  2569. decorateWithWriteConcern(mapCommandHash, self, options);
  2570. } else {
  2571. decorateWithReadConcern(mapCommandHash, self, options);
  2572. }
  2573. // Is bypassDocumentValidation specified
  2574. if (typeof options.bypassDocumentValidation === 'boolean') {
  2575. mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
  2576. }
  2577. // Have we specified collation
  2578. decorateWithCollation(mapCommandHash, self, options);
  2579. // Execute command
  2580. self.s.db.command(mapCommandHash, options, function(err, result) {
  2581. if (err) return handleCallback(callback, err);
  2582. // Check if we have an error
  2583. if (1 !== result.ok || result.err || result.errmsg) {
  2584. return handleCallback(callback, toError(result));
  2585. }
  2586. // Create statistics value
  2587. var stats = {};
  2588. if (result.timeMillis) stats['processtime'] = result.timeMillis;
  2589. if (result.counts) stats['counts'] = result.counts;
  2590. if (result.timing) stats['timing'] = result.timing;
  2591. // invoked with inline?
  2592. if (result.results) {
  2593. // If we wish for no verbosity
  2594. if (options['verbose'] == null || !options['verbose']) {
  2595. return handleCallback(callback, null, result.results);
  2596. }
  2597. return handleCallback(callback, null, { results: result.results, stats: stats });
  2598. }
  2599. // The returned collection
  2600. var collection = null;
  2601. // If we have an object it's a different db
  2602. if (result.result != null && typeof result.result === 'object') {
  2603. var doc = result.result;
  2604. // Return a collection from another db
  2605. var Db = require('./db');
  2606. collection = new Db(doc.db, self.s.db.s.topology, self.s.db.s.options).collection(
  2607. doc.collection
  2608. );
  2609. } else {
  2610. // Create a collection object that wraps the result collection
  2611. collection = self.s.db.collection(result.result);
  2612. }
  2613. // If we wish for no verbosity
  2614. if (options['verbose'] == null || !options['verbose']) {
  2615. return handleCallback(callback, err, collection);
  2616. }
  2617. // Return stats as third set of values
  2618. handleCallback(callback, err, { collection: collection, stats: stats });
  2619. });
  2620. };
  2621. define.classMethod('mapReduce', { callback: true, promise: true });
  2622. /**
  2623. * Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
  2624. *
  2625. * @method
  2626. * @param {object} [options=null] Optional settings.
  2627. * @param {(number|string)} [options.w=null] The write concern.
  2628. * @param {number} [options.wtimeout=null] The write concern timeout.
  2629. * @param {boolean} [options.j=false] Specify a journal write concern.
  2630. * @param {ClientSession} [options.session] optional session to use for this operation
  2631. * @return {UnorderedBulkOperation}
  2632. */
  2633. Collection.prototype.initializeUnorderedBulkOp = function(options) {
  2634. options = options || {};
  2635. options.promiseLibrary = this.s.promiseLibrary;
  2636. return unordered(this.s.topology, this, options);
  2637. };
  2638. define.classMethod('initializeUnorderedBulkOp', {
  2639. callback: false,
  2640. promise: false,
  2641. returns: [ordered.UnorderedBulkOperation]
  2642. });
  2643. /**
  2644. * 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.
  2645. *
  2646. * @method
  2647. * @param {object} [options=null] Optional settings.
  2648. * @param {(number|string)} [options.w=null] The write concern.
  2649. * @param {number} [options.wtimeout=null] The write concern timeout.
  2650. * @param {boolean} [options.j=false] Specify a journal write concern.
  2651. * @param {ClientSession} [options.session] optional session to use for this operation
  2652. * @param {OrderedBulkOperation} callback The command result callback
  2653. * @return {null}
  2654. */
  2655. Collection.prototype.initializeOrderedBulkOp = function(options) {
  2656. options = options || {};
  2657. options.promiseLibrary = this.s.promiseLibrary;
  2658. return ordered(this.s.topology, this, options);
  2659. };
  2660. define.classMethod('initializeOrderedBulkOp', {
  2661. callback: false,
  2662. promise: false,
  2663. returns: [ordered.OrderedBulkOperation]
  2664. });
  2665. // Get write concern
  2666. var writeConcern = function(target, db, col, options) {
  2667. if (options.w != null || options.j != null || options.fsync != null) {
  2668. var opts = {};
  2669. if (options.w != null) opts.w = options.w;
  2670. if (options.wtimeout != null) opts.wtimeout = options.wtimeout;
  2671. if (options.j != null) opts.j = options.j;
  2672. if (options.fsync != null) opts.fsync = options.fsync;
  2673. target.writeConcern = opts;
  2674. } else if (
  2675. col.writeConcern.w != null ||
  2676. col.writeConcern.j != null ||
  2677. col.writeConcern.fsync != null
  2678. ) {
  2679. target.writeConcern = col.writeConcern;
  2680. } else if (
  2681. db.writeConcern.w != null ||
  2682. db.writeConcern.j != null ||
  2683. db.writeConcern.fsync != null
  2684. ) {
  2685. target.writeConcern = db.writeConcern;
  2686. }
  2687. // NOTE: there is probably a much better place for this
  2688. if (db.s.options.retryWrites) target.retryWrites = true;
  2689. return target;
  2690. };
  2691. // Figure out the read preference
  2692. var getReadPreference = function(self, options, db) {
  2693. let r = null;
  2694. if (options.readPreference) {
  2695. r = options.readPreference;
  2696. } else if (self.s.readPreference) {
  2697. r = self.s.readPreference;
  2698. } else if (db.s.readPreference) {
  2699. r = db.s.readPreference;
  2700. } else {
  2701. return options;
  2702. }
  2703. if (typeof r === 'string') {
  2704. options.readPreference = new ReadPreference(r);
  2705. } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
  2706. const mode = r.mode || r.preference;
  2707. if (mode && typeof mode === 'string') {
  2708. options.readPreference = new ReadPreference(mode, r.tags, {
  2709. maxStalenessSeconds: r.maxStalenessSeconds
  2710. });
  2711. }
  2712. } else if (!(r instanceof ReadPreference)) {
  2713. throw new TypeError('Invalid read preference: ' + r);
  2714. }
  2715. return options;
  2716. };
  2717. module.exports = Collection;