1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546 |
- 'use strict';
- const applyWriteConcern = require('../utils').applyWriteConcern;
- const applyRetryableWrites = require('../utils').applyRetryableWrites;
- const checkCollectionName = require('../utils').checkCollectionName;
- const Code = require('mongodb-core').BSON.Code;
- const createIndexDb = require('./db_ops').createIndex;
- const decorateCommand = require('../utils').decorateCommand;
- const decorateWithCollation = require('../utils').decorateWithCollation;
- const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
- const ensureIndexDb = require('./db_ops').ensureIndex;
- const evaluate = require('./db_ops').evaluate;
- const executeCommand = require('./db_ops').executeCommand;
- const executeDbAdminCommand = require('./db_ops').executeDbAdminCommand;
- const formattedOrderClause = require('../utils').formattedOrderClause;
- const resolveReadPreference = require('../utils').resolveReadPreference;
- const handleCallback = require('../utils').handleCallback;
- const indexInformationDb = require('./db_ops').indexInformation;
- const isObject = require('../utils').isObject;
- const Long = require('mongodb-core').BSON.Long;
- const MongoError = require('mongodb-core').MongoError;
- const ReadPreference = require('mongodb-core').ReadPreference;
- const toError = require('../utils').toError;
- let collection;
- function loadCollection() {
- if (!collection) {
- collection = require('../collection');
- }
- return collection;
- }
- let db;
- function loadDb() {
- if (!db) {
- db = require('../db');
- }
- return db;
- }
- /**
- * Group function helper
- * @ignore
- */
- // var groupFunction = function () {
- // var c = db[ns].find(condition);
- // var map = new Map();
- // var reduce_function = reduce;
- //
- // while (c.hasNext()) {
- // var obj = c.next();
- // var key = {};
- //
- // for (var i = 0, len = keys.length; i < len; ++i) {
- // var k = keys[i];
- // key[k] = obj[k];
- // }
- //
- // var aggObj = map.get(key);
- //
- // if (aggObj == null) {
- // var newObj = Object.extend({}, key);
- // aggObj = Object.extend(newObj, initial);
- // map.put(key, aggObj);
- // }
- //
- // reduce_function(obj, aggObj);
- // }
- //
- // return { "result": map.values() };
- // }.toString();
- const groupFunction =
- '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}';
- /**
- * Perform a bulkWrite operation. See Collection.prototype.bulkWrite for more information.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object[]} operations Bulk operations to perform.
- * @param {object} [options] Optional settings. See Collection.prototype.bulkWrite for a list of options.
- * @param {Collection~bulkWriteOpCallback} [callback] The command result callback
- */
- function bulkWrite(coll, operations, options, callback) {
- // Add ignoreUndefined
- if (coll.s.options.ignoreUndefined) {
- options = Object.assign({}, options);
- options.ignoreUndefined = coll.s.options.ignoreUndefined;
- }
- // Create the bulk operation
- const bulk =
- options.ordered === true || options.ordered == null
- ? coll.initializeOrderedBulkOp(options)
- : coll.initializeUnorderedBulkOp(options);
- // Do we have a collation
- let collation = false;
- // for each op go through and add to the bulk
- try {
- for (let i = 0; i < operations.length; i++) {
- // Get the operation type
- const key = Object.keys(operations[i])[0];
- // Check if we have a collation
- if (operations[i][key].collation) {
- collation = true;
- }
- // Pass to the raw bulk
- bulk.raw(operations[i]);
- }
- } catch (err) {
- return callback(err, null);
- }
- // Final options for retryable writes and write concern
- let finalOptions = Object.assign({}, options);
- finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
- finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
- const writeCon = finalOptions.writeConcern ? finalOptions.writeConcern : {};
- const capabilities = coll.s.topology.capabilities();
- // Did the user pass in a collation, check if our write server supports it
- if (collation && capabilities && !capabilities.commandsTakeCollation) {
- return callback(new MongoError('server/primary/mongos does not support collation'));
- }
- // Execute the bulk
- bulk.execute(writeCon, finalOptions, (err, r) => {
- // We have connection level error
- if (!r && err) {
- return callback(err, null);
- }
- r.insertedCount = r.nInserted;
- r.matchedCount = r.nMatched;
- r.modifiedCount = r.nModified || 0;
- r.deletedCount = r.nRemoved;
- r.upsertedCount = r.getUpsertedIds().length;
- r.upsertedIds = {};
- r.insertedIds = {};
- // Update the n
- r.n = r.insertedCount;
- // Inserted documents
- const inserted = r.getInsertedIds();
- // Map inserted ids
- for (let i = 0; i < inserted.length; i++) {
- r.insertedIds[inserted[i].index] = inserted[i]._id;
- }
- // Upserted documents
- const upserted = r.getUpsertedIds();
- // Map upserted ids
- for (let i = 0; i < upserted.length; i++) {
- r.upsertedIds[upserted[i].index] = upserted[i]._id;
- }
- // Return the results
- callback(null, r);
- });
- }
- // Check the update operation to ensure it has atomic operators.
- function checkForAtomicOperators(update) {
- const keys = Object.keys(update);
- // same errors as the server would give for update doc lacking atomic operators
- if (keys.length === 0) {
- return toError('The update operation document must contain at least one atomic operator.');
- }
- if (keys[0][0] !== '$') {
- return toError('the update operation document must contain atomic operators.');
- }
- }
- /**
- * Count the number of documents in the collection that match the query.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} query The query for the count.
- * @param {object} [options] Optional settings. See Collection.prototype.count for a list of options.
- * @param {Collection~countCallback} [callback] The command result callback
- */
- function count(coll, query, options, callback) {
- if (typeof options === 'function') (callback = options), (options = {});
- options = Object.assign({}, options);
- options.collectionName = coll.s.name;
- options.readPreference = resolveReadPreference(options, {
- db: coll.s.db,
- collection: coll
- });
- let cmd;
- try {
- cmd = buildCountCommand(coll, query, options);
- } catch (err) {
- return callback(err);
- }
- executeCommand(coll.s.db, cmd, options, (err, result) => {
- if (err) return handleCallback(callback, err);
- handleCallback(callback, null, result.n);
- });
- }
- function countDocuments(coll, query, options, callback) {
- const skip = options.skip;
- const limit = options.limit;
- options = Object.assign({}, options);
- const pipeline = [{ $match: query }];
- // Add skip and limit if defined
- if (typeof skip === 'number') {
- pipeline.push({ $skip: skip });
- }
- if (typeof limit === 'number') {
- pipeline.push({ $limit: limit });
- }
- pipeline.push({ $group: { _id: 1, n: { $sum: 1 } } });
- delete options.limit;
- delete options.skip;
- coll.aggregate(pipeline, options).toArray((err, docs) => {
- if (err) return handleCallback(callback, err);
- handleCallback(callback, null, docs.length ? docs[0].n : 0);
- });
- }
- /**
- * Build the count command.
- *
- * @method
- * @param {collectionOrCursor} an instance of a collection or cursor
- * @param {object} query The query for the count.
- * @param {object} [options] Optional settings. See Collection.prototype.count and Cursor.prototype.count for a list of options.
- */
- function buildCountCommand(collectionOrCursor, query, options) {
- const skip = options.skip;
- const limit = options.limit;
- let hint = options.hint;
- const maxTimeMS = options.maxTimeMS;
- query = query || {};
- // Final query
- const cmd = {
- count: options.collectionName,
- query: query
- };
- // check if collectionOrCursor is a cursor by using cursor.s.numberOfRetries
- if (collectionOrCursor.s.numberOfRetries) {
- if (collectionOrCursor.s.options.hint) {
- hint = collectionOrCursor.s.options.hint;
- } else if (collectionOrCursor.s.cmd.hint) {
- hint = collectionOrCursor.s.cmd.hint;
- }
- decorateWithCollation(cmd, collectionOrCursor, collectionOrCursor.s.cmd);
- } else {
- decorateWithCollation(cmd, collectionOrCursor, options);
- }
- // Add limit, skip and maxTimeMS if defined
- if (typeof skip === 'number') cmd.skip = skip;
- if (typeof limit === 'number') cmd.limit = limit;
- if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
- if (hint) cmd.hint = hint;
- // Do we have a readConcern specified
- decorateWithReadConcern(cmd, collectionOrCursor);
- return cmd;
- }
- /**
- * Create an index on the db and collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {(string|object)} fieldOrSpec Defines the index.
- * @param {object} [options] Optional settings. See Collection.prototype.createIndex for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function createIndex(coll, fieldOrSpec, options, callback) {
- createIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
- }
- /**
- * Create multiple indexes in the collection. This method is only supported for
- * MongoDB 2.6 or higher. Earlier version of MongoDB will throw a command not supported
- * error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {array} indexSpecs An array of index specifications to be created
- * @param {Object} [options] Optional settings. See Collection.prototype.createIndexes for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function createIndexes(coll, indexSpecs, options, callback) {
- const capabilities = coll.s.topology.capabilities();
- // Ensure we generate the correct name if the parameter is not set
- for (let i = 0; i < indexSpecs.length; i++) {
- if (indexSpecs[i].name == null) {
- const keys = [];
- // Did the user pass in a collation, check if our write server supports it
- if (indexSpecs[i].collation && capabilities && !capabilities.commandsTakeCollation) {
- return callback(new MongoError('server/primary/mongos does not support collation'));
- }
- for (let name in indexSpecs[i].key) {
- keys.push(`${name}_${indexSpecs[i].key[name]}`);
- }
- // Set the name
- indexSpecs[i].name = keys.join('_');
- }
- }
- options = Object.assign({}, options, { readPreference: ReadPreference.PRIMARY });
- // Execute the index
- executeCommand(
- coll.s.db,
- {
- createIndexes: coll.s.name,
- indexes: indexSpecs
- },
- options,
- callback
- );
- }
- function deleteCallback(err, r, callback) {
- if (callback == null) return;
- if (err && callback) return callback(err);
- if (r == null) return callback(null, { result: { ok: 1 } });
- r.deletedCount = r.result.n;
- if (callback) callback(null, r);
- }
- /**
- * Delete multiple documents from the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter The Filter used to select the documents to remove
- * @param {object} [options] Optional settings. See Collection.prototype.deleteMany for a list of options.
- * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
- */
- function deleteMany(coll, filter, options, callback) {
- options.single = false;
- removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
- }
- /**
- * Delete a single document from the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter The Filter used to select the document to remove
- * @param {object} [options] Optional settings. See Collection.prototype.deleteOne for a list of options.
- * @param {Collection~deleteWriteOpCallback} [callback] The command result callback
- */
- function deleteOne(coll, filter, options, callback) {
- options.single = true;
- removeDocuments(coll, filter, options, (err, r) => deleteCallback(err, r, callback));
- }
- /**
- * Return a list of distinct values for the given key across a collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {string} key Field of the document to find distinct values for.
- * @param {object} query The query for filtering the set of documents to which we apply the distinct filter.
- * @param {object} [options] Optional settings. See Collection.prototype.distinct for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function distinct(coll, key, query, options, callback) {
- // maxTimeMS option
- const maxTimeMS = options.maxTimeMS;
- // Distinct command
- const cmd = {
- distinct: coll.s.name,
- key: key,
- query: query
- };
- options = Object.assign({}, options);
- // Ensure we have the right read preference inheritance
- options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
- // Add maxTimeMS if defined
- if (typeof maxTimeMS === 'number') cmd.maxTimeMS = maxTimeMS;
- // Do we have a readConcern specified
- decorateWithReadConcern(cmd, coll, options);
- // Have we specified collation
- try {
- decorateWithCollation(cmd, coll, options);
- } catch (err) {
- return callback(err, null);
- }
- // Execute the command
- executeCommand(coll.s.db, cmd, options, (err, result) => {
- if (err) return handleCallback(callback, err);
- handleCallback(callback, null, result.values);
- });
- }
- /**
- * Drop an index from this collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {string} indexName Name of the index to drop.
- * @param {object} [options] Optional settings. See Collection.prototype.dropIndex for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function dropIndex(coll, indexName, options, callback) {
- // Delete index command
- const cmd = { dropIndexes: coll.s.name, index: indexName };
- // Decorate command with writeConcern if supported
- applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
- // Execute command
- executeCommand(coll.s.db, cmd, options, (err, result) => {
- if (typeof callback !== 'function') return;
- if (err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result);
- });
- }
- /**
- * Drop all indexes from this collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {Object} [options] Optional settings. See Collection.prototype.dropIndexes for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function dropIndexes(coll, options, callback) {
- dropIndex(coll, '*', options, err => {
- if (err) return handleCallback(callback, err, false);
- handleCallback(callback, null, true);
- });
- }
- /**
- * Ensure that an index exists. If the index does not exist, this function creates it.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {(string|object)} fieldOrSpec Defines the index.
- * @param {object} [options] Optional settings. See Collection.prototype.ensureIndex for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function ensureIndex(coll, fieldOrSpec, options, callback) {
- ensureIndexDb(coll.s.db, coll.s.name, fieldOrSpec, options, callback);
- }
- /**
- * Find and update a document.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} query Query object to locate the object to modify.
- * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
- * @param {object} doc The fields/vals to be updated.
- * @param {object} [options] Optional settings. See Collection.prototype.findAndModify for a list of options.
- * @param {Collection~findAndModifyCallback} [callback] The command result callback
- * @deprecated use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead
- */
- function findAndModify(coll, query, sort, doc, options, callback) {
- // Create findAndModify command object
- const queryObject = {
- findAndModify: coll.s.name,
- query: query
- };
- sort = formattedOrderClause(sort);
- if (sort) {
- queryObject.sort = sort;
- }
- queryObject.new = options.new ? true : false;
- queryObject.remove = options.remove ? true : false;
- queryObject.upsert = options.upsert ? true : false;
- const projection = options.projection || options.fields;
- if (projection) {
- queryObject.fields = projection;
- }
- if (options.arrayFilters) {
- queryObject.arrayFilters = options.arrayFilters;
- delete options.arrayFilters;
- }
- if (doc && !options.remove) {
- queryObject.update = doc;
- }
- if (options.maxTimeMS) queryObject.maxTimeMS = options.maxTimeMS;
- // Either use override on the function, or go back to default on either the collection
- // level or db
- options.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
- // No check on the documents
- options.checkKeys = false;
- // Final options for retryable writes and write concern
- let finalOptions = Object.assign({}, options);
- finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
- finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
- // Decorate the findAndModify command with the write Concern
- if (finalOptions.writeConcern) {
- queryObject.writeConcern = finalOptions.writeConcern;
- }
- // Have we specified bypassDocumentValidation
- if (finalOptions.bypassDocumentValidation === true) {
- queryObject.bypassDocumentValidation = finalOptions.bypassDocumentValidation;
- }
- finalOptions.readPreference = ReadPreference.primary;
- // Have we specified collation
- try {
- decorateWithCollation(queryObject, coll, finalOptions);
- } catch (err) {
- return callback(err, null);
- }
- // Execute the command
- executeCommand(coll.s.db, queryObject, finalOptions, (err, result) => {
- if (err) return handleCallback(callback, err, null);
- return handleCallback(callback, null, result);
- });
- }
- /**
- * Find and remove a document.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} query Query object to locate the object to modify.
- * @param {array} sort If multiple docs match, choose the first one in the specified sort order as the object to manipulate.
- * @param {object} [options] Optional settings. See Collection.prototype.findAndRemove for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- * @deprecated use findOneAndDelete instead
- */
- function findAndRemove(coll, query, sort, options, callback) {
- // Add the remove option
- options.remove = true;
- // Execute the callback
- findAndModify(coll, query, sort, null, options, callback);
- }
- /**
- * Fetch the first document that matches the query.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} query Query for find Operation
- * @param {object} [options] Optional settings. See Collection.prototype.findOne for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function findOne(coll, query, options, callback) {
- const cursor = coll
- .find(query, options)
- .limit(-1)
- .batchSize(1);
- // Return the item
- cursor.next((err, item) => {
- if (err != null) return handleCallback(callback, toError(err), null);
- handleCallback(callback, null, item);
- });
- }
- /**
- * Find a document and delete it in one atomic operation. This requires a write lock for the duration of the operation.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter Document selection filter.
- * @param {object} [options] Optional settings. See Collection.prototype.findOneAndDelete for a list of options.
- * @param {Collection~findAndModifyCallback} [callback] The collection result callback
- */
- function findOneAndDelete(coll, filter, options, callback) {
- // Final options
- const finalOptions = Object.assign({}, options);
- finalOptions.fields = options.projection;
- finalOptions.remove = true;
- // Execute find and Modify
- findAndModify(coll, filter, options.sort, null, finalOptions, callback);
- }
- /**
- * Find a document and replace it in one atomic operation. This requires a write lock for the duration of the operation.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter Document selection filter.
- * @param {object} replacement Document replacing the matching document.
- * @param {object} [options] Optional settings. See Collection.prototype.findOneAndReplace for a list of options.
- * @param {Collection~findAndModifyCallback} [callback] The collection result callback
- */
- function findOneAndReplace(coll, filter, replacement, options, callback) {
- // Final options
- const finalOptions = Object.assign({}, options);
- finalOptions.fields = options.projection;
- finalOptions.update = true;
- finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
- finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;
- // Execute findAndModify
- findAndModify(coll, filter, options.sort, replacement, finalOptions, callback);
- }
- /**
- * Find a document and update it in one atomic operation. This requires a write lock for the duration of the operation.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter Document selection filter.
- * @param {object} update Update operations to be performed on the document
- * @param {object} [options] Optional settings. See Collection.prototype.findOneAndUpdate for a list of options.
- * @param {Collection~findAndModifyCallback} [callback] The collection result callback
- */
- function findOneAndUpdate(coll, filter, update, options, callback) {
- // Final options
- const finalOptions = Object.assign({}, options);
- finalOptions.fields = options.projection;
- finalOptions.update = true;
- finalOptions.new = typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
- finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;
- // Execute findAndModify
- findAndModify(coll, filter, options.sort, update, finalOptions, callback);
- }
- /**
- * Execute a geo search using a geo haystack index on a collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {number} x Point to search on the x axis, ensure the indexes are ordered in the same order.
- * @param {number} y Point to search on the y axis, ensure the indexes are ordered in the same order.
- * @param {object} [options] Optional settings. See Collection.prototype.geoHaystackSearch for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function geoHaystackSearch(coll, x, y, options, callback) {
- // Build command object
- let commandObject = {
- geoSearch: coll.s.name,
- near: [x, y]
- };
- // Remove read preference from hash if it exists
- commandObject = decorateCommand(commandObject, options, ['readPreference', 'session']);
- options = Object.assign({}, options);
- // Ensure we have the right read preference inheritance
- options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
- // Do we have a readConcern specified
- decorateWithReadConcern(commandObject, coll, options);
- // Execute the command
- executeCommand(coll.s.db, commandObject, options, (err, res) => {
- if (err) return handleCallback(callback, err);
- if (res.err || res.errmsg) handleCallback(callback, toError(res));
- // should we only be returning res.results here? Not sure if the user
- // should see the other return information
- handleCallback(callback, null, res);
- });
- }
- /**
- * Run a group command across a collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {(object|array|function|code)} keys An object, array or function expressing the keys to group by.
- * @param {object} condition An optional condition that must be true for a row to be considered.
- * @param {object} initial Initial value of the aggregation counter object.
- * @param {(function|Code)} reduce The reduce function aggregates (reduces) the objects iterated
- * @param {(function|Code)} finalize An optional function to be run on each item in the result set just before the item is returned.
- * @param {boolean} command Specify if you wish to run using the internal group command or using eval, default is true.
- * @param {object} [options] Optional settings. See Collection.prototype.group for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- * @deprecated MongoDB 3.6 or higher will no longer support the group command. We recommend rewriting using the aggregation framework.
- */
- function group(coll, keys, condition, initial, reduce, finalize, command, options, callback) {
- // Execute using the command
- if (command) {
- const reduceFunction = reduce && reduce._bsontype === 'Code' ? reduce : new Code(reduce);
- const selector = {
- group: {
- ns: coll.s.name,
- $reduce: reduceFunction,
- cond: condition,
- initial: initial,
- out: 'inline'
- }
- };
- // if finalize is defined
- if (finalize != null) selector.group['finalize'] = finalize;
- // Set up group selector
- if ('function' === typeof keys || (keys && keys._bsontype === 'Code')) {
- selector.group.$keyf = keys && keys._bsontype === 'Code' ? keys : new Code(keys);
- } else {
- const hash = {};
- keys.forEach(key => {
- hash[key] = 1;
- });
- selector.group.key = hash;
- }
- options = Object.assign({}, options);
- // Ensure we have the right read preference inheritance
- options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
- // Do we have a readConcern specified
- decorateWithReadConcern(selector, coll, options);
- // Have we specified collation
- try {
- decorateWithCollation(selector, coll, options);
- } catch (err) {
- return callback(err, null);
- }
- // Execute command
- executeCommand(coll.s.db, selector, options, (err, result) => {
- if (err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result.retval);
- });
- } else {
- // Create execution scope
- const scope = reduce != null && reduce._bsontype === 'Code' ? reduce.scope : {};
- scope.ns = coll.s.name;
- scope.keys = keys;
- scope.condition = condition;
- scope.initial = initial;
- // Pass in the function text to execute within mongodb.
- const groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
- evaluate(coll.s.db, new Code(groupfn, scope), null, options, (err, results) => {
- if (err) return handleCallback(callback, err, null);
- handleCallback(callback, null, results.result || results);
- });
- }
- }
- /**
- * Retrieve all the indexes on the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {Object} [options] Optional settings. See Collection.prototype.indexes for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function indexes(coll, options, callback) {
- options = Object.assign({}, { full: true }, options);
- indexInformationDb(coll.s.db, coll.s.name, options, callback);
- }
- /**
- * Check if one or more indexes exist on the collection. This fails on the first index that doesn't exist.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {(string|array)} indexes One or more index names to check.
- * @param {Object} [options] Optional settings. See Collection.prototype.indexExists for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function indexExists(coll, indexes, options, callback) {
- indexInformation(coll, options, (err, indexInformation) => {
- // If we have an error return
- if (err != null) return handleCallback(callback, err, null);
- // Let's check for the index names
- if (!Array.isArray(indexes))
- return handleCallback(callback, null, indexInformation[indexes] != null);
- // Check in list of indexes
- for (let i = 0; i < indexes.length; i++) {
- if (indexInformation[indexes[i]] == null) {
- return handleCallback(callback, null, false);
- }
- }
- // All keys found return true
- return handleCallback(callback, null, true);
- });
- }
- /**
- * Retrieve this collection's index info.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} [options] Optional settings. See Collection.prototype.indexInformation for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function indexInformation(coll, options, callback) {
- indexInformationDb(coll.s.db, coll.s.name, options, callback);
- }
- function insertDocuments(coll, docs, options, callback) {
- if (typeof options === 'function') (callback = options), (options = {});
- options = options || {};
- // Ensure we are operating on an array op docs
- docs = Array.isArray(docs) ? docs : [docs];
- // Final options for retryable writes and write concern
- let finalOptions = Object.assign({}, options);
- finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
- finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
- // If keep going set unordered
- if (finalOptions.keepGoing === true) finalOptions.ordered = false;
- finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
- docs = prepareDocs(coll, docs, options);
- // File inserts
- coll.s.topology.insert(coll.s.namespace, docs, finalOptions, (err, result) => {
- if (callback == null) return;
- if (err) return handleCallback(callback, err);
- if (result == null) return handleCallback(callback, null, null);
- if (result.result.code) return handleCallback(callback, toError(result.result));
- if (result.result.writeErrors)
- return handleCallback(callback, toError(result.result.writeErrors[0]));
- // Add docs to the list
- result.ops = docs;
- // Return the results
- handleCallback(callback, null, result);
- });
- }
- /**
- * Insert a single document into the collection. See Collection.prototype.insertOne for more information.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} doc Document to insert.
- * @param {object} [options] Optional settings. See Collection.prototype.insertOne for a list of options.
- * @param {Collection~insertOneWriteOpCallback} [callback] The command result callback
- */
- function insertOne(coll, doc, options, callback) {
- if (Array.isArray(doc)) {
- return callback(
- MongoError.create({ message: 'doc parameter must be an object', driver: true })
- );
- }
- insertDocuments(coll, [doc], options, (err, r) => {
- if (callback == null) return;
- if (err && callback) return callback(err);
- // Workaround for pre 2.6 servers
- if (r == null) return callback(null, { result: { ok: 1 } });
- // Add values to top level to ensure crud spec compatibility
- r.insertedCount = r.result.n;
- r.insertedId = doc._id;
- if (callback) callback(null, r);
- });
- }
- /**
- * Inserts an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
- * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
- * can be overridden by setting the **forceServerObjectId** flag.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object[]} docs Documents to insert.
- * @param {number} [options] Optional settings. See Collection.prototype.insertMany for a list of options.
- * @param {Collection~insertWriteOpCallback} [callback] The command result callback
- */
- function insertMany(coll, docs, options, callback) {
- if (!Array.isArray(docs)) {
- return callback(
- MongoError.create({ message: 'docs parameter must be an array of documents', driver: true })
- );
- }
- // If keep going set unordered
- options['serializeFunctions'] = options['serializeFunctions'] || coll.s.serializeFunctions;
- docs = prepareDocs(coll, docs, options);
- // Generate the bulk write operations
- const operations = [
- {
- insertMany: docs
- }
- ];
- bulkWrite(coll, operations, options, (err, result) => {
- if (err) return callback(err, null);
- callback(null, mapInsertManyResults(docs, result));
- });
- }
- function mapInsertManyResults(docs, r) {
- const finalResult = {
- result: { ok: 1, n: r.insertedCount },
- ops: docs,
- insertedCount: r.insertedCount,
- insertedIds: r.insertedIds
- };
- if (r.getLastOp()) {
- finalResult.result.opTime = r.getLastOp();
- }
- return finalResult;
- }
- /**
- * Determine whether the collection is a capped collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {Object} [options] Optional settings. See Collection.prototype.isCapped for a list of options.
- * @param {Collection~resultCallback} [callback] The results callback
- */
- function isCapped(coll, options, callback) {
- optionsOp(coll, options, (err, document) => {
- if (err) return handleCallback(callback, err);
- handleCallback(callback, null, !!(document && document.capped));
- });
- }
- /**
- * Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {(function|string)} map The mapping function.
- * @param {(function|string)} reduce The reduce function.
- * @param {object} [options] Optional settings. See Collection.prototype.mapReduce for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function mapReduce(coll, map, reduce, options, callback) {
- const mapCommandHash = {
- mapreduce: coll.s.name,
- map: map,
- reduce: reduce
- };
- // Exclusion list
- const exclusionList = ['readPreference', 'session', 'bypassDocumentValidation'];
- // Add any other options passed in
- for (let n in options) {
- if ('scope' === n) {
- mapCommandHash[n] = processScope(options[n]);
- } else {
- // Only include if not in exclusion list
- if (exclusionList.indexOf(n) === -1) {
- mapCommandHash[n] = options[n];
- }
- }
- }
- options = Object.assign({}, options);
- // Ensure we have the right read preference inheritance
- options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
- // If we have a read preference and inline is not set as output fail hard
- if (
- options.readPreference !== false &&
- options.readPreference !== 'primary' &&
- options['out'] &&
- (options['out'].inline !== 1 && options['out'] !== 'inline')
- ) {
- // Force readPreference to primary
- options.readPreference = 'primary';
- // Decorate command with writeConcern if supported
- applyWriteConcern(mapCommandHash, { db: coll.s.db, collection: coll }, options);
- } else {
- decorateWithReadConcern(mapCommandHash, coll, options);
- }
- // Is bypassDocumentValidation specified
- if (options.bypassDocumentValidation === true) {
- mapCommandHash.bypassDocumentValidation = options.bypassDocumentValidation;
- }
- // Have we specified collation
- try {
- decorateWithCollation(mapCommandHash, coll, options);
- } catch (err) {
- return callback(err, null);
- }
- // Execute command
- executeCommand(coll.s.db, mapCommandHash, options, (err, result) => {
- if (err) return handleCallback(callback, err);
- // Check if we have an error
- if (1 !== result.ok || result.err || result.errmsg) {
- return handleCallback(callback, toError(result));
- }
- // Create statistics value
- const stats = {};
- if (result.timeMillis) stats['processtime'] = result.timeMillis;
- if (result.counts) stats['counts'] = result.counts;
- if (result.timing) stats['timing'] = result.timing;
- // invoked with inline?
- if (result.results) {
- // If we wish for no verbosity
- if (options['verbose'] == null || !options['verbose']) {
- return handleCallback(callback, null, result.results);
- }
- return handleCallback(callback, null, { results: result.results, stats: stats });
- }
- // The returned collection
- let collection = null;
- // If we have an object it's a different db
- if (result.result != null && typeof result.result === 'object') {
- const doc = result.result;
- // Return a collection from another db
- let Db = loadDb();
- collection = new Db(doc.db, coll.s.db.s.topology, coll.s.db.s.options).collection(
- doc.collection
- );
- } else {
- // Create a collection object that wraps the result collection
- collection = coll.s.db.collection(result.result);
- }
- // If we wish for no verbosity
- if (options['verbose'] == null || !options['verbose']) {
- return handleCallback(callback, err, collection);
- }
- // Return stats as third set of values
- handleCallback(callback, err, { collection: collection, stats: stats });
- });
- }
- /**
- * Return the options of the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {Object} [options] Optional settings. See Collection.prototype.options for a list of options.
- * @param {Collection~resultCallback} [callback] The results callback
- */
- function optionsOp(coll, opts, callback) {
- coll.s.db.listCollections({ name: coll.s.name }, opts).toArray((err, collections) => {
- if (err) return handleCallback(callback, err);
- if (collections.length === 0) {
- return handleCallback(
- callback,
- MongoError.create({ message: `collection ${coll.s.namespace} not found`, driver: true })
- );
- }
- handleCallback(callback, err, collections[0].options || null);
- });
- }
- /**
- * Return N parallel cursors for a collection to allow parallel reading of the entire collection. There are
- * no ordering guarantees for returned results.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} [options] Optional settings. See Collection.prototype.parallelCollectionScan for a list of options.
- * @param {Collection~parallelCollectionScanCallback} [callback] The command result callback
- */
- function parallelCollectionScan(coll, options, callback) {
- // Create command object
- const commandObject = {
- parallelCollectionScan: coll.s.name,
- numCursors: options.numCursors
- };
- // Do we have a readConcern specified
- decorateWithReadConcern(commandObject, coll, options);
- // Store the raw value
- const raw = options.raw;
- delete options['raw'];
- // Execute the command
- executeCommand(coll.s.db, commandObject, options, (err, result) => {
- if (err) return handleCallback(callback, err, null);
- if (result == null)
- return handleCallback(
- callback,
- new Error('no result returned for parallelCollectionScan'),
- null
- );
- options = Object.assign({ explicitlyIgnoreSession: true }, options);
- const cursors = [];
- // Add the raw back to the option
- if (raw) options.raw = raw;
- // Create command cursors for each item
- for (let i = 0; i < result.cursors.length; i++) {
- const rawId = result.cursors[i].cursor.id;
- // Convert cursorId to Long if needed
- const cursorId = typeof rawId === 'number' ? Long.fromNumber(rawId) : rawId;
- // Add a command cursor
- cursors.push(coll.s.topology.cursor(coll.s.namespace, cursorId, options));
- }
- handleCallback(callback, null, cursors);
- });
- }
- // modifies documents before being inserted or updated
- function prepareDocs(coll, docs, options) {
- const forceServerObjectId =
- typeof options.forceServerObjectId === 'boolean'
- ? options.forceServerObjectId
- : coll.s.db.options.forceServerObjectId;
- // no need to modify the docs if server sets the ObjectId
- if (forceServerObjectId === true) {
- return docs;
- }
- return docs.map(doc => {
- if (forceServerObjectId !== true && doc._id == null) {
- doc._id = coll.s.pkFactory.createPk();
- }
- return doc;
- });
- }
- /**
- * Functions that are passed as scope args must
- * be converted to Code instances.
- * @ignore
- */
- function processScope(scope) {
- if (!isObject(scope) || scope._bsontype === 'ObjectID') {
- return scope;
- }
- const keys = Object.keys(scope);
- let key;
- const new_scope = {};
- for (let i = keys.length - 1; i >= 0; i--) {
- key = keys[i];
- if ('function' === typeof scope[key]) {
- new_scope[key] = new Code(String(scope[key]));
- } else {
- new_scope[key] = processScope(scope[key]);
- }
- }
- return new_scope;
- }
- /**
- * Reindex all indexes on the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {Object} [options] Optional settings. See Collection.prototype.reIndex for a list of options.
- * @param {Collection~resultCallback} [callback] The command result callback
- */
- function reIndex(coll, options, callback) {
- // Reindex
- const cmd = { reIndex: coll.s.name };
- // Execute the command
- executeCommand(coll.s.db, cmd, options, (err, result) => {
- if (callback == null) return;
- if (err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result.ok ? true : false);
- });
- }
- function removeDocuments(coll, selector, options, callback) {
- if (typeof options === 'function') {
- (callback = options), (options = {});
- } else if (typeof selector === 'function') {
- callback = selector;
- options = {};
- selector = {};
- }
- // Create an empty options object if the provided one is null
- options = options || {};
- // Final options for retryable writes and write concern
- let finalOptions = Object.assign({}, options);
- finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
- finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
- // If selector is null set empty
- if (selector == null) selector = {};
- // Build the op
- const op = { q: selector, limit: 0 };
- if (options.single) {
- op.limit = 1;
- } else if (finalOptions.retryWrites) {
- finalOptions.retryWrites = false;
- }
- // Have we specified collation
- try {
- decorateWithCollation(finalOptions, coll, options);
- } catch (err) {
- return callback(err, null);
- }
- // Execute the remove
- coll.s.topology.remove(coll.s.namespace, [op], finalOptions, (err, result) => {
- if (callback == null) return;
- if (err) return handleCallback(callback, err, null);
- if (result == null) return handleCallback(callback, null, null);
- if (result.result.code) return handleCallback(callback, toError(result.result));
- if (result.result.writeErrors)
- return handleCallback(callback, toError(result.result.writeErrors[0]));
- // Return the results
- handleCallback(callback, null, result);
- });
- }
- /**
- * Rename the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {string} newName New name of of the collection.
- * @param {object} [options] Optional settings. See Collection.prototype.rename for a list of options.
- * @param {Collection~collectionResultCallback} [callback] The results callback
- */
- function rename(coll, newName, options, callback) {
- let Collection = loadCollection();
- // Check the collection name
- checkCollectionName(newName);
- // Build the command
- const renameCollection = `${coll.s.dbName}.${coll.s.name}`;
- const toCollection = `${coll.s.dbName}.${newName}`;
- const dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
- const cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
- // Decorate command with writeConcern if supported
- applyWriteConcern(cmd, { db: coll.s.db, collection: coll }, options);
- // Execute against admin
- executeDbAdminCommand(coll.s.db.admin().s.db, cmd, options, (err, doc) => {
- if (err) return handleCallback(callback, err, null);
- // We have an error
- if (doc.errmsg) return handleCallback(callback, toError(doc), null);
- try {
- return handleCallback(
- callback,
- null,
- new Collection(
- coll.s.db,
- coll.s.topology,
- coll.s.dbName,
- newName,
- coll.s.pkFactory,
- coll.s.options
- )
- );
- } catch (err) {
- return handleCallback(callback, toError(err), null);
- }
- });
- }
- /**
- * Replace a document in the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter The Filter used to select the document to update
- * @param {object} doc The Document that replaces the matching document
- * @param {object} [options] Optional settings. See Collection.prototype.replaceOne for a list of options.
- * @param {Collection~updateWriteOpCallback} [callback] The command result callback
- */
- function replaceOne(coll, filter, doc, options, callback) {
- // Set single document update
- options.multi = false;
- // Execute update
- updateDocuments(coll, filter, doc, options, (err, r) => {
- if (callback == null) return;
- if (err && callback) return callback(err);
- if (r == null) return callback(null, { result: { ok: 1 } });
- r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
- r.upsertedId =
- Array.isArray(r.result.upserted) && r.result.upserted.length > 0
- ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
- : null;
- r.upsertedCount =
- Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
- r.matchedCount =
- Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
- r.ops = [doc];
- if (callback) callback(null, r);
- });
- }
- /**
- * Save a document.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} doc Document to save
- * @param {object} [options] Optional settings. See Collection.prototype.save for a list of options.
- * @param {Collection~writeOpCallback} [callback] The command result callback
- * @deprecated use insertOne, insertMany, updateOne or updateMany
- */
- function save(coll, doc, options, callback) {
- // Get the write concern options
- const finalOptions = applyWriteConcern(
- Object.assign({}, options),
- { db: coll.s.db, collection: coll },
- options
- );
- // Establish if we need to perform an insert or update
- if (doc._id != null) {
- finalOptions.upsert = true;
- return updateDocuments(coll, { _id: doc._id }, doc, finalOptions, callback);
- }
- // Insert the document
- insertDocuments(coll, [doc], finalOptions, (err, result) => {
- if (callback == null) return;
- if (doc == null) return handleCallback(callback, null, null);
- if (err) return handleCallback(callback, err, null);
- handleCallback(callback, null, result);
- });
- }
- /**
- * Get all the collection statistics.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} [options] Optional settings. See Collection.prototype.stats for a list of options.
- * @param {Collection~resultCallback} [callback] The collection result callback
- */
- function stats(coll, options, callback) {
- // Build command object
- const commandObject = {
- collStats: coll.s.name
- };
- // Check if we have the scale value
- if (options['scale'] != null) commandObject['scale'] = options['scale'];
- options = Object.assign({}, options);
- // Ensure we have the right read preference inheritance
- options.readPreference = resolveReadPreference(options, { db: coll.s.db, collection: coll });
- // Execute the command
- executeCommand(coll.s.db, commandObject, options, callback);
- }
- function updateCallback(err, r, callback) {
- if (callback == null) return;
- if (err) return callback(err);
- if (r == null) return callback(null, { result: { ok: 1 } });
- r.modifiedCount = r.result.nModified != null ? r.result.nModified : r.result.n;
- r.upsertedId =
- Array.isArray(r.result.upserted) && r.result.upserted.length > 0
- ? r.result.upserted[0] // FIXME(major): should be `r.result.upserted[0]._id`
- : null;
- r.upsertedCount =
- Array.isArray(r.result.upserted) && r.result.upserted.length ? r.result.upserted.length : 0;
- r.matchedCount =
- Array.isArray(r.result.upserted) && r.result.upserted.length > 0 ? 0 : r.result.n;
- callback(null, r);
- }
- function updateDocuments(coll, selector, document, options, callback) {
- if ('function' === typeof options) (callback = options), (options = null);
- if (options == null) options = {};
- if (!('function' === typeof callback)) callback = null;
- // If we are not providing a selector or document throw
- if (selector == null || typeof selector !== 'object')
- return callback(toError('selector must be a valid JavaScript object'));
- if (document == null || typeof document !== 'object')
- return callback(toError('document must be a valid JavaScript object'));
- // Final options for retryable writes and write concern
- let finalOptions = Object.assign({}, options);
- finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
- finalOptions = applyWriteConcern(finalOptions, { db: coll.s.db, collection: coll }, options);
- // Do we return the actual result document
- // Either use override on the function, or go back to default on either the collection
- // level or db
- finalOptions.serializeFunctions = options.serializeFunctions || coll.s.serializeFunctions;
- // Execute the operation
- const op = { q: selector, u: document };
- op.upsert = options.upsert !== void 0 ? !!options.upsert : false;
- op.multi = options.multi !== void 0 ? !!options.multi : false;
- if (finalOptions.arrayFilters) {
- op.arrayFilters = finalOptions.arrayFilters;
- delete finalOptions.arrayFilters;
- }
- if (finalOptions.retryWrites && op.multi) {
- finalOptions.retryWrites = false;
- }
- // Have we specified collation
- try {
- decorateWithCollation(finalOptions, coll, options);
- } catch (err) {
- return callback(err, null);
- }
- // Update options
- coll.s.topology.update(coll.s.namespace, [op], finalOptions, (err, result) => {
- if (callback == null) return;
- if (err) return handleCallback(callback, err, null);
- if (result == null) return handleCallback(callback, null, null);
- if (result.result.code) return handleCallback(callback, toError(result.result));
- if (result.result.writeErrors)
- return handleCallback(callback, toError(result.result.writeErrors[0]));
- // Return the results
- handleCallback(callback, null, result);
- });
- }
- /**
- * Update multiple documents in the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter The Filter used to select the documents to update
- * @param {object} update The update operations to be applied to the document
- * @param {object} [options] Optional settings. See Collection.prototype.updateMany for a list of options.
- * @param {Collection~updateWriteOpCallback} [callback] The command result callback
- */
- function updateMany(coll, filter, update, options, callback) {
- // Set single document update
- options.multi = true;
- // Execute update
- updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
- }
- /**
- * Update a single document in the collection.
- *
- * @method
- * @param {Collection} a Collection instance.
- * @param {object} filter The Filter used to select the document to update
- * @param {object} update The update operations to be applied to the document
- * @param {object} [options] Optional settings. See Collection.prototype.updateOne for a list of options.
- * @param {Collection~updateWriteOpCallback} [callback] The command result callback
- */
- function updateOne(coll, filter, update, options, callback) {
- // Set single document update
- options.multi = false;
- // Execute update
- updateDocuments(coll, filter, update, options, (err, r) => updateCallback(err, r, callback));
- }
- module.exports = {
- bulkWrite,
- checkForAtomicOperators,
- count,
- countDocuments,
- buildCountCommand,
- createIndex,
- createIndexes,
- deleteMany,
- deleteOne,
- distinct,
- dropIndex,
- dropIndexes,
- ensureIndex,
- findAndModify,
- findAndRemove,
- findOne,
- findOneAndDelete,
- findOneAndReplace,
- findOneAndUpdate,
- geoHaystackSearch,
- group,
- indexes,
- indexExists,
- indexInformation,
- insertMany,
- insertOne,
- isCapped,
- mapReduce,
- optionsOp,
- parallelCollectionScan,
- prepareDocs,
- reIndex,
- removeDocuments,
- rename,
- replaceOne,
- save,
- stats,
- updateDocuments,
- updateMany,
- updateOne
- };
|