collection_ops.js 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  1. 'use strict';
  2. const applyWriteConcern = require('../utils').applyWriteConcern;
  3. const applyRetryableWrites = require('../utils').applyRetryableWrites;
  4. const checkCollectionName = require('../utils').checkCollectionName;
  5. const Code = require('mongodb-core').BSON.Code;
  6. const createIndexDb = require('./db_ops').createIndex;
  7. const decorateCommand = require('../utils').decorateCommand;
  8. const decorateWithCollation = require('../utils').decorateWithCollation;
  9. const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
  10. const ensureIndexDb = require('./db_ops').ensureIndex;
  11. const evaluate = require('./db_ops').evaluate;
  12. const executeCommand = require('./db_ops').executeCommand;
  13. const executeDbAdminCommand = require('./db_ops').executeDbAdminCommand;
  14. const formattedOrderClause = require('../utils').formattedOrderClause;
  15. const resolveReadPreference = require('../utils').resolveReadPreference;
  16. const handleCallback = require('../utils').handleCallback;
  17. const indexInformationDb = require('./db_ops').indexInformation;
  18. const isObject = require('../utils').isObject;
  19. const Long = require('mongodb-core').BSON.Long;
  20. const MongoError = require('mongodb-core').MongoError;
  21. const ReadPreference = require('mongodb-core').ReadPreference;
  22. const toError = require('../utils').toError;
  23. let collection;
  24. function loadCollection() {
  25. if (!collection) {
  26. collection = require('../collection');
  27. }
  28. return collection;
  29. }
  30. let db;
  31. function loadDb() {
  32. if (!db) {
  33. db = require('../db');
  34. }
  35. return db;
  36. }
  37. /**
  38. * Group function helper
  39. * @ignore
  40. */
  41. // var groupFunction = function () {
  42. // var c = db[ns].find(condition);
  43. // var map = new Map();
  44. // var reduce_function = reduce;
  45. //
  46. // while (c.hasNext()) {
  47. // var obj = c.next();
  48. // var key = {};
  49. //
  50. // for (var i = 0, len = keys.length; i < len; ++i) {
  51. // var k = keys[i];
  52. // key[k] = obj[k];
  53. // }
  54. //
  55. // var aggObj = map.get(key);
  56. //
  57. // if (aggObj == null) {
  58. // var newObj = Object.extend({}, key);
  59. // aggObj = Object.extend(newObj, initial);
  60. // map.put(key, aggObj);
  61. // }
  62. //
  63. // reduce_function(obj, aggObj);
  64. // }
  65. //
  66. // return { "result": map.values() };
  67. // }.toString();
  68. const groupFunction =
  69. '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}';
  70. /**
  71. * Perform a bulkWrite operation. See Collection.prototype.bulkWrite for more information.
  72. *
  73. * @method
  74. * @param {Collection} a Collection instance.
  75. * @param {object[]} operations Bulk operations to perform.
  76. * @param {object} [options] Optional settings. See Collection.prototype.bulkWrite for a list of options.
  77. * @param {Collection~bulkWriteOpCallback} [callback] The command result callback
  78. */
  79. function bulkWrite(coll, operations, options, callback) {
  80. // Add ignoreUndfined
  81. if (coll.s.options.ignoreUndefined) {
  82. options = Object.assign({}, options);
  83. options.ignoreUndefined = coll.s.options.ignoreUndefined;
  84. }
  85. // Create the bulk operation
  86. const bulk =
  87. options.ordered === true || options.ordered == null
  88. ? coll.initializeOrderedBulkOp(options)
  89. : coll.initializeUnorderedBulkOp(options);
  90. // Do we have a collation
  91. let collation = false;
  92. // for each op go through and add to the bulk
  93. try {
  94. for (let i = 0; i < operations.length; i++) {
  95. // Get the operation type
  96. const key = Object.keys(operations[i])[0];
  97. // Check if we have a collation
  98. if (operations[i][key].collation) {
  99. collation = true;
  100. }
  101. // Pass to the raw bulk
  102. bulk.raw(operations[i]);
  103. }
  104. } catch (err) {
  105. return callback(err, null);
  106. }
  107. // Final options for retryable writes and write concern
  108. let finalOptions = Object.assign({}, options);
  109. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  110. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  111. const writeCon = finalOptions.writeConcern ? finalOptions.writeConcern : {};
  112. const capabilities = coll.s.topology.capabilities();
  113. // Did the user pass in a collation, check if our write server supports it
  114. if (collation && capabilities && !capabilities.commandsTakeCollation) {
  115. return callback(new MongoError('server/primary/mongos does not support collation'));
  116. }
  117. // Execute the bulk
  118. bulk.execute(writeCon, finalOptions, (err, r) => {
  119. // We have connection level error
  120. if (!r && err) {
  121. return callback(err, null);
  122. }
  123. r.insertedCount = r.nInserted;
  124. r.matchedCount = r.nMatched;
  125. r.modifiedCount = r.nModified || 0;
  126. r.deletedCount = r.nRemoved;
  127. r.upsertedCount = r.getUpsertedIds().length;
  128. r.upsertedIds = {};
  129. r.insertedIds = {};
  130. // Update the n
  131. r.n = r.insertedCount;
  132. // Inserted documents
  133. const inserted = r.getInsertedIds();
  134. // Map inserted ids
  135. for (let i = 0; i < inserted.length; i++) {
  136. r.insertedIds[inserted[i].index] = inserted[i]._id;
  137. }
  138. // Upserted documents
  139. const upserted = r.getUpsertedIds();
  140. // Map upserted ids
  141. for (let i = 0; i < upserted.length; i++) {
  142. r.upsertedIds[upserted[i].index] = upserted[i]._id;
  143. }
  144. // Return the results
  145. callback(null, r);
  146. });
  147. }
  148. // Check the update operation to ensure it has atomic operators.
  149. function checkForAtomicOperators(update) {
  150. const keys = Object.keys(update);
  151. // same errors as the server would give for update doc lacking atomic operators
  152. if (keys.length === 0) {
  153. return toError('The update operation document must contain at least one atomic operator.');
  154. }
  155. if (keys[0][0] !== '$') {
  156. return toError('the update operation document must contain atomic operators.');
  157. }
  158. }
  159. /**
  160. * Count the number of documents in the collection that match the query.
  161. *
  162. * @method
  163. * @param {Collection} a Collection instance.
  164. * @param {object} query The query for the count.
  165. * @param {object} [options] Optional settings. See Collection.prototype.count for a list of options.
  166. * @param {Collection~countCallback} [callback] The command result callback
  167. */
  168. function count(coll, query, options, callback) {
  169. if (typeof options === 'function') (callback = options), (options = {});
  170. options = Object.assign({}, options);
  171. options.collectionName = coll.s.name;
  172. options.readPreference = resolveReadPreference(options, {
  173. db: coll.s.db,
  174. collection: coll
  175. });
  176. let cmd;
  177. try {
  178. cmd = buildCountCommand(coll, query, options);
  179. } catch (err) {
  180. return callback(err);
  181. }
  182. executeCommand(coll.s.db, cmd, options, (err, result) => {
  183. if (err) return handleCallback(callback, err);
  184. handleCallback(callback, null, result.n);
  185. });
  186. }
  187. function countDocuments(coll, query, options, callback) {
  188. const skip = options.skip;
  189. const limit = options.limit;
  190. options = Object.assign({}, options);
  191. const pipeline = [{ $match: query }];
  192. // Add skip and limit if defined
  193. if (typeof skip === 'number') {
  194. pipeline.push({ $skip: skip });
  195. }
  196. if (typeof limit === 'number') {
  197. pipeline.push({ $limit: limit });
  198. }
  199. pipeline.push({ $group: { _id: null, n: { $sum: 1 } } });
  200. delete options.limit;
  201. delete options.skip;
  202. coll.aggregate(pipeline, options).toArray((err, docs) => {
  203. if (err) return handleCallback(callback, err);
  204. handleCallback(callback, null, docs.length ? docs[0].n : 0);
  205. });
  206. }
  207. /**
  208. * Build the count command.
  209. *
  210. * @method
  211. * @param {collectionOrCursor} an instance of a collection or cursor
  212. * @param {object} query The query for the count.
  213. * @param {object} [options] Optional settings. See Collection.prototype.count and Cursor.prototype.count for a list of options.
  214. */
  215. function buildCountCommand(collectionOrCursor, query, options) {
  216. const skip = options.skip;
  217. const limit = options.limit;
  218. let hint = options.hint;
  219. const maxTimeMS = options.maxTimeMS;
  220. query = query || {};
  221. // Final query
  222. const cmd = {
  223. count: options.collectionName,
  224. query: query
  225. };
  226. // check if collectionOrCursor is a cursor by using cursor.s.numberOfRetries
  227. if (collectionOrCursor.s.numberOfRetries) {
  228. if (collectionOrCursor.s.options.hint) {
  229. hint = collectionOrCursor.s.options.hint;
  230. } else if (collectionOrCursor.s.cmd.hint) {
  231. hint = collectionOrCursor.s.cmd.hint;
  232. }
  233. decorateWithCollation(cmd, collectionOrCursor, collectionOrCursor.s.cmd);
  234. } else {
  235. decorateWithCollation(cmd, collectionOrCursor, options);
  236. }
  237. // Add limit, skip and maxTimeMS if defined
  238. if (typeof skip === 'number') cmd.skip = skip;
  239. if (typeof limit === 'number') cmd.limit = limit;
  240. if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
  241. if (hint) cmd.hint = hint;
  242. // Do we have a readConcern specified
  243. decorateWithReadConcern(cmd, collectionOrCursor);
  244. return cmd;
  245. }
  246. /**
  247. * Create an index on the db and collection.
  248. *
  249. * @method
  250. * @param {Collection} a Collection instance.
  251. * @param {(string|object)} fieldOrSpec Defines the index.
  252. * @param {object} [options] Optional settings. See Collection.prototype.createIndex for a list of options.
  253. * @param {Collection~resultCallback} [callback] The command result callback
  254. */
  255. function createIndex(coll, fieldOrSpec, options, callback) {
  256. createIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
  257. }
  258. /**
  259. * Create multiple indexes in the collection. This method is only supported for
  260. * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
  261. * error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
  262. *
  263. * @method
  264. * @param {Collection} a Collection instance.
  265. * @param {array} indexSpecs An array of index specifications to be created
  266. * @param {Object} [options] Optional settings. See Collection.prototype.createIndexes for a list of options.
  267. * @param {Collection~resultCallback} [callback] The command result callback
  268. */
  269. function createIndexes(coll, indexSpecs, options, callback) {
  270. const capabilities = coll.s.topology.capabilities();
  271. // Ensure we generate the correct name if the parameter is not set
  272. for (let i = 0; i < indexSpecs.length; i++) {
  273. if (indexSpecs[i].name == null) {
  274. const keys = [];
  275. // Did the user pass in a collation, check if our write server supports it
  276. if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
  277. return callback(new MongoError('server/primary/mongos does not support collation'));
  278. }
  279. for (let name in indexSpecs[i].key) {
  280. keys.push(`${name}_${indexSpecs[i].key[name]}`);
  281. }
  282. // Set the name
  283. indexSpecs[i].name = keys.join('_');
  284. }
  285. }
  286. options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
  287. // Execute the index
  288. executeCommand(
  289. coll.s.db,
  290. {
  291. createIndexes: coll.s.name,
  292. indexes: indexSpecs
  293. },
  294. options,
  295. callback
  296. );
  297. }
  298. function deleteCallback(err, r, callback) {
  299. if (callback == null) return;
  300. if (err && callback) return callback(err);
  301. if (r == null) return callback(null, { result: { ok: 1 } });
  302. r.deletedCount = r.result.n;
  303. if (callback) callback(null, r);
  304. }
  305. /**
  306. * Delete multiple documents from the collection.
  307. *
  308. * @method
  309. * @param {Collection} a Collection instance.
  310. * @param {object} filter The Filter used to select the documents to remove
  311. * @param {object} [options] Optional settings. See Collection.prototype.deleteMany for a list of options.
  312. * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
  313. */
  314. function deleteMany(coll, filter, options, callback) {
  315. options.single = false;
  316. removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
  317. }
  318. /**
  319. * Delete a single document from the collection.
  320. *
  321. * @method
  322. * @param {Collection} a Collection instance.
  323. * @param {object} filter The Filter used to select the document to remove
  324. * @param {object} [options] Optional settings. See Collection.prototype.deleteOne for a list of options.
  325. * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
  326. */
  327. function deleteOne(coll, filter, options, callback) {
  328. options.single = true;
  329. removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
  330. }
  331. /**
  332. * Return a list of distinct values for the given key across a collection.
  333. *
  334. * @method
  335. * @param {Collection} a Collection instance.
  336. * @param {string} key Field of the document to find distinct values for.
  337. * @param {object} query The query for filtering the set of documents to which we apply the distinct filter.
  338. * @param {object} [options] Optional settings. See Collection.prototype.distinct for a list of options.
  339. * @param {Collection~resultCallback} [callback] The command result callback
  340. */
  341. function distinct(coll, key, query, options, callback) {
  342. // maxTimeMS option
  343. const maxTimeMS = options.maxTimeMS;
  344. // Distinct command
  345. const cmd = {
  346. distinct: coll.s.name,
  347. key: key,
  348. query: query
  349. };
  350. options = Object.assign({}, options);
  351. // Ensure we have the right read preference inheritance
  352. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  353. // Add maxTimeMS if defined
  354. if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
  355. // Do we have a readConcern specified
  356. decorateWithReadConcern(cmd, coll, options);
  357. // Have we specified collation
  358. try {
  359. decorateWithCollation(cmd, coll, options);
  360. } catch (err) {
  361. return callback(err, null);
  362. }
  363. // Execute the command
  364. executeCommand(coll.s.db, cmd, options, (err, result) => {
  365. if (err) return handleCallback(callback, err);
  366. handleCallback(callback, null, result.values);
  367. });
  368. }
  369. /**
  370. * Drop an index from this collection.
  371. *
  372. * @method
  373. * @param {Collection} a Collection instance.
  374. * @param {string} indexName Name of the index to drop.
  375. * @param {object} [options] Optional settings. See Collection.prototype.dropIndex for a list of options.
  376. * @param {Collection~resultCallback} [callback] The command result callback
  377. */
  378. function dropIndex(coll, indexName, options, callback) {
  379. // Delete index command
  380. const cmd = { dropIndexes: coll.s.name, index: indexName };
  381. // Decorate command with writeConcern if supported
  382. applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
  383. // Execute command
  384. executeCommand(coll.s.db, cmd, options, (err, result) => {
  385. if (typeof callback !== 'function') return;
  386. if (err) return handleCallback(callback, err, null);
  387. handleCallback(callback, null, result);
  388. });
  389. }
  390. /**
  391. * Drop all indexes from this collection.
  392. *
  393. * @method
  394. * @param {Collection} a Collection instance.
  395. * @param {Object} [options] Optional settings. See Collection.prototype.dropIndexes for a list of options.
  396. * @param {Collection~resultCallback} [callback] The command result callback
  397. */
  398. function dropIndexes(coll, options, callback) {
  399. dropIndex(coll, '*', options, err => {
  400. if (err) return handleCallback(callback, err, false);
  401. handleCallback(callback, null, true);
  402. });
  403. }
  404. /**
  405. * Ensure that an index exists. If the index does not exist, this function creates it.
  406. *
  407. * @method
  408. * @param {Collection} a Collection instance.
  409. * @param {(string|object)} fieldOrSpec Defines the index.
  410. * @param {object} [options] Optional settings. See Collection.prototype.ensureIndex for a list of options.
  411. * @param {Collection~resultCallback} [callback] The command result callback
  412. */
  413. function ensureIndex(coll, fieldOrSpec, options, callback) {
  414. ensureIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
  415. }
  416. /**
  417. * Find and update a document.
  418. *
  419. * @method
  420. * @param {Collection} a Collection instance.
  421. * @param {object} query Query object to locate the object to modify.
  422. * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
  423. * @param {object} doc The fields/vals to be updated.
  424. * @param {object} [options] Optional settings. See Collection.prototype.findAndModify for a list of options.
  425. * @param {Collection~findAndModifyCallback} [callback] The command result callback
  426. * @deprecated use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
  427. */
  428. function findAndModify(coll, query, sort, doc, options, callback) {
  429. // Create findAndModify command object
  430. const queryObject = {
  431. findAndModify: coll.s.name,
  432. query: query
  433. };
  434. sort = formattedOrderClause(sort);
  435. if (sort) {
  436. queryObject.sort = sort;
  437. }
  438. queryObject.new = options.new ? true : false;
  439. queryObject.remove = options.remove ? true : false;
  440. queryObject.upsert = options.upsert ? true : false;
  441. const projection = options.projection || options.fields;
  442. if (projection) {
  443. queryObject.fields = projection;
  444. }
  445. if (options.arrayFilters) {
  446. queryObject.arrayFilters = options.arrayFilters;
  447. delete options.arrayFilters;
  448. }
  449. if (doc && !options.remove) {
  450. queryObject.update = doc;
  451. }
  452. if (options.maxTimeMS) queryObject.maxTimeMS = options.maxTimeMS;
  453. // Either use override on the function, or go back to default on either the collection
  454. // level or db
  455. options.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
  456. // No check on the documents
  457. options.checkKeys = false;
  458. // Final options for retryable writes and write concern
  459. let finalOptions = Object.assign({}, options);
  460. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  461. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  462. // Decorate the findAndModify command with the write Concern
  463. if (finalOptions.writeConcern) {
  464. queryObject.writeConcern = finalOptions.writeConcern;
  465. }
  466. // Have we specified bypassDocumentValidation
  467. if (finalOptions.bypassDocumentValidation === true) {
  468. queryObject.bypassDocumentValidation = finalOptions.bypassDocumentValidation;
  469. }
  470. finalOptions.readPreference = ReadPreference.primary;
  471. // Have we specified collation
  472. try {
  473. decorateWithCollation(queryObject, coll, finalOptions);
  474. } catch (err) {
  475. return callback(err, null);
  476. }
  477. // Execute the command
  478. executeCommand(coll.s.db, queryObject, finalOptions, (err, result) => {
  479. if (err) return handleCallback(callback, err, null);
  480. return handleCallback(callback, null, result);
  481. });
  482. }
  483. /**
  484. * Find and remove a document.
  485. *
  486. * @method
  487. * @param {Collection} a Collection instance.
  488. * @param {object} query Query object to locate the object to modify.
  489. * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
  490. * @param {object} [options] Optional settings. See Collection.prototype.findAndRemove for a list of options.
  491. * @param {Collection~resultCallback} [callback] The command result callback
  492. * @deprecated use findOneAndDelete instead
  493. */
  494. function findAndRemove(coll, query, sort, options, callback) {
  495. // Add the remove option
  496. options.remove = true;
  497. // Execute the callback
  498. findAndModify(coll, query, sort, null, options, callback);
  499. }
  500. /**
  501. * Fetch the first document that matches the query.
  502. *
  503. * @method
  504. * @param {Collection} a Collection instance.
  505. * @param {object} query Query for find Operation
  506. * @param {object} [options] Optional settings. See Collection.prototype.findOne for a list of options.
  507. * @param {Collection~resultCallback} [callback] The command result callback
  508. */
  509. function findOne(coll, query, options, callback) {
  510. const cursor = coll
  511. .find(query, options)
  512. .limit(-1)
  513. .batchSize(1);
  514. // Return the item
  515. cursor.next((err, item) => {
  516. if (err != null) return handleCallback(callback, toError(err), null);
  517. handleCallback(callback, null, item);
  518. });
  519. }
  520. /**
  521. * Find a document and delete it in one atomic operation. This requires a write lock for the duration of the operation.
  522. *
  523. * @method
  524. * @param {Collection} a Collection instance.
  525. * @param {object} filter Document selection filter.
  526. * @param {object} [options] Optional settings. See Collection.prototype.findOneAndDelete for a list of options.
  527. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  528. */
  529. function findOneAndDelete(coll, filter, options, callback) {
  530. // Final options
  531. const finalOptions = Object.assign({}, options);
  532. finalOptions.fields = options.projection;
  533. finalOptions.remove = true;
  534. // Execute find and Modify
  535. findAndModify(coll, filter, options.sort, null, finalOptions, callback);
  536. }
  537. /**
  538. * Find a document and replace it in one atomic operation. This requires a write lock for the duration of the operation.
  539. *
  540. * @method
  541. * @param {Collection} a Collection instance.
  542. * @param {object} filter Document selection filter.
  543. * @param {object} replacement Document replacing the matching document.
  544. * @param {object} [options] Optional settings. See Collection.prototype.findOneAndReplace for a list of options.
  545. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  546. */
  547. function findOneAndReplace(coll, filter, replacement, options, callback) {
  548. // Final options
  549. const finalOptions = Object.assign({}, options);
  550. finalOptions.fields = options.projection;
  551. finalOptions.update = true;
  552. finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
  553. finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;
  554. // Execute findAndModify
  555. findAndModify(coll, filter, options.sort, replacement, finalOptions, callback);
  556. }
  557. /**
  558. * Find a document and update it in one atomic operation. This requires a write lock for the duration of the operation.
  559. *
  560. * @method
  561. * @param {Collection} a Collection instance.
  562. * @param {object} filter Document selection filter.
  563. * @param {object} update Update operations to be performed on the document
  564. * @param {object} [options] Optional settings. See Collection.prototype.findOneAndUpdate for a list of options.
  565. * @param {Collection~findAndModifyCallback} [callback] The collection result callback
  566. */
  567. function findOneAndUpdate(coll, filter, update, options, callback) {
  568. // Final options
  569. const finalOptions = Object.assign({}, options);
  570. finalOptions.fields = options.projection;
  571. finalOptions.update = true;
  572. finalOptions.new = typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
  573. finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;
  574. // Execute findAndModify
  575. findAndModify(coll, filter, options.sort, update, finalOptions, callback);
  576. }
  577. /**
  578. * Execute a geo search using a geo haystack index on a collection.
  579. *
  580. * @method
  581. * @param {Collection} a Collection instance.
  582. * @param {number} x Point to search on the x axis, ensure the indexes are ordered in the same order.
  583. * @param {number} y Point to search on the y axis, ensure the indexes are ordered in the same order.
  584. * @param {object} [options] Optional settings. See Collection.prototype.geoHaystackSearch for a list of options.
  585. * @param {Collection~resultCallback} [callback] The command result callback
  586. */
  587. function geoHaystackSearch(coll, x, y, options, callback) {
  588. // Build command object
  589. let commandObject = {
  590. geoSearch: coll.s.name,
  591. near: [x, y]
  592. };
  593. // Remove read preference from hash if it exists
  594. commandObject = decorateCommand(commandObject, options, ['readPreference', 'session']);
  595. options = Object.assign({}, options);
  596. // Ensure we have the right read preference inheritance
  597. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  598. // Do we have a readConcern specified
  599. decorateWithReadConcern(commandObject, coll, options);
  600. // Execute the command
  601. executeCommand(coll.s.db, commandObject, options, (err, res) => {
  602. if (err) return handleCallback(callback, err);
  603. if (res.err || res.errmsg) handleCallback(callback, toError(res));
  604. // should we only be returning res.results here? Not sure if the user
  605. // should see the other return information
  606. handleCallback(callback, null, res);
  607. });
  608. }
  609. /**
  610. * Run a group command across a collection.
  611. *
  612. * @method
  613. * @param {Collection} a Collection instance.
  614. * @param {(object|array|function|code)} keys An object, array or function expressing the keys to group by.
  615. * @param {object} condition An optional condition that must be true for a row to be considered.
  616. * @param {object} initial Initial value of the aggregation counter object.
  617. * @param {(function|Code)} reduce The reduce function aggregates (reduces) the objects iterated
  618. * @param {(function|Code)} finalize An optional function to be run on each item in the result set just before the item is returned.
  619. * @param {boolean} command Specify if you wish to run using the internal group command or using eval, default is true.
  620. * @param {object} [options] Optional settings. See Collection.prototype.group for a list of options.
  621. * @param {Collection~resultCallback} [callback] The command result callback
  622. * @deprecated MongoDB 3.6 or higher will no longer support the group command. We recommend rewriting using the aggregation framework.
  623. */
  624. function group(coll, keys, condition, initial, reduce, finalize, command, options, callback) {
  625. // Execute using the command
  626. if (command) {
  627. const reduceFunction = reduce && reduce._bsontype === 'Code' ? reduce : new Code(reduce);
  628. const selector = {
  629. group: {
  630. ns: coll.s.name,
  631. $reduce: reduceFunction,
  632. cond: condition,
  633. initial: initial,
  634. out: 'inline'
  635. }
  636. };
  637. // if finalize is defined
  638. if (finalize != null) selector.group['finalize'] = finalize;
  639. // Set up group selector
  640. if ('function' === typeof keys || (keys && keys._bsontype === 'Code')) {
  641. selector.group.$keyf = keys && keys._bsontype === 'Code' ? keys : new Code(keys);
  642. } else {
  643. const hash = {};
  644. keys.forEach(key => {
  645. hash[key] = 1;
  646. });
  647. selector.group.key = hash;
  648. }
  649. options = Object.assign({}, options);
  650. // Ensure we have the right read preference inheritance
  651. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  652. // Do we have a readConcern specified
  653. decorateWithReadConcern(selector, coll, options);
  654. // Have we specified collation
  655. try {
  656. decorateWithCollation(selector, coll, options);
  657. } catch (err) {
  658. return callback(err, null);
  659. }
  660. // Execute command
  661. executeCommand(coll.s.db, selector, options, (err, result) => {
  662. if (err) return handleCallback(callback, err, null);
  663. handleCallback(callback, null, result.retval);
  664. });
  665. } else {
  666. // Create execution scope
  667. const scope = reduce != null && reduce._bsontype === 'Code' ? reduce.scope : {};
  668. scope.ns = coll.s.name;
  669. scope.keys = keys;
  670. scope.condition = condition;
  671. scope.initial = initial;
  672. // Pass in the function text to execute within mongodb.
  673. const groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
  674. evaluate(coll.s.db, new Code(groupfn, scope), null, options, (err, results) => {
  675. if (err) return handleCallback(callback, err, null);
  676. handleCallback(callback, null, results.result || results);
  677. });
  678. }
  679. }
  680. /**
  681. * Retrieve all the indexes on the collection.
  682. *
  683. * @method
  684. * @param {Collection} a Collection instance.
  685. * @param {Object} [options] Optional settings. See Collection.prototype.indexes for a list of options.
  686. * @param {Collection~resultCallback} [callback] The command result callback
  687. */
  688. function indexes(coll, options, callback) {
  689. options = Object.assign({}, { full: true }, options);
  690. indexInformationDb(coll.s.db, coll.s.name, options, callback);
  691. }
  692. /**
  693. * Check if one or more indexes exist on the collection. This fails on the first index that doesn't exist.
  694. *
  695. * @method
  696. * @param {Collection} a Collection instance.
  697. * @param {(string|array)} indexes One or more index names to check.
  698. * @param {Object} [options] Optional settings. See Collection.prototype.indexExists for a list of options.
  699. * @param {Collection~resultCallback} [callback] The command result callback
  700. */
  701. function indexExists(coll, indexes, options, callback) {
  702. indexInformation(coll, options, (err, indexInformation) => {
  703. // If we have an error return
  704. if (err != null) return handleCallback(callback, err, null);
  705. // Let's check for the index names
  706. if (!Array.isArray(indexes))
  707. return handleCallback(callback, null, indexInformation[indexes] != null);
  708. // Check in list of indexes
  709. for (let i = 0; i < indexes.length; i++) {
  710. if (indexInformation[indexes[i]] == null) {
  711. return handleCallback(callback, null, false);
  712. }
  713. }
  714. // All keys found return true
  715. return handleCallback(callback, null, true);
  716. });
  717. }
  718. /**
  719. * Retrieve this collection's index info.
  720. *
  721. * @method
  722. * @param {Collection} a Collection instance.
  723. * @param {object} [options] Optional settings. See Collection.prototype.indexInformation for a list of options.
  724. * @param {Collection~resultCallback} [callback] The command result callback
  725. */
  726. function indexInformation(coll, options, callback) {
  727. indexInformationDb(coll.s.db, coll.s.name, options, callback);
  728. }
  729. function insertDocuments(coll, docs, options, callback) {
  730. if (typeof options === 'function') (callback = options), (options = {});
  731. options = options || {};
  732. // Ensure we are operating on an array op docs
  733. docs = Array.isArray(docs) ? docs : [docs];
  734. // Final options for retryable writes and write concern
  735. let finalOptions = Object.assign({}, options);
  736. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  737. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  738. // If keep going set unordered
  739. if (finalOptions.keepGoing === true) finalOptions.ordered = false;
  740. finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
  741. docs = prepareDocs(coll, docs, options);
  742. // File inserts
  743. coll.s.topology.insert(coll.s.namespace, docs, finalOptions, (err, result) => {
  744. if (callback == null) return;
  745. if (err) return handleCallback(callback, err);
  746. if (result == null) return handleCallback(callback, null, null);
  747. if (result.result.code) return handleCallback(callback, toError(result.result));
  748. if (result.result.writeErrors)
  749. return handleCallback(callback, toError(result.result.writeErrors[0]));
  750. // Add docs to the list
  751. result.ops = docs;
  752. // Return the results
  753. handleCallback(callback, null, result);
  754. });
  755. }
  756. /**
  757. * Insert a single document into the collection. See Collection.prototype.insertOne for more information.
  758. *
  759. * @method
  760. * @param {Collection} a Collection instance.
  761. * @param {object} doc Document to insert.
  762. * @param {object} [options] Optional settings. See Collection.prototype.insertOne for a list of options.
  763. * @param {Collection~insertOneWriteOpCallback} [callback] The command result callback
  764. */
  765. function insertOne(coll, doc, options, callback) {
  766. if (Array.isArray(doc)) {
  767. return callback(
  768. MongoError.create({ message: 'doc parameter must be an object', driver: true })
  769. );
  770. }
  771. insertDocuments(coll, [doc], options, (err, r) => {
  772. if (callback == null) return;
  773. if (err && callback) return callback(err);
  774. // Workaround for pre 2.6 servers
  775. if (r == null) return callback(null, { result: { ok: 1 } });
  776. // Add values to top level to ensure crud spec compatibility
  777. r.insertedCount = r.result.n;
  778. r.insertedId = doc._id;
  779. if (callback) callback(null, r);
  780. });
  781. }
  782. /**
  783. * Determine whether the collection is a capped collection.
  784. *
  785. * @method
  786. * @param {Collection} a Collection instance.
  787. * @param {Object} [options] Optional settings. See Collection.prototype.isCapped for a list of options.
  788. * @param {Collection~resultCallback} [callback] The results callback
  789. */
  790. function isCapped(coll, options, callback) {
  791. optionsOp(coll, options, (err, document) => {
  792. if (err) return handleCallback(callback, err);
  793. handleCallback(callback, null, !!(document && document.capped));
  794. });
  795. }
  796. /**
  797. * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
  798. *
  799. * @method
  800. * @param {Collection} a Collection instance.
  801. * @param {(function|string)} map The mapping function.
  802. * @param {(function|string)} reduce The reduce function.
  803. * @param {object} [options] Optional settings. See Collection.prototype.mapReduce for a list of options.
  804. * @param {Collection~resultCallback} [callback] The command result callback
  805. */
  806. function mapReduce(coll, map, reduce, options, callback) {
  807. const mapCommandHash = {
  808. mapreduce: coll.s.name,
  809. map: map,
  810. reduce: reduce
  811. };
  812. // Exclusion list
  813. const exclusionList = ['readPreference', 'session', 'bypassDocumentValidation'];
  814. // Add any other options passed in
  815. for (let n in options) {
  816. if ('scope' === n) {
  817. mapCommandHash[n] = processScope(options[n]);
  818. } else {
  819. // Only include if not in exclusion list
  820. if (exclusionList.indexOf(n) === -1) {
  821. mapCommandHash[n] = options[n];
  822. }
  823. }
  824. }
  825. options = Object.assign({}, options);
  826. // Ensure we have the right read preference inheritance
  827. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  828. // If we have a read preference and inline is not set as output fail hard
  829. if (
  830. options.readPreference !== false &&
  831. options.readPreference !== 'primary' &&
  832. options['out'] &&
  833. (options['out'].inline !== 1 && options['out'] !== 'inline')
  834. ) {
  835. // Force readPreference to primary
  836. options.readPreference = 'primary';
  837. // Decorate command with writeConcern if supported
  838. applyWriteConcern(mapCommandHash, { db: coll.s.db, collection: coll }, options);
  839. } else {
  840. decorateWithReadConcern(mapCommandHash, coll, options);
  841. }
  842. // Is bypassDocumentValidation specified
  843. if (options.bypassDocumentValidation === true) {
  844. mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
  845. }
  846. // Have we specified collation
  847. try {
  848. decorateWithCollation(mapCommandHash, coll, options);
  849. } catch (err) {
  850. return callback(err, null);
  851. }
  852. // Execute command
  853. executeCommand(coll.s.db, mapCommandHash, options, (err, result) => {
  854. if (err) return handleCallback(callback, err);
  855. // Check if we have an error
  856. if (1 !== result.ok || result.err || result.errmsg) {
  857. return handleCallback(callback, toError(result));
  858. }
  859. // Create statistics value
  860. const stats = {};
  861. if (result.timeMillis) stats['processtime'] = result.timeMillis;
  862. if (result.counts) stats['counts'] = result.counts;
  863. if (result.timing) stats['timing'] = result.timing;
  864. // invoked with inline?
  865. if (result.results) {
  866. // If we wish for no verbosity
  867. if (options['verbose'] == null || !options['verbose']) {
  868. return handleCallback(callback, null, result.results);
  869. }
  870. return handleCallback(callback, null, { results: result.results, stats: stats });
  871. }
  872. // The returned collection
  873. let collection = null;
  874. // If we have an object it's a different db
  875. if (result.result != null && typeof result.result === 'object') {
  876. const doc = result.result;
  877. // Return a collection from another db
  878. let Db = loadDb();
  879. collection = new Db(doc.db, coll.s.db.s.topology, coll.s.db.s.options).collection(
  880. doc.collection
  881. );
  882. } else {
  883. // Create a collection object that wraps the result collection
  884. collection = coll.s.db.collection(result.result);
  885. }
  886. // If we wish for no verbosity
  887. if (options['verbose'] == null || !options['verbose']) {
  888. return handleCallback(callback, err, collection);
  889. }
  890. // Return stats as third set of values
  891. handleCallback(callback, err, { collection: collection, stats: stats });
  892. });
  893. }
  894. /**
  895. * Return the options of the collection.
  896. *
  897. * @method
  898. * @param {Collection} a Collection instance.
  899. * @param {Object} [options] Optional settings. See Collection.prototype.options for a list of options.
  900. * @param {Collection~resultCallback} [callback] The results callback
  901. */
  902. function optionsOp(coll, opts, callback) {
  903. coll.s.db.listCollections({ name: coll.s.name }, opts).toArray((err, collections) => {
  904. if (err) return handleCallback(callback, err);
  905. if (collections.length === 0) {
  906. return handleCallback(
  907. callback,
  908. MongoError.create({ message: `collection ${coll.s.namespace} not found`, driver: true })
  909. );
  910. }
  911. handleCallback(callback, err, collections[0].options || null);
  912. });
  913. }
  914. /**
  915. * Return N parallel cursors for a collection to allow parallel reading of the entire collection. There are
  916. * no ordering guarantees for returned results.
  917. *
  918. * @method
  919. * @param {Collection} a Collection instance.
  920. * @param {object} [options] Optional settings. See Collection.prototype.parallelCollectionScan for a list of options.
  921. * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
  922. */
  923. function parallelCollectionScan(coll, options, callback) {
  924. // Create command object
  925. const commandObject = {
  926. parallelCollectionScan: coll.s.name,
  927. numCursors: options.numCursors
  928. };
  929. // Do we have a readConcern specified
  930. decorateWithReadConcern(commandObject, coll, options);
  931. // Store the raw value
  932. const raw = options.raw;
  933. delete options['raw'];
  934. // Execute the command
  935. executeCommand(coll.s.db, commandObject, options, (err, result) => {
  936. if (err) return handleCallback(callback, err, null);
  937. if (result == null)
  938. return handleCallback(
  939. callback,
  940. new Error('no result returned for parallelCollectionScan'),
  941. null
  942. );
  943. options = Object.assign({ explicitlyIgnoreSession: true }, options);
  944. const cursors = [];
  945. // Add the raw back to the option
  946. if (raw) options.raw = raw;
  947. // Create command cursors for each item
  948. for (let i = 0; i < result.cursors.length; i++) {
  949. const rawId = result.cursors[i].cursor.id;
  950. // Convert cursorId to Long if needed
  951. const cursorId = typeof rawId === 'number' ? Long.fromNumber(rawId) : rawId;
  952. // Add a command cursor
  953. cursors.push(coll.s.topology.cursor(coll.s.namespace, cursorId, options));
  954. }
  955. handleCallback(callback, null, cursors);
  956. });
  957. }
  958. // modifies documents before being inserted or updated
  959. function prepareDocs(coll, docs, options) {
  960. const forceServerObjectId =
  961. typeof options.forceServerObjectId === 'boolean'
  962. ? options.forceServerObjectId
  963. : coll.s.db.options.forceServerObjectId;
  964. // no need to modify the docs if server sets the ObjectId
  965. if (forceServerObjectId === true) {
  966. return docs;
  967. }
  968. return docs.map(doc => {
  969. if (forceServerObjectId !== true && doc._id == null) {
  970. doc._id = coll.s.pkFactory.createPk();
  971. }
  972. return doc;
  973. });
  974. }
  975. /**
  976. * Functions that are passed as scope args must
  977. * be converted to Code instances.
  978. * @ignore
  979. */
  980. function processScope(scope) {
  981. if (!isObject(scope) || scope._bsontype === 'ObjectID') {
  982. return scope;
  983. }
  984. const keys = Object.keys(scope);
  985. let key;
  986. const new_scope = {};
  987. for (let i = keys.length - 1; i >= 0; i--) {
  988. key = keys[i];
  989. if ('function' === typeof scope[key]) {
  990. new_scope[key] = new Code(String(scope[key]));
  991. } else {
  992. new_scope[key] = processScope(scope[key]);
  993. }
  994. }
  995. return new_scope;
  996. }
  997. /**
  998. * Reindex all indexes on the collection.
  999. *
  1000. * @method
  1001. * @param {Collection} a Collection instance.
  1002. * @param {Object} [options] Optional settings. See Collection.prototype.reIndex for a list of options.
  1003. * @param {Collection~resultCallback} [callback] The command result callback
  1004. */
  1005. function reIndex(coll, options, callback) {
  1006. // Reindex
  1007. const cmd = { reIndex: coll.s.name };
  1008. // Execute the command
  1009. executeCommand(coll.s.db, cmd, options, (err, result) => {
  1010. if (callback == null) return;
  1011. if (err) return handleCallback(callback, err, null);
  1012. handleCallback(callback, null, result.ok ? true : false);
  1013. });
  1014. }
  1015. function removeDocuments(coll, selector, options, callback) {
  1016. if (typeof options === 'function') {
  1017. (callback = options), (options = {});
  1018. } else if (typeof selector === 'function') {
  1019. callback = selector;
  1020. options = {};
  1021. selector = {};
  1022. }
  1023. // Create an empty options object if the provided one is null
  1024. options = options || {};
  1025. // Final options for retryable writes and write concern
  1026. let finalOptions = Object.assign({}, options);
  1027. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  1028. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  1029. // If selector is null set empty
  1030. if (selector == null) selector = {};
  1031. // Build the op
  1032. const op = { q: selector, limit: 0 };
  1033. if (options.single) {
  1034. op.limit = 1;
  1035. } else if (finalOptions.retryWrites) {
  1036. finalOptions.retryWrites = false;
  1037. }
  1038. // Have we specified collation
  1039. try {
  1040. decorateWithCollation(finalOptions, coll, options);
  1041. } catch (err) {
  1042. return callback(err, null);
  1043. }
  1044. // Execute the remove
  1045. coll.s.topology.remove(coll.s.namespace, [op], finalOptions, (err, result) => {
  1046. if (callback == null) return;
  1047. if (err) return handleCallback(callback, err, null);
  1048. if (result == null) return handleCallback(callback, null, null);
  1049. if (result.result.code) return handleCallback(callback, toError(result.result));
  1050. if (result.result.writeErrors)
  1051. return handleCallback(callback, toError(result.result.writeErrors[0]));
  1052. // Return the results
  1053. handleCallback(callback, null, result);
  1054. });
  1055. }
  1056. /**
  1057. * Rename the collection.
  1058. *
  1059. * @method
  1060. * @param {Collection} a Collection instance.
  1061. * @param {string} newName New name of of the collection.
  1062. * @param {object} [options] Optional settings. See Collection.prototype.rename for a list of options.
  1063. * @param {Collection~collectionResultCallback} [callback] The results callback
  1064. */
  1065. function rename(coll, newName, options, callback) {
  1066. let Collection = loadCollection();
  1067. // Check the collection name
  1068. checkCollectionName(newName);
  1069. // Build the command
  1070. const renameCollection = `${coll.s.dbName}.${coll.s.name}`;
  1071. const toCollection = `${coll.s.dbName}.${newName}`;
  1072. const dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
  1073. const cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
  1074. // Decorate command with writeConcern if supported
  1075. applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
  1076. // Execute against admin
  1077. executeDbAdminCommand(coll.s.db.admin().s.db, cmd, options, (err, doc) => {
  1078. if (err) return handleCallback(callback, err, null);
  1079. // We have an error
  1080. if (doc.errmsg) return handleCallback(callback, toError(doc), null);
  1081. try {
  1082. return handleCallback(
  1083. callback,
  1084. null,
  1085. new Collection(
  1086. coll.s.db,
  1087. coll.s.topology,
  1088. coll.s.dbName,
  1089. newName,
  1090. coll.s.pkFactory,
  1091. coll.s.options
  1092. )
  1093. );
  1094. } catch (err) {
  1095. return handleCallback(callback, toError(err), null);
  1096. }
  1097. });
  1098. }
  1099. /**
  1100. * Replace a document in the collection.
  1101. *
  1102. * @method
  1103. * @param {Collection} a Collection instance.
  1104. * @param {object} filter The Filter used to select the document to update
  1105. * @param {object} doc The Document that replaces the matching document
  1106. * @param {object} [options] Optional settings. See Collection.prototype.replaceOne for a list of options.
  1107. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1108. */
  1109. function replaceOne(coll, filter, doc, options, callback) {
  1110. // Set single document update
  1111. options.multi = false;
  1112. // Execute update
  1113. updateDocuments(coll, filter, doc, options, (err, r) => {
  1114. if (callback == null) return;
  1115. if (err && callback) return callback(err);
  1116. if (r == null) return callback(null, { result: { ok: 1 } });
  1117. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  1118. r.upsertedId =
  1119. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  1120. ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
  1121. : null;
  1122. r.upsertedCount =
  1123. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  1124. r.matchedCount =
  1125. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  1126. r.ops = [doc];
  1127. if (callback) callback(null, r);
  1128. });
  1129. }
  1130. /**
  1131. * Save a document.
  1132. *
  1133. * @method
  1134. * @param {Collection} a Collection instance.
  1135. * @param {object} doc Document to save
  1136. * @param {object} [options] Optional settings. See Collection.prototype.save for a list of options.
  1137. * @param {Collection~writeOpCallback} [callback] The command result callback
  1138. * @deprecated use insertOne, insertMany, updateOne or updateMany
  1139. */
  1140. function save(coll, doc, options, callback) {
  1141. // Get the write concern options
  1142. const finalOptions = applyWriteConcern(
  1143. Object.assign({}, options),
  1144. { db: coll.s.db, collection: coll },
  1145. options
  1146. );
  1147. // Establish if we need to perform an insert or update
  1148. if (doc._id != null) {
  1149. finalOptions.upsert = true;
  1150. return updateDocuments(coll, { _id: doc._id }, doc, finalOptions, callback);
  1151. }
  1152. // Insert the document
  1153. insertDocuments(coll, [doc], finalOptions, (err, result) => {
  1154. if (callback == null) return;
  1155. if (doc == null) return handleCallback(callback, null, null);
  1156. if (err) return handleCallback(callback, err, null);
  1157. handleCallback(callback, null, result);
  1158. });
  1159. }
  1160. /**
  1161. * Get all the collection statistics.
  1162. *
  1163. * @method
  1164. * @param {Collection} a Collection instance.
  1165. * @param {object} [options] Optional settings. See Collection.prototype.stats for a list of options.
  1166. * @param {Collection~resultCallback} [callback] The collection result callback
  1167. */
  1168. function stats(coll, options, callback) {
  1169. // Build command object
  1170. const commandObject = {
  1171. collStats: coll.s.name
  1172. };
  1173. // Check if we have the scale value
  1174. if (options['scale'] != null) commandObject['scale'] = options['scale'];
  1175. options = Object.assign({}, options);
  1176. // Ensure we have the right read preference inheritance
  1177. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  1178. // Execute the command
  1179. executeCommand(coll.s.db, commandObject, options, callback);
  1180. }
  1181. function updateCallback(err, r, callback) {
  1182. if (callback == null) return;
  1183. if (err) return callback(err);
  1184. if (r == null) return callback(null, { result: { ok: 1 } });
  1185. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  1186. r.upsertedId =
  1187. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  1188. ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
  1189. : null;
  1190. r.upsertedCount =
  1191. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  1192. r.matchedCount =
  1193. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  1194. callback(null, r);
  1195. }
  1196. function updateDocuments(coll, selector, document, options, callback) {
  1197. if ('function' === typeof options) (callback = options), (options = null);
  1198. if (options == null) options = {};
  1199. if (!('function' === typeof callback)) callback = null;
  1200. // If we are not providing a selector or document throw
  1201. if (selector == null || typeof selector !== 'object')
  1202. return callback(toError('selector must be a valid JavaScript object'));
  1203. if (document == null || typeof document !== 'object')
  1204. return callback(toError('document must be a valid JavaScript object'));
  1205. // Final options for retryable writes and write concern
  1206. let finalOptions = Object.assign({}, options);
  1207. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  1208. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  1209. // Do we return the actual result document
  1210. // Either use override on the function, or go back to default on either the collection
  1211. // level or db
  1212. finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
  1213. // Execute the operation
  1214. const op = { q: selector, u: document };
  1215. op.upsert = options.upsert !== void 0 ? !!options.upsert : false;
  1216. op.multi = options.multi !== void 0 ? !!options.multi : false;
  1217. if (finalOptions.arrayFilters) {
  1218. op.arrayFilters = finalOptions.arrayFilters;
  1219. delete finalOptions.arrayFilters;
  1220. }
  1221. if (finalOptions.retryWrites && op.multi) {
  1222. finalOptions.retryWrites = false;
  1223. }
  1224. // Have we specified collation
  1225. try {
  1226. decorateWithCollation(finalOptions, coll, options);
  1227. } catch (err) {
  1228. return callback(err, null);
  1229. }
  1230. // Update options
  1231. coll.s.topology.update(coll.s.namespace, [op], finalOptions, (err, result) => {
  1232. if (callback == null) return;
  1233. if (err) return handleCallback(callback, err, null);
  1234. if (result == null) return handleCallback(callback, null, null);
  1235. if (result.result.code) return handleCallback(callback, toError(result.result));
  1236. if (result.result.writeErrors)
  1237. return handleCallback(callback, toError(result.result.writeErrors[0]));
  1238. // Return the results
  1239. handleCallback(callback, null, result);
  1240. });
  1241. }
  1242. /**
  1243. * Update multiple documents in the collection.
  1244. *
  1245. * @method
  1246. * @param {Collection} a Collection instance.
  1247. * @param {object} filter The Filter used to select the documents to update
  1248. * @param {object} update The update operations to be applied to the document
  1249. * @param {object} [options] Optional settings. See Collection.prototype.updateMany for a list of options.
  1250. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1251. */
  1252. function updateMany(coll, filter, update, options, callback) {
  1253. // Set single document update
  1254. options.multi = true;
  1255. // Execute update
  1256. updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
  1257. }
  1258. /**
  1259. * Update a single document in the collection.
  1260. *
  1261. * @method
  1262. * @param {Collection} a Collection instance.
  1263. * @param {object} filter The Filter used to select the document to update
  1264. * @param {object} update The update operations to be applied to the document
  1265. * @param {object} [options] Optional settings. See Collection.prototype.updateOne for a list of options.
  1266. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1267. */
  1268. function updateOne(coll, filter, update, options, callback) {
  1269. // Set single document update
  1270. options.multi = false;
  1271. // Execute update
  1272. updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
  1273. }
  1274. module.exports = {
  1275. bulkWrite,
  1276. checkForAtomicOperators,
  1277. count,
  1278. countDocuments,
  1279. buildCountCommand,
  1280. createIndex,
  1281. createIndexes,
  1282. deleteMany,
  1283. deleteOne,
  1284. distinct,
  1285. dropIndex,
  1286. dropIndexes,
  1287. ensureIndex,
  1288. findAndModify,
  1289. findAndRemove,
  1290. findOne,
  1291. findOneAndDelete,
  1292. findOneAndReplace,
  1293. findOneAndUpdate,
  1294. geoHaystackSearch,
  1295. group,
  1296. indexes,
  1297. indexExists,
  1298. indexInformation,
  1299. insertOne,
  1300. isCapped,
  1301. mapReduce,
  1302. optionsOp,
  1303. parallelCollectionScan,
  1304. prepareDocs,
  1305. reIndex,
  1306. removeDocuments,
  1307. rename,
  1308. replaceOne,
  1309. save,
  1310. stats,
  1311. updateDocuments,
  1312. updateMany,
  1313. updateOne
  1314. };