Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

collection_ops.js 50KB

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