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