collection_ops.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  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 ignoreUndefined
  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: 1, 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. * Inserts an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
  784. * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
  785. * can be overridden by setting the **forceServerObjectId** flag.
  786. *
  787. * @method
  788. * @param {Collection} a Collection instance.
  789. * @param {object[]} docs Documents to insert.
  790. * @param {number} [options] Optional settings. See Collection.prototype.insertMany for a list of options.
  791. * @param {Collection~insertWriteOpCallback} [callback] The command result callback
  792. */
  793. function insertMany(coll, docs, options, callback) {
  794. if (!Array.isArray(docs)) {
  795. return callback(
  796. MongoError.create({ message: 'docs parameter must be an array of documents', driver: true })
  797. );
  798. }
  799. // If keep going set unordered
  800. options['serializeFunctions'] = options['serializeFunctions'] || coll.s.serializeFunctions;
  801. docs = prepareDocs(coll, docs, options);
  802. // Generate the bulk write operations
  803. const operations = [
  804. {
  805. insertMany: docs
  806. }
  807. ];
  808. bulkWrite(coll, operations, options, (err, result) => {
  809. if (err) return callback(err, null);
  810. callback(null, mapInsertManyResults(docs, result));
  811. });
  812. }
  813. function mapInsertManyResults(docs, r) {
  814. const finalResult = {
  815. result: { ok: 1, n: r.insertedCount },
  816. ops: docs,
  817. insertedCount: r.insertedCount,
  818. insertedIds: r.insertedIds
  819. };
  820. if (r.getLastOp()) {
  821. finalResult.result.opTime = r.getLastOp();
  822. }
  823. return finalResult;
  824. }
  825. /**
  826. * Determine whether the collection is a capped collection.
  827. *
  828. * @method
  829. * @param {Collection} a Collection instance.
  830. * @param {Object} [options] Optional settings. See Collection.prototype.isCapped for a list of options.
  831. * @param {Collection~resultCallback} [callback] The results callback
  832. */
  833. function isCapped(coll, options, callback) {
  834. optionsOp(coll, options, (err, document) => {
  835. if (err) return handleCallback(callback, err);
  836. handleCallback(callback, null, !!(document && document.capped));
  837. });
  838. }
  839. /**
  840. * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
  841. *
  842. * @method
  843. * @param {Collection} a Collection instance.
  844. * @param {(function|string)} map The mapping function.
  845. * @param {(function|string)} reduce The reduce function.
  846. * @param {object} [options] Optional settings. See Collection.prototype.mapReduce for a list of options.
  847. * @param {Collection~resultCallback} [callback] The command result callback
  848. */
  849. function mapReduce(coll, map, reduce, options, callback) {
  850. const mapCommandHash = {
  851. mapreduce: coll.s.name,
  852. map: map,
  853. reduce: reduce
  854. };
  855. // Exclusion list
  856. const exclusionList = ['readPreference', 'session', 'bypassDocumentValidation'];
  857. // Add any other options passed in
  858. for (let n in options) {
  859. if ('scope' === n) {
  860. mapCommandHash[n] = processScope(options[n]);
  861. } else {
  862. // Only include if not in exclusion list
  863. if (exclusionList.indexOf(n) === -1) {
  864. mapCommandHash[n] = options[n];
  865. }
  866. }
  867. }
  868. options = Object.assign({}, options);
  869. // Ensure we have the right read preference inheritance
  870. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  871. // If we have a read preference and inline is not set as output fail hard
  872. if (
  873. options.readPreference !== false &&
  874. options.readPreference !== 'primary' &&
  875. options['out'] &&
  876. (options['out'].inline !== 1 && options['out'] !== 'inline')
  877. ) {
  878. // Force readPreference to primary
  879. options.readPreference = 'primary';
  880. // Decorate command with writeConcern if supported
  881. applyWriteConcern(mapCommandHash, { db: coll.s.db, collection: coll }, options);
  882. } else {
  883. decorateWithReadConcern(mapCommandHash, coll, options);
  884. }
  885. // Is bypassDocumentValidation specified
  886. if (options.bypassDocumentValidation === true) {
  887. mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
  888. }
  889. // Have we specified collation
  890. try {
  891. decorateWithCollation(mapCommandHash, coll, options);
  892. } catch (err) {
  893. return callback(err, null);
  894. }
  895. // Execute command
  896. executeCommand(coll.s.db, mapCommandHash, options, (err, result) => {
  897. if (err) return handleCallback(callback, err);
  898. // Check if we have an error
  899. if (1 !== result.ok || result.err || result.errmsg) {
  900. return handleCallback(callback, toError(result));
  901. }
  902. // Create statistics value
  903. const stats = {};
  904. if (result.timeMillis) stats['processtime'] = result.timeMillis;
  905. if (result.counts) stats['counts'] = result.counts;
  906. if (result.timing) stats['timing'] = result.timing;
  907. // invoked with inline?
  908. if (result.results) {
  909. // If we wish for no verbosity
  910. if (options['verbose'] == null || !options['verbose']) {
  911. return handleCallback(callback, null, result.results);
  912. }
  913. return handleCallback(callback, null, { results: result.results, stats: stats });
  914. }
  915. // The returned collection
  916. let collection = null;
  917. // If we have an object it's a different db
  918. if (result.result != null && typeof result.result === 'object') {
  919. const doc = result.result;
  920. // Return a collection from another db
  921. let Db = loadDb();
  922. collection = new Db(doc.db, coll.s.db.s.topology, coll.s.db.s.options).collection(
  923. doc.collection
  924. );
  925. } else {
  926. // Create a collection object that wraps the result collection
  927. collection = coll.s.db.collection(result.result);
  928. }
  929. // If we wish for no verbosity
  930. if (options['verbose'] == null || !options['verbose']) {
  931. return handleCallback(callback, err, collection);
  932. }
  933. // Return stats as third set of values
  934. handleCallback(callback, err, { collection: collection, stats: stats });
  935. });
  936. }
  937. /**
  938. * Return the options of the collection.
  939. *
  940. * @method
  941. * @param {Collection} a Collection instance.
  942. * @param {Object} [options] Optional settings. See Collection.prototype.options for a list of options.
  943. * @param {Collection~resultCallback} [callback] The results callback
  944. */
  945. function optionsOp(coll, opts, callback) {
  946. coll.s.db.listCollections({ name: coll.s.name }, opts).toArray((err, collections) => {
  947. if (err) return handleCallback(callback, err);
  948. if (collections.length === 0) {
  949. return handleCallback(
  950. callback,
  951. MongoError.create({ message: `collection ${coll.s.namespace} not found`, driver: true })
  952. );
  953. }
  954. handleCallback(callback, err, collections[0].options || null);
  955. });
  956. }
  957. /**
  958. * Return N parallel cursors for a collection to allow parallel reading of the entire collection. There are
  959. * no ordering guarantees for returned results.
  960. *
  961. * @method
  962. * @param {Collection} a Collection instance.
  963. * @param {object} [options] Optional settings. See Collection.prototype.parallelCollectionScan for a list of options.
  964. * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
  965. */
  966. function parallelCollectionScan(coll, options, callback) {
  967. // Create command object
  968. const commandObject = {
  969. parallelCollectionScan: coll.s.name,
  970. numCursors: options.numCursors
  971. };
  972. // Do we have a readConcern specified
  973. decorateWithReadConcern(commandObject, coll, options);
  974. // Store the raw value
  975. const raw = options.raw;
  976. delete options['raw'];
  977. // Execute the command
  978. executeCommand(coll.s.db, commandObject, options, (err, result) => {
  979. if (err) return handleCallback(callback, err, null);
  980. if (result == null)
  981. return handleCallback(
  982. callback,
  983. new Error('no result returned for parallelCollectionScan'),
  984. null
  985. );
  986. options = Object.assign({ explicitlyIgnoreSession: true }, options);
  987. const cursors = [];
  988. // Add the raw back to the option
  989. if (raw) options.raw = raw;
  990. // Create command cursors for each item
  991. for (let i = 0; i < result.cursors.length; i++) {
  992. const rawId = result.cursors[i].cursor.id;
  993. // Convert cursorId to Long if needed
  994. const cursorId = typeof rawId === 'number' ? Long.fromNumber(rawId) : rawId;
  995. // Add a command cursor
  996. cursors.push(coll.s.topology.cursor(coll.s.namespace, cursorId, options));
  997. }
  998. handleCallback(callback, null, cursors);
  999. });
  1000. }
  1001. // modifies documents before being inserted or updated
  1002. function prepareDocs(coll, docs, options) {
  1003. const forceServerObjectId =
  1004. typeof options.forceServerObjectId === 'boolean'
  1005. ? options.forceServerObjectId
  1006. : coll.s.db.options.forceServerObjectId;
  1007. // no need to modify the docs if server sets the ObjectId
  1008. if (forceServerObjectId === true) {
  1009. return docs;
  1010. }
  1011. return docs.map(doc => {
  1012. if (forceServerObjectId !== true && doc._id == null) {
  1013. doc._id = coll.s.pkFactory.createPk();
  1014. }
  1015. return doc;
  1016. });
  1017. }
  1018. /**
  1019. * Functions that are passed as scope args must
  1020. * be converted to Code instances.
  1021. * @ignore
  1022. */
  1023. function processScope(scope) {
  1024. if (!isObject(scope) || scope._bsontype === 'ObjectID') {
  1025. return scope;
  1026. }
  1027. const keys = Object.keys(scope);
  1028. let key;
  1029. const new_scope = {};
  1030. for (let i = keys.length - 1; i >= 0; i--) {
  1031. key = keys[i];
  1032. if ('function' === typeof scope[key]) {
  1033. new_scope[key] = new Code(String(scope[key]));
  1034. } else {
  1035. new_scope[key] = processScope(scope[key]);
  1036. }
  1037. }
  1038. return new_scope;
  1039. }
  1040. /**
  1041. * Reindex all indexes on the collection.
  1042. *
  1043. * @method
  1044. * @param {Collection} a Collection instance.
  1045. * @param {Object} [options] Optional settings. See Collection.prototype.reIndex for a list of options.
  1046. * @param {Collection~resultCallback} [callback] The command result callback
  1047. */
  1048. function reIndex(coll, options, callback) {
  1049. // Reindex
  1050. const cmd = { reIndex: coll.s.name };
  1051. // Execute the command
  1052. executeCommand(coll.s.db, cmd, options, (err, result) => {
  1053. if (callback == null) return;
  1054. if (err) return handleCallback(callback, err, null);
  1055. handleCallback(callback, null, result.ok ? true : false);
  1056. });
  1057. }
  1058. function removeDocuments(coll, selector, options, callback) {
  1059. if (typeof options === 'function') {
  1060. (callback = options), (options = {});
  1061. } else if (typeof selector === 'function') {
  1062. callback = selector;
  1063. options = {};
  1064. selector = {};
  1065. }
  1066. // Create an empty options object if the provided one is null
  1067. options = options || {};
  1068. // Final options for retryable writes and write concern
  1069. let finalOptions = Object.assign({}, options);
  1070. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  1071. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  1072. // If selector is null set empty
  1073. if (selector == null) selector = {};
  1074. // Build the op
  1075. const op = { q: selector, limit: 0 };
  1076. if (options.single) {
  1077. op.limit = 1;
  1078. } else if (finalOptions.retryWrites) {
  1079. finalOptions.retryWrites = false;
  1080. }
  1081. // Have we specified collation
  1082. try {
  1083. decorateWithCollation(finalOptions, coll, options);
  1084. } catch (err) {
  1085. return callback(err, null);
  1086. }
  1087. // Execute the remove
  1088. coll.s.topology.remove(coll.s.namespace, [op], finalOptions, (err, result) => {
  1089. if (callback == null) return;
  1090. if (err) return handleCallback(callback, err, null);
  1091. if (result == null) return handleCallback(callback, null, null);
  1092. if (result.result.code) return handleCallback(callback, toError(result.result));
  1093. if (result.result.writeErrors)
  1094. return handleCallback(callback, toError(result.result.writeErrors[0]));
  1095. // Return the results
  1096. handleCallback(callback, null, result);
  1097. });
  1098. }
  1099. /**
  1100. * Rename the collection.
  1101. *
  1102. * @method
  1103. * @param {Collection} a Collection instance.
  1104. * @param {string} newName New name of of the collection.
  1105. * @param {object} [options] Optional settings. See Collection.prototype.rename for a list of options.
  1106. * @param {Collection~collectionResultCallback} [callback] The results callback
  1107. */
  1108. function rename(coll, newName, options, callback) {
  1109. let Collection = loadCollection();
  1110. // Check the collection name
  1111. checkCollectionName(newName);
  1112. // Build the command
  1113. const renameCollection = `${coll.s.dbName}.${coll.s.name}`;
  1114. const toCollection = `${coll.s.dbName}.${newName}`;
  1115. const dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
  1116. const cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
  1117. // Decorate command with writeConcern if supported
  1118. applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
  1119. // Execute against admin
  1120. executeDbAdminCommand(coll.s.db.admin().s.db, cmd, options, (err, doc) => {
  1121. if (err) return handleCallback(callback, err, null);
  1122. // We have an error
  1123. if (doc.errmsg) return handleCallback(callback, toError(doc), null);
  1124. try {
  1125. return handleCallback(
  1126. callback,
  1127. null,
  1128. new Collection(
  1129. coll.s.db,
  1130. coll.s.topology,
  1131. coll.s.dbName,
  1132. newName,
  1133. coll.s.pkFactory,
  1134. coll.s.options
  1135. )
  1136. );
  1137. } catch (err) {
  1138. return handleCallback(callback, toError(err), null);
  1139. }
  1140. });
  1141. }
  1142. /**
  1143. * Replace a document in the collection.
  1144. *
  1145. * @method
  1146. * @param {Collection} a Collection instance.
  1147. * @param {object} filter The Filter used to select the document to update
  1148. * @param {object} doc The Document that replaces the matching document
  1149. * @param {object} [options] Optional settings. See Collection.prototype.replaceOne for a list of options.
  1150. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1151. */
  1152. function replaceOne(coll, filter, doc, options, callback) {
  1153. // Set single document update
  1154. options.multi = false;
  1155. // Execute update
  1156. updateDocuments(coll, filter, doc, options, (err, r) => {
  1157. if (callback == null) return;
  1158. if (err && callback) return callback(err);
  1159. if (r == null) return callback(null, { result: { ok: 1 } });
  1160. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  1161. r.upsertedId =
  1162. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  1163. ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
  1164. : null;
  1165. r.upsertedCount =
  1166. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  1167. r.matchedCount =
  1168. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  1169. r.ops = [doc];
  1170. if (callback) callback(null, r);
  1171. });
  1172. }
  1173. /**
  1174. * Save a document.
  1175. *
  1176. * @method
  1177. * @param {Collection} a Collection instance.
  1178. * @param {object} doc Document to save
  1179. * @param {object} [options] Optional settings. See Collection.prototype.save for a list of options.
  1180. * @param {Collection~writeOpCallback} [callback] The command result callback
  1181. * @deprecated use insertOne, insertMany, updateOne or updateMany
  1182. */
  1183. function save(coll, doc, options, callback) {
  1184. // Get the write concern options
  1185. const finalOptions = applyWriteConcern(
  1186. Object.assign({}, options),
  1187. { db: coll.s.db, collection: coll },
  1188. options
  1189. );
  1190. // Establish if we need to perform an insert or update
  1191. if (doc._id != null) {
  1192. finalOptions.upsert = true;
  1193. return updateDocuments(coll, { _id: doc._id }, doc, finalOptions, callback);
  1194. }
  1195. // Insert the document
  1196. insertDocuments(coll, [doc], finalOptions, (err, result) => {
  1197. if (callback == null) return;
  1198. if (doc == null) return handleCallback(callback, null, null);
  1199. if (err) return handleCallback(callback, err, null);
  1200. handleCallback(callback, null, result);
  1201. });
  1202. }
  1203. /**
  1204. * Get all the collection statistics.
  1205. *
  1206. * @method
  1207. * @param {Collection} a Collection instance.
  1208. * @param {object} [options] Optional settings. See Collection.prototype.stats for a list of options.
  1209. * @param {Collection~resultCallback} [callback] The collection result callback
  1210. */
  1211. function stats(coll, options, callback) {
  1212. // Build command object
  1213. const commandObject = {
  1214. collStats: coll.s.name
  1215. };
  1216. // Check if we have the scale value
  1217. if (options['scale'] != null) commandObject['scale'] = options['scale'];
  1218. options = Object.assign({}, options);
  1219. // Ensure we have the right read preference inheritance
  1220. options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
  1221. // Execute the command
  1222. executeCommand(coll.s.db, commandObject, options, callback);
  1223. }
  1224. function updateCallback(err, r, callback) {
  1225. if (callback == null) return;
  1226. if (err) return callback(err);
  1227. if (r == null) return callback(null, { result: { ok: 1 } });
  1228. r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
  1229. r.upsertedId =
  1230. Array.isArray(r.result.upserted) && r.result.upserted.length > 0
  1231. ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
  1232. : null;
  1233. r.upsertedCount =
  1234. Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
  1235. r.matchedCount =
  1236. Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
  1237. callback(null, r);
  1238. }
  1239. function updateDocuments(coll, selector, document, options, callback) {
  1240. if ('function' === typeof options) (callback = options), (options = null);
  1241. if (options == null) options = {};
  1242. if (!('function' === typeof callback)) callback = null;
  1243. // If we are not providing a selector or document throw
  1244. if (selector == null || typeof selector !== 'object')
  1245. return callback(toError('selector must be a valid JavaScript object'));
  1246. if (document == null || typeof document !== 'object')
  1247. return callback(toError('document must be a valid JavaScript object'));
  1248. // Final options for retryable writes and write concern
  1249. let finalOptions = Object.assign({}, options);
  1250. finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
  1251. finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
  1252. // Do we return the actual result document
  1253. // Either use override on the function, or go back to default on either the collection
  1254. // level or db
  1255. finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
  1256. // Execute the operation
  1257. const op = { q: selector, u: document };
  1258. op.upsert = options.upsert !== void 0 ? !!options.upsert : false;
  1259. op.multi = options.multi !== void 0 ? !!options.multi : false;
  1260. if (finalOptions.arrayFilters) {
  1261. op.arrayFilters = finalOptions.arrayFilters;
  1262. delete finalOptions.arrayFilters;
  1263. }
  1264. if (finalOptions.retryWrites && op.multi) {
  1265. finalOptions.retryWrites = false;
  1266. }
  1267. // Have we specified collation
  1268. try {
  1269. decorateWithCollation(finalOptions, coll, options);
  1270. } catch (err) {
  1271. return callback(err, null);
  1272. }
  1273. // Update options
  1274. coll.s.topology.update(coll.s.namespace, [op], finalOptions, (err, result) => {
  1275. if (callback == null) return;
  1276. if (err) return handleCallback(callback, err, null);
  1277. if (result == null) return handleCallback(callback, null, null);
  1278. if (result.result.code) return handleCallback(callback, toError(result.result));
  1279. if (result.result.writeErrors)
  1280. return handleCallback(callback, toError(result.result.writeErrors[0]));
  1281. // Return the results
  1282. handleCallback(callback, null, result);
  1283. });
  1284. }
  1285. /**
  1286. * Update multiple documents in the collection.
  1287. *
  1288. * @method
  1289. * @param {Collection} a Collection instance.
  1290. * @param {object} filter The Filter used to select the documents to update
  1291. * @param {object} update The update operations to be applied to the document
  1292. * @param {object} [options] Optional settings. See Collection.prototype.updateMany for a list of options.
  1293. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1294. */
  1295. function updateMany(coll, filter, update, options, callback) {
  1296. // Set single document update
  1297. options.multi = true;
  1298. // Execute update
  1299. updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
  1300. }
  1301. /**
  1302. * Update a single document in the collection.
  1303. *
  1304. * @method
  1305. * @param {Collection} a Collection instance.
  1306. * @param {object} filter The Filter used to select the document to update
  1307. * @param {object} update The update operations to be applied to the document
  1308. * @param {object} [options] Optional settings. See Collection.prototype.updateOne for a list of options.
  1309. * @param {Collection~updateWriteOpCallback} [callback] The command result callback
  1310. */
  1311. function updateOne(coll, filter, update, options, callback) {
  1312. // Set single document update
  1313. options.multi = false;
  1314. // Execute update
  1315. updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
  1316. }
  1317. module.exports = {
  1318. bulkWrite,
  1319. checkForAtomicOperators,
  1320. count,
  1321. countDocuments,
  1322. buildCountCommand,
  1323. createIndex,
  1324. createIndexes,
  1325. deleteMany,
  1326. deleteOne,
  1327. distinct,
  1328. dropIndex,
  1329. dropIndexes,
  1330. ensureIndex,
  1331. findAndModify,
  1332. findAndRemove,
  1333. findOne,
  1334. findOneAndDelete,
  1335. findOneAndReplace,
  1336. findOneAndUpdate,
  1337. geoHaystackSearch,
  1338. group,
  1339. indexes,
  1340. indexExists,
  1341. indexInformation,
  1342. insertMany,
  1343. insertOne,
  1344. isCapped,
  1345. mapReduce,
  1346. optionsOp,
  1347. parallelCollectionScan,
  1348. prepareDocs,
  1349. reIndex,
  1350. removeDocuments,
  1351. rename,
  1352. replaceOne,
  1353. save,
  1354. stats,
  1355. updateDocuments,
  1356. updateMany,
  1357. updateOne
  1358. };