UNPKG

156 kBJavaScriptView Raw
1'use strict';
2
3/*!
4 * Module dependencies.
5 */
6
7const CastError = require('./error/cast');
8const DocumentNotFoundError = require('./error/notFound');
9const Kareem = require('kareem');
10const MongooseError = require('./error/mongooseError');
11const ObjectParameterError = require('./error/objectParameter');
12const QueryCursor = require('./cursor/QueryCursor');
13const ReadPreference = require('./driver').get().ReadPreference;
14const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
15const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
16const cast = require('./cast');
17const castArrayFilters = require('./helpers/update/castArrayFilters');
18const castUpdate = require('./helpers/query/castUpdate');
19const completeMany = require('./helpers/query/completeMany');
20const get = require('./helpers/get');
21const hasDollarKeys = require('./helpers/query/hasDollarKeys');
22const helpers = require('./queryhelpers');
23const isInclusive = require('./helpers/projection/isInclusive');
24const mquery = require('mquery');
25const selectPopulatedFields = require('./helpers/query/selectPopulatedFields');
26const setDefaultsOnInsert = require('./helpers/setDefaultsOnInsert');
27const slice = require('sliced');
28const updateValidators = require('./helpers/updateValidators');
29const util = require('util');
30const utils = require('./utils');
31const wrapThunk = require('./helpers/query/wrapThunk');
32
33/**
34 * Query constructor used for building queries. You do not need
35 * to instantiate a `Query` directly. Instead use Model functions like
36 * [`Model.find()`](/docs/api.html#find_find).
37 *
38 * ####Example:
39 *
40 * const query = MyModel.find(); // `query` is an instance of `Query`
41 * query.setOptions({ lean : true });
42 * query.collection(MyModel.collection);
43 * query.where('age').gte(21).exec(callback);
44 *
45 * // You can instantiate a query directly. There is no need to do
46 * // this unless you're an advanced user with a very good reason to.
47 * const query = new mongoose.Query();
48 *
49 * @param {Object} [options]
50 * @param {Object} [model]
51 * @param {Object} [conditions]
52 * @param {Object} [collection] Mongoose collection
53 * @api public
54 */
55
56function Query(conditions, options, model, collection) {
57 // this stuff is for dealing with custom queries created by #toConstructor
58 if (!this._mongooseOptions) {
59 this._mongooseOptions = {};
60 }
61 options = options || {};
62
63 this._transforms = [];
64 this._hooks = new Kareem();
65 this._executionCount = 0;
66
67 // this is the case where we have a CustomQuery, we need to check if we got
68 // options passed in, and if we did, merge them in
69 const keys = Object.keys(options);
70 for (let i = 0; i < keys.length; ++i) {
71 const k = keys[i];
72 this._mongooseOptions[k] = options[k];
73 }
74
75 if (collection) {
76 this.mongooseCollection = collection;
77 }
78
79 if (model) {
80 this.model = model;
81 this.schema = model.schema;
82 }
83
84 // this is needed because map reduce returns a model that can be queried, but
85 // all of the queries on said model should be lean
86 if (this.model && this.model._mapreduce) {
87 this.lean();
88 }
89
90 // inherit mquery
91 mquery.call(this, this.mongooseCollection, options);
92
93 if (conditions) {
94 this.find(conditions);
95 }
96
97 this.options = this.options || {};
98
99 // For gh-6880. mquery still needs to support `fields` by default for old
100 // versions of MongoDB
101 this.$useProjection = true;
102
103 const collation = get(this, 'schema.options.collation', null);
104 if (collation != null) {
105 this.options.collation = collation;
106 }
107}
108
109/*!
110 * inherit mquery
111 */
112
113Query.prototype = new mquery;
114Query.prototype.constructor = Query;
115Query.base = mquery.prototype;
116
117/**
118 * Flag to opt out of using `$geoWithin`.
119 *
120 * mongoose.Query.use$geoWithin = false;
121 *
122 * MongoDB 2.4 deprecated the use of `$within`, replacing it with `$geoWithin`. Mongoose uses `$geoWithin` by default (which is 100% backward compatible with `$within`). If you are running an older version of MongoDB, set this flag to `false` so your `within()` queries continue to work.
123 *
124 * @see http://docs.mongodb.org/manual/reference/operator/geoWithin/
125 * @default true
126 * @property use$geoWithin
127 * @memberOf Query
128 * @receiver Query
129 * @api public
130 */
131
132Query.use$geoWithin = mquery.use$geoWithin;
133
134/**
135 * Converts this query to a customized, reusable query constructor with all arguments and options retained.
136 *
137 * ####Example
138 *
139 * // Create a query for adventure movies and read from the primary
140 * // node in the replica-set unless it is down, in which case we'll
141 * // read from a secondary node.
142 * var query = Movie.find({ tags: 'adventure' }).read('primaryPreferred');
143 *
144 * // create a custom Query constructor based off these settings
145 * var Adventure = query.toConstructor();
146 *
147 * // Adventure is now a subclass of mongoose.Query and works the same way but with the
148 * // default query parameters and options set.
149 * Adventure().exec(callback)
150 *
151 * // further narrow down our query results while still using the previous settings
152 * Adventure().where({ name: /^Life/ }).exec(callback);
153 *
154 * // since Adventure is a stand-alone constructor we can also add our own
155 * // helper methods and getters without impacting global queries
156 * Adventure.prototype.startsWith = function (prefix) {
157 * this.where({ name: new RegExp('^' + prefix) })
158 * return this;
159 * }
160 * Object.defineProperty(Adventure.prototype, 'highlyRated', {
161 * get: function () {
162 * this.where({ rating: { $gt: 4.5 }});
163 * return this;
164 * }
165 * })
166 * Adventure().highlyRated.startsWith('Life').exec(callback)
167 *
168 * @return {Query} subclass-of-Query
169 * @api public
170 */
171
172Query.prototype.toConstructor = function toConstructor() {
173 const model = this.model;
174 const coll = this.mongooseCollection;
175
176 const CustomQuery = function(criteria, options) {
177 if (!(this instanceof CustomQuery)) {
178 return new CustomQuery(criteria, options);
179 }
180 this._mongooseOptions = utils.clone(p._mongooseOptions);
181 Query.call(this, criteria, options || null, model, coll);
182 };
183
184 util.inherits(CustomQuery, model.Query);
185
186 // set inherited defaults
187 const p = CustomQuery.prototype;
188
189 p.options = {};
190
191 p.setOptions(this.options);
192
193 p.op = this.op;
194 p._conditions = utils.clone(this._conditions);
195 p._fields = utils.clone(this._fields);
196 p._update = utils.clone(this._update, {
197 flattenDecimals: false
198 });
199 p._path = this._path;
200 p._distinct = this._distinct;
201 p._collection = this._collection;
202 p._mongooseOptions = this._mongooseOptions;
203
204 return CustomQuery;
205};
206
207/**
208 * Specifies a javascript function or expression to pass to MongoDBs query system.
209 *
210 * ####Example
211 *
212 * query.$where('this.comments.length === 10 || this.name.length === 5')
213 *
214 * // or
215 *
216 * query.$where(function () {
217 * return this.comments.length === 10 || this.name.length === 5;
218 * })
219 *
220 * ####NOTE:
221 *
222 * Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`.
223 * **Be sure to read about all of [its caveats](http://docs.mongodb.org/manual/reference/operator/where/) before using.**
224 *
225 * @see $where http://docs.mongodb.org/manual/reference/operator/where/
226 * @method $where
227 * @param {String|Function} js javascript string or function
228 * @return {Query} this
229 * @memberOf Query
230 * @instance
231 * @method $where
232 * @api public
233 */
234
235/**
236 * Specifies a `path` for use with chaining.
237 *
238 * ####Example
239 *
240 * // instead of writing:
241 * User.find({age: {$gte: 21, $lte: 65}}, callback);
242 *
243 * // we can instead write:
244 * User.where('age').gte(21).lte(65);
245 *
246 * // passing query conditions is permitted
247 * User.find().where({ name: 'vonderful' })
248 *
249 * // chaining
250 * User
251 * .where('age').gte(21).lte(65)
252 * .where('name', /^vonderful/i)
253 * .where('friends').slice(10)
254 * .exec(callback)
255 *
256 * @method where
257 * @memberOf Query
258 * @instance
259 * @param {String|Object} [path]
260 * @param {any} [val]
261 * @return {Query} this
262 * @api public
263 */
264
265Query.prototype.slice = function() {
266 if (arguments.length === 0) {
267 return this;
268 }
269
270 this._validate('slice');
271
272 let path;
273 let val;
274
275 if (arguments.length === 1) {
276 const arg = arguments[0];
277 if (typeof arg === 'object' && !Array.isArray(arg)) {
278 const keys = Object.keys(arg);
279 const numKeys = keys.length;
280 for (let i = 0; i < numKeys; ++i) {
281 this.slice(keys[i], arg[keys[i]]);
282 }
283 return this;
284 }
285 this._ensurePath('slice');
286 path = this._path;
287 val = arguments[0];
288 } else if (arguments.length === 2) {
289 if ('number' === typeof arguments[0]) {
290 this._ensurePath('slice');
291 path = this._path;
292 val = slice(arguments);
293 } else {
294 path = arguments[0];
295 val = arguments[1];
296 }
297 } else if (arguments.length === 3) {
298 path = arguments[0];
299 val = slice(arguments, 1);
300 }
301
302 const p = {};
303 p[path] = { $slice: val };
304 this.select(p);
305
306 return this;
307};
308
309
310/**
311 * Specifies the complementary comparison value for paths specified with `where()`
312 *
313 * ####Example
314 *
315 * User.where('age').equals(49);
316 *
317 * // is the same as
318 *
319 * User.where('age', 49);
320 *
321 * @method equals
322 * @memberOf Query
323 * @instance
324 * @param {Object} val
325 * @return {Query} this
326 * @api public
327 */
328
329/**
330 * Specifies arguments for an `$or` condition.
331 *
332 * ####Example
333 *
334 * query.or([{ color: 'red' }, { status: 'emergency' }])
335 *
336 * @see $or http://docs.mongodb.org/manual/reference/operator/or/
337 * @method or
338 * @memberOf Query
339 * @instance
340 * @param {Array} array array of conditions
341 * @return {Query} this
342 * @api public
343 */
344
345/**
346 * Specifies arguments for a `$nor` condition.
347 *
348 * ####Example
349 *
350 * query.nor([{ color: 'green' }, { status: 'ok' }])
351 *
352 * @see $nor http://docs.mongodb.org/manual/reference/operator/nor/
353 * @method nor
354 * @memberOf Query
355 * @instance
356 * @param {Array} array array of conditions
357 * @return {Query} this
358 * @api public
359 */
360
361/**
362 * Specifies arguments for a `$and` condition.
363 *
364 * ####Example
365 *
366 * query.and([{ color: 'green' }, { status: 'ok' }])
367 *
368 * @method and
369 * @memberOf Query
370 * @instance
371 * @see $and http://docs.mongodb.org/manual/reference/operator/and/
372 * @param {Array} array array of conditions
373 * @return {Query} this
374 * @api public
375 */
376
377/**
378 * Specifies a `$gt` query condition.
379 *
380 * When called with one argument, the most recent path passed to `where()` is used.
381 *
382 * ####Example
383 *
384 * Thing.find().where('age').gt(21)
385 *
386 * // or
387 * Thing.find().gt('age', 21)
388 *
389 * @method gt
390 * @memberOf Query
391 * @instance
392 * @param {String} [path]
393 * @param {Number} val
394 * @see $gt http://docs.mongodb.org/manual/reference/operator/gt/
395 * @api public
396 */
397
398/**
399 * Specifies a `$gte` query condition.
400 *
401 * When called with one argument, the most recent path passed to `where()` is used.
402 *
403 * @method gte
404 * @memberOf Query
405 * @instance
406 * @param {String} [path]
407 * @param {Number} val
408 * @see $gte http://docs.mongodb.org/manual/reference/operator/gte/
409 * @api public
410 */
411
412/**
413 * Specifies a `$lt` query condition.
414 *
415 * When called with one argument, the most recent path passed to `where()` is used.
416 *
417 * @method lt
418 * @memberOf Query
419 * @instance
420 * @param {String} [path]
421 * @param {Number} val
422 * @see $lt http://docs.mongodb.org/manual/reference/operator/lt/
423 * @api public
424 */
425
426/**
427 * Specifies a `$lte` query condition.
428 *
429 * When called with one argument, the most recent path passed to `where()` is used.
430 *
431 * @method lte
432 * @see $lte http://docs.mongodb.org/manual/reference/operator/lte/
433 * @memberOf Query
434 * @instance
435 * @param {String} [path]
436 * @param {Number} val
437 * @api public
438 */
439
440/**
441 * Specifies a `$ne` query condition.
442 *
443 * When called with one argument, the most recent path passed to `where()` is used.
444 *
445 * @see $ne http://docs.mongodb.org/manual/reference/operator/ne/
446 * @method ne
447 * @memberOf Query
448 * @instance
449 * @param {String} [path]
450 * @param {Number} val
451 * @api public
452 */
453
454/**
455 * Specifies an `$in` query condition.
456 *
457 * When called with one argument, the most recent path passed to `where()` is used.
458 *
459 * @see $in http://docs.mongodb.org/manual/reference/operator/in/
460 * @method in
461 * @memberOf Query
462 * @instance
463 * @param {String} [path]
464 * @param {Number} val
465 * @api public
466 */
467
468/**
469 * Specifies an `$nin` query condition.
470 *
471 * When called with one argument, the most recent path passed to `where()` is used.
472 *
473 * @see $nin http://docs.mongodb.org/manual/reference/operator/nin/
474 * @method nin
475 * @memberOf Query
476 * @instance
477 * @param {String} [path]
478 * @param {Number} val
479 * @api public
480 */
481
482/**
483 * Specifies an `$all` query condition.
484 *
485 * When called with one argument, the most recent path passed to `where()` is used.
486 *
487 * ####Example:
488 *
489 * MyModel.find().where('pets').all(['dog', 'cat', 'ferret']);
490 * // Equivalent:
491 * MyModel.find().all('pets', ['dog', 'cat', 'ferret']);
492 *
493 * @see $all http://docs.mongodb.org/manual/reference/operator/all/
494 * @method all
495 * @memberOf Query
496 * @instance
497 * @param {String} [path]
498 * @param {Array} val
499 * @api public
500 */
501
502/**
503 * Specifies a `$size` query condition.
504 *
505 * When called with one argument, the most recent path passed to `where()` is used.
506 *
507 * ####Example
508 *
509 * MyModel.where('tags').size(0).exec(function (err, docs) {
510 * if (err) return handleError(err);
511 *
512 * assert(Array.isArray(docs));
513 * console.log('documents with 0 tags', docs);
514 * })
515 *
516 * @see $size http://docs.mongodb.org/manual/reference/operator/size/
517 * @method size
518 * @memberOf Query
519 * @instance
520 * @param {String} [path]
521 * @param {Number} val
522 * @api public
523 */
524
525/**
526 * Specifies a `$regex` query condition.
527 *
528 * When called with one argument, the most recent path passed to `where()` is used.
529 *
530 * @see $regex http://docs.mongodb.org/manual/reference/operator/regex/
531 * @method regex
532 * @memberOf Query
533 * @instance
534 * @param {String} [path]
535 * @param {String|RegExp} val
536 * @api public
537 */
538
539/**
540 * Specifies a `maxDistance` query condition.
541 *
542 * When called with one argument, the most recent path passed to `where()` is used.
543 *
544 * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
545 * @method maxDistance
546 * @memberOf Query
547 * @instance
548 * @param {String} [path]
549 * @param {Number} val
550 * @api public
551 */
552
553/**
554 * Specifies a `$mod` condition, filters documents for documents whose
555 * `path` property is a number that is equal to `remainder` modulo `divisor`.
556 *
557 * ####Example
558 *
559 * // All find products whose inventory is odd
560 * Product.find().mod('inventory', [2, 1]);
561 * Product.find().where('inventory').mod([2, 1]);
562 * // This syntax is a little strange, but supported.
563 * Product.find().where('inventory').mod(2, 1);
564 *
565 * @method mod
566 * @memberOf Query
567 * @instance
568 * @param {String} [path]
569 * @param {Array} val must be of length 2, first element is `divisor`, 2nd element is `remainder`.
570 * @return {Query} this
571 * @see $mod http://docs.mongodb.org/manual/reference/operator/mod/
572 * @api public
573 */
574
575Query.prototype.mod = function() {
576 let val;
577 let path;
578
579 if (arguments.length === 1) {
580 this._ensurePath('mod');
581 val = arguments[0];
582 path = this._path;
583 } else if (arguments.length === 2 && !Array.isArray(arguments[1])) {
584 this._ensurePath('mod');
585 val = slice(arguments);
586 path = this._path;
587 } else if (arguments.length === 3) {
588 val = slice(arguments, 1);
589 path = arguments[0];
590 } else {
591 val = arguments[1];
592 path = arguments[0];
593 }
594
595 const conds = this._conditions[path] || (this._conditions[path] = {});
596 conds.$mod = val;
597 return this;
598};
599
600/**
601 * Specifies an `$exists` condition
602 *
603 * ####Example
604 *
605 * // { name: { $exists: true }}
606 * Thing.where('name').exists()
607 * Thing.where('name').exists(true)
608 * Thing.find().exists('name')
609 *
610 * // { name: { $exists: false }}
611 * Thing.where('name').exists(false);
612 * Thing.find().exists('name', false);
613 *
614 * @method exists
615 * @memberOf Query
616 * @instance
617 * @param {String} [path]
618 * @param {Number} val
619 * @return {Query} this
620 * @see $exists http://docs.mongodb.org/manual/reference/operator/exists/
621 * @api public
622 */
623
624/**
625 * Specifies an `$elemMatch` condition
626 *
627 * ####Example
628 *
629 * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
630 *
631 * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
632 *
633 * query.elemMatch('comment', function (elem) {
634 * elem.where('author').equals('autobot');
635 * elem.where('votes').gte(5);
636 * })
637 *
638 * query.where('comment').elemMatch(function (elem) {
639 * elem.where({ author: 'autobot' });
640 * elem.where('votes').gte(5);
641 * })
642 *
643 * @method elemMatch
644 * @memberOf Query
645 * @instance
646 * @param {String|Object|Function} path
647 * @param {Object|Function} filter
648 * @return {Query} this
649 * @see $elemMatch http://docs.mongodb.org/manual/reference/operator/elemMatch/
650 * @api public
651 */
652
653/**
654 * Defines a `$within` or `$geoWithin` argument for geo-spatial queries.
655 *
656 * ####Example
657 *
658 * query.where(path).within().box()
659 * query.where(path).within().circle()
660 * query.where(path).within().geometry()
661 *
662 * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
663 * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
664 * query.where('loc').within({ polygon: [[],[],[],[]] });
665 *
666 * query.where('loc').within([], [], []) // polygon
667 * query.where('loc').within([], []) // box
668 * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
669 *
670 * **MUST** be used after `where()`.
671 *
672 * ####NOTE:
673 *
674 * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](#query_Query-use%2524geoWithin).
675 *
676 * ####NOTE:
677 *
678 * In Mongoose 3.7, `within` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
679 *
680 * @method within
681 * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
682 * @see $box http://docs.mongodb.org/manual/reference/operator/box/
683 * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
684 * @see $center http://docs.mongodb.org/manual/reference/operator/center/
685 * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
686 * @memberOf Query
687 * @instance
688 * @return {Query} this
689 * @api public
690 */
691
692/**
693 * Specifies a `$slice` projection for an array.
694 *
695 * ####Example
696 *
697 * query.slice('comments', 5)
698 * query.slice('comments', -5)
699 * query.slice('comments', [10, 5])
700 * query.where('comments').slice(5)
701 * query.where('comments').slice([-10, 5])
702 *
703 * @method slice
704 * @memberOf Query
705 * @instance
706 * @param {String} [path]
707 * @param {Number} val number/range of elements to slice
708 * @return {Query} this
709 * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
710 * @see $slice http://docs.mongodb.org/manual/reference/projection/slice/#prj._S_slice
711 * @api public
712 */
713
714/**
715 * Specifies the maximum number of documents the query will return.
716 *
717 * ####Example
718 *
719 * query.limit(20)
720 *
721 * ####Note
722 *
723 * Cannot be used with `distinct()`
724 *
725 * @method limit
726 * @memberOf Query
727 * @instance
728 * @param {Number} val
729 * @api public
730 */
731
732/**
733 * Specifies the number of documents to skip.
734 *
735 * ####Example
736 *
737 * query.skip(100).limit(20)
738 *
739 * ####Note
740 *
741 * Cannot be used with `distinct()`
742 *
743 * @method skip
744 * @memberOf Query
745 * @instance
746 * @param {Number} val
747 * @see cursor.skip http://docs.mongodb.org/manual/reference/method/cursor.skip/
748 * @api public
749 */
750
751/**
752 * Specifies the maxScan option.
753 *
754 * ####Example
755 *
756 * query.maxScan(100)
757 *
758 * ####Note
759 *
760 * Cannot be used with `distinct()`
761 *
762 * @method maxScan
763 * @memberOf Query
764 * @instance
765 * @param {Number} val
766 * @see maxScan http://docs.mongodb.org/manual/reference/operator/maxScan/
767 * @api public
768 */
769
770/**
771 * Specifies the batchSize option.
772 *
773 * ####Example
774 *
775 * query.batchSize(100)
776 *
777 * ####Note
778 *
779 * Cannot be used with `distinct()`
780 *
781 * @method batchSize
782 * @memberOf Query
783 * @instance
784 * @param {Number} val
785 * @see batchSize http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
786 * @api public
787 */
788
789/**
790 * Specifies the `comment` option.
791 *
792 * ####Example
793 *
794 * query.comment('login query')
795 *
796 * ####Note
797 *
798 * Cannot be used with `distinct()`
799 *
800 * @method comment
801 * @memberOf Query
802 * @instance
803 * @param {String} val
804 * @see comment http://docs.mongodb.org/manual/reference/operator/comment/
805 * @api public
806 */
807
808/**
809 * Specifies this query as a `snapshot` query.
810 *
811 * ####Example
812 *
813 * query.snapshot() // true
814 * query.snapshot(true)
815 * query.snapshot(false)
816 *
817 * ####Note
818 *
819 * Cannot be used with `distinct()`
820 *
821 * @method snapshot
822 * @memberOf Query
823 * @instance
824 * @see snapshot http://docs.mongodb.org/manual/reference/operator/snapshot/
825 * @return {Query} this
826 * @api public
827 */
828
829/**
830 * Sets query hints.
831 *
832 * ####Example
833 *
834 * query.hint({ indexA: 1, indexB: -1})
835 *
836 * ####Note
837 *
838 * Cannot be used with `distinct()`
839 *
840 * @method hint
841 * @memberOf Query
842 * @instance
843 * @param {Object} val a hint object
844 * @return {Query} this
845 * @see $hint http://docs.mongodb.org/manual/reference/operator/hint/
846 * @api public
847 */
848
849/**
850 * Get/set the current projection (AKA fields). Pass `null` to remove the
851 * current projection.
852 *
853 * Unlike `projection()`, the `select()` function modifies the current
854 * projection in place. This function overwrites the existing projection.
855 *
856 * ####Example:
857 *
858 * const q = Model.find();
859 * q.projection(); // null
860 *
861 * q.select('a b');
862 * q.projection(); // { a: 1, b: 1 }
863 *
864 * q.projection({ c: 1 });
865 * q.projection(); // { c: 1 }
866 *
867 * q.projection(null);
868 * q.projection(); // null
869 *
870 *
871 * @method projection
872 * @memberOf Query
873 * @instance
874 * @param {Object|null} arg
875 * @return {Object} the current projection
876 * @api public
877 */
878
879Query.prototype.projection = function(arg) {
880 if (arguments.length === 0) {
881 return this._fields;
882 }
883
884 this._fields = {};
885 this._userProvidedFields = {};
886 this.select(arg);
887 return this._fields;
888};
889
890/**
891 * Specifies which document fields to include or exclude (also known as the query "projection")
892 *
893 * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](/docs/api.html#schematype_SchemaType-select).
894 *
895 * A projection _must_ be either inclusive or exclusive. In other words, you must
896 * either list the fields to include (which excludes all others), or list the fields
897 * to exclude (which implies all other fields are included). The [`_id` field is the only exception because MongoDB includes it by default](https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/#suppress-id-field).
898 *
899 * ####Example
900 *
901 * // include a and b, exclude other fields
902 * query.select('a b');
903 *
904 * // exclude c and d, include other fields
905 * query.select('-c -d');
906 *
907 * // Use `+` to override schema-level `select: false` without making the
908 * // projection inclusive.
909 * const schema = new Schema({
910 * foo: { type: String, select: false },
911 * bar: String
912 * });
913 * // ...
914 * query.select('+foo'); // Override foo's `select: false` without excluding `bar`
915 *
916 * // or you may use object notation, useful when
917 * // you have keys already prefixed with a "-"
918 * query.select({ a: 1, b: 1 });
919 * query.select({ c: 0, d: 0 });
920 *
921 *
922 * @method select
923 * @memberOf Query
924 * @instance
925 * @param {Object|String} arg
926 * @return {Query} this
927 * @see SchemaType
928 * @api public
929 */
930
931Query.prototype.select = function select() {
932 let arg = arguments[0];
933 if (!arg) return this;
934 let i;
935 let len;
936
937 if (arguments.length !== 1) {
938 throw new Error('Invalid select: select only takes 1 argument');
939 }
940
941 this._validate('select');
942
943 const fields = this._fields || (this._fields = {});
944 const userProvidedFields = this._userProvidedFields || (this._userProvidedFields = {});
945 const type = typeof arg;
946
947 if (('string' == type || Object.prototype.toString.call(arg) === '[object Arguments]') &&
948 'number' == typeof arg.length || Array.isArray(arg)) {
949 if ('string' == type)
950 arg = arg.split(/\s+/);
951
952 for (i = 0, len = arg.length; i < len; ++i) {
953 let field = arg[i];
954 if (!field) continue;
955 const include = '-' == field[0] ? 0 : 1;
956 if (include === 0) field = field.substring(1);
957 fields[field] = include;
958 userProvidedFields[field] = include;
959 }
960 return this;
961 }
962
963 if (utils.isObject(arg)) {
964 const keys = Object.keys(arg);
965 for (i = 0; i < keys.length; ++i) {
966 fields[keys[i]] = arg[keys[i]];
967 userProvidedFields[keys[i]] = arg[keys[i]];
968 }
969 return this;
970 }
971
972 throw new TypeError('Invalid select() argument. Must be string or object.');
973};
974
975/**
976 * _DEPRECATED_ Sets the slaveOk option.
977 *
978 * **Deprecated** in MongoDB 2.2 in favor of [read preferences](#query_Query-read).
979 *
980 * ####Example:
981 *
982 * query.slaveOk() // true
983 * query.slaveOk(true)
984 * query.slaveOk(false)
985 *
986 * @method slaveOk
987 * @memberOf Query
988 * @instance
989 * @deprecated use read() preferences instead if on mongodb >= 2.2
990 * @param {Boolean} v defaults to true
991 * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
992 * @see slaveOk http://docs.mongodb.org/manual/reference/method/rs.slaveOk/
993 * @see read() #query_Query-read
994 * @return {Query} this
995 * @api public
996 */
997
998/**
999 * Determines the MongoDB nodes from which to read.
1000 *
1001 * ####Preferences:
1002 *
1003 * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
1004 * secondary Read from secondary if available, otherwise error.
1005 * primaryPreferred Read from primary if available, otherwise a secondary.
1006 * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
1007 * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
1008 *
1009 * Aliases
1010 *
1011 * p primary
1012 * pp primaryPreferred
1013 * s secondary
1014 * sp secondaryPreferred
1015 * n nearest
1016 *
1017 * ####Example:
1018 *
1019 * new Query().read('primary')
1020 * new Query().read('p') // same as primary
1021 *
1022 * new Query().read('primaryPreferred')
1023 * new Query().read('pp') // same as primaryPreferred
1024 *
1025 * new Query().read('secondary')
1026 * new Query().read('s') // same as secondary
1027 *
1028 * new Query().read('secondaryPreferred')
1029 * new Query().read('sp') // same as secondaryPreferred
1030 *
1031 * new Query().read('nearest')
1032 * new Query().read('n') // same as nearest
1033 *
1034 * // read from secondaries with matching tags
1035 * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])
1036 *
1037 * Read more about how to use read preferrences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
1038 *
1039 * @method read
1040 * @memberOf Query
1041 * @instance
1042 * @param {String} pref one of the listed preference options or aliases
1043 * @param {Array} [tags] optional tags for this query
1044 * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
1045 * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
1046 * @return {Query} this
1047 * @api public
1048 */
1049
1050Query.prototype.read = function read(pref, tags) {
1051 // first cast into a ReadPreference object to support tags
1052 const read = new ReadPreference(pref, tags);
1053 this.options.readPreference = read;
1054 return this;
1055};
1056
1057/**
1058 * Sets the [MongoDB session](https://docs.mongodb.com/manual/reference/server-sessions/)
1059 * associated with this query. Sessions are how you mark a query as part of a
1060 * [transaction](/docs/transactions.html).
1061 *
1062 * Calling `session(null)` removes the session from this query.
1063 *
1064 * ####Example:
1065 *
1066 * const s = await mongoose.startSession();
1067 * await mongoose.model('Person').findOne({ name: 'Axl Rose' }).session(s);
1068 *
1069 * @method session
1070 * @memberOf Query
1071 * @instance
1072 * @param {ClientSession} [session] from `await conn.startSession()`
1073 * @see Connection.prototype.startSession() /docs/api.html#connection_Connection-startSession
1074 * @see mongoose.startSession() /docs/api.html#mongoose_Mongoose-startSession
1075 * @return {Query} this
1076 * @api public
1077 */
1078
1079Query.prototype.session = function session(v) {
1080 if (v == null) {
1081 delete this.options.session;
1082 }
1083 this.options.session = v;
1084 return this;
1085};
1086
1087/**
1088 * Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
1089 * that must acknowledge this write before this write is considered successful.
1090 * This option is only valid for operations that write to the database:
1091 *
1092 * - `deleteOne()`
1093 * - `deleteMany()`
1094 * - `findOneAndDelete()`
1095 * - `findOneAndReplace()`
1096 * - `findOneAndUpdate()`
1097 * - `remove()`
1098 * - `update()`
1099 * - `updateOne()`
1100 * - `updateMany()`
1101 *
1102 * Defaults to the schema's [`writeConcern.w` option](/docs/guide.html#writeConcern)
1103 *
1104 * ####Example:
1105 *
1106 * // The 'majority' option means the `deleteOne()` promise won't resolve
1107 * // until the `deleteOne()` has propagated to the majority of the replica set
1108 * await mongoose.model('Person').
1109 * deleteOne({ name: 'Ned Stark' }).
1110 * w('majority');
1111 *
1112 * @method w
1113 * @memberOf Query
1114 * @instance
1115 * @param {String|number} val 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://docs.mongodb.com/manual/reference/write-concern/#w-option).
1116 * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#w-option
1117 * @return {Query} this
1118 * @api public
1119 */
1120
1121Query.prototype.w = function w(val) {
1122 if (val == null) {
1123 delete this.options.w;
1124 }
1125 this.options.w = val;
1126 return this;
1127};
1128
1129/**
1130 * Requests acknowledgement that this operation has been persisted to MongoDB's
1131 * on-disk journal.
1132 * This option is only valid for operations that write to the database:
1133 *
1134 * - `deleteOne()`
1135 * - `deleteMany()`
1136 * - `findOneAndDelete()`
1137 * - `findOneAndReplace()`
1138 * - `findOneAndUpdate()`
1139 * - `remove()`
1140 * - `update()`
1141 * - `updateOne()`
1142 * - `updateMany()`
1143 *
1144 * Defaults to the schema's [`writeConcern.j` option](/docs/guide.html#writeConcern)
1145 *
1146 * ####Example:
1147 *
1148 * await mongoose.model('Person').deleteOne({ name: 'Ned Stark' }).j(true);
1149 *
1150 * @method j
1151 * @memberOf Query
1152 * @instance
1153 * @param {boolean} val
1154 * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#j-option
1155 * @return {Query} this
1156 * @api public
1157 */
1158
1159Query.prototype.j = function j(val) {
1160 if (val == null) {
1161 delete this.options.j;
1162 }
1163 this.options.j = val;
1164 return this;
1165};
1166
1167/**
1168 * If [`w > 1`](/docs/api.html#query_Query-w), the maximum amount of time to
1169 * wait for this write to propagate through the replica set before this
1170 * operation fails. The default is `0`, which means no timeout.
1171 *
1172 * This option is only valid for operations that write to the database:
1173 *
1174 * - `deleteOne()`
1175 * - `deleteMany()`
1176 * - `findOneAndDelete()`
1177 * - `findOneAndReplace()`
1178 * - `findOneAndUpdate()`
1179 * - `remove()`
1180 * - `update()`
1181 * - `updateOne()`
1182 * - `updateMany()`
1183 *
1184 * Defaults to the schema's [`writeConcern.wtimeout` option](/docs/guide.html#writeConcern)
1185 *
1186 * ####Example:
1187 *
1188 * // The `deleteOne()` promise won't resolve until this `deleteOne()` has
1189 * // propagated to at least `w = 2` members of the replica set. If it takes
1190 * // longer than 1 second, this `deleteOne()` will fail.
1191 * await mongoose.model('Person').
1192 * deleteOne({ name: 'Ned Stark' }).
1193 * w(2).
1194 * wtimeout(1000);
1195 *
1196 * @method wtimeout
1197 * @memberOf Query
1198 * @instance
1199 * @param {number} ms number of milliseconds to wait
1200 * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#wtimeout
1201 * @return {Query} this
1202 * @api public
1203 */
1204
1205Query.prototype.wtimeout = function wtimeout(ms) {
1206 if (ms == null) {
1207 delete this.options.wtimeout;
1208 }
1209 this.options.wtimeout = ms;
1210 return this;
1211};
1212
1213/**
1214 * Sets the readConcern option for the query.
1215 *
1216 * ####Example:
1217 *
1218 * new Query().readConcern('local')
1219 * new Query().readConcern('l') // same as local
1220 *
1221 * new Query().readConcern('available')
1222 * new Query().readConcern('a') // same as available
1223 *
1224 * new Query().readConcern('majority')
1225 * new Query().readConcern('m') // same as majority
1226 *
1227 * new Query().readConcern('linearizable')
1228 * new Query().readConcern('lz') // same as linearizable
1229 *
1230 * new Query().readConcern('snapshot')
1231 * new Query().readConcern('s') // same as snapshot
1232 *
1233 *
1234 * ####Read Concern Level:
1235 *
1236 * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
1237 * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
1238 * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure.
1239 * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results.
1240 * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data.
1241 *
1242 * Aliases
1243 *
1244 * l local
1245 * a available
1246 * m majority
1247 * lz linearizable
1248 * s snapshot
1249 *
1250 * Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/).
1251 *
1252 * @memberOf Query
1253 * @method readConcern
1254 * @param {String} level one of the listed read concern level or their aliases
1255 * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/
1256 * @return {Query} this
1257 * @api public
1258 */
1259
1260/**
1261 * Gets query options.
1262 *
1263 * ####Example:
1264 *
1265 * var query = new Query();
1266 * query.limit(10);
1267 * query.setOptions({ maxTimeMS: 1000 })
1268 * query.getOptions(); // { limit: 10, maxTimeMS: 1000 }
1269 *
1270 * @return {Object} the options
1271 * @api public
1272 */
1273
1274Query.prototype.getOptions = function() {
1275 return this.options;
1276};
1277
1278/**
1279 * Sets query options. Some options only make sense for certain operations.
1280 *
1281 * ####Options:
1282 *
1283 * The following options are only for `find()`:
1284 *
1285 * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors)
1286 * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D)
1287 * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D)
1288 * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D)
1289 * - [maxscan](https://docs.mongodb.org/v3.2/reference/operator/meta/maxScan/#metaOp._S_maxScan)
1290 * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D)
1291 * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment)
1292 * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D)
1293 * - [readPreference](http://docs.mongodb.org/manual/applications/replication/#read-preference)
1294 * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint)
1295 *
1296 * The following options are only for write operations: `update()`, `updateOne()`, `updateMany()`, `replaceOne()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
1297 *
1298 * - [upsert](https://docs.mongodb.com/manual/reference/method/db.collection.update/)
1299 * - [writeConcern](https://docs.mongodb.com/manual/reference/method/db.collection.update/)
1300 * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options.
1301 * - omitUndefined: delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
1302 *
1303 * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
1304 *
1305 * - [lean](./api.html#query_Query-lean)
1306 *
1307 * The following options are only for all operations **except** `update()`, `updateOne()`, `updateMany()`, `remove()`, `deleteOne()`, and `deleteMany()`:
1308 *
1309 * - [maxTimeMS](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/)
1310 *
1311 * The following options are for all operations:
1312 *
1313 * - [collation](https://docs.mongodb.com/manual/reference/collation/)
1314 * - [session](https://docs.mongodb.com/manual/reference/server-sessions/)
1315 *
1316 * @param {Object} options
1317 * @return {Query} this
1318 * @api public
1319 */
1320
1321Query.prototype.setOptions = function(options, overwrite) {
1322 // overwrite is only for internal use
1323 if (overwrite) {
1324 // ensure that _mongooseOptions & options are two different objects
1325 this._mongooseOptions = (options && utils.clone(options)) || {};
1326 this.options = options || {};
1327
1328 if ('populate' in options) {
1329 this.populate(this._mongooseOptions);
1330 }
1331 return this;
1332 }
1333
1334 if (options == null) {
1335 return this;
1336 }
1337 if (typeof options !== 'object') {
1338 throw new Error('Options must be an object, got "' + options + '"');
1339 }
1340
1341 if (Array.isArray(options.populate)) {
1342 const populate = options.populate;
1343 delete options.populate;
1344 const _numPopulate = populate.length;
1345 for (let i = 0; i < _numPopulate; ++i) {
1346 this.populate(populate[i]);
1347 }
1348 }
1349
1350 if ('useFindAndModify' in options) {
1351 this._mongooseOptions.useFindAndModify = options.useFindAndModify;
1352 delete options.useFindAndModify;
1353 }
1354 if ('omitUndefined' in options) {
1355 this._mongooseOptions.omitUndefined = options.omitUndefined;
1356 delete options.omitUndefined;
1357 }
1358
1359 return Query.base.setOptions.call(this, options);
1360};
1361
1362/**
1363 * Sets the [`explain` option](https://docs.mongodb.com/manual/reference/method/cursor.explain/),
1364 * which makes this query return detailed execution stats instead of the actual
1365 * query result. This method is useful for determining what index your queries
1366 * use.
1367 *
1368 * Calling `query.explain(v)` is equivalent to `query.setOption({ explain: v })`
1369 *
1370 * ####Example:
1371 *
1372 * const query = new Query();
1373 * const res = await query.find({ a: 1 }).explain('queryPlanner');
1374 * console.log(res);
1375 *
1376 * @param {String} [verbose] The verbosity mode. Either 'queryPlanner', 'executionStats', or 'allPlansExecution'. The default is 'queryPlanner'
1377 * @return {Query} this
1378 * @api public
1379 */
1380
1381Query.prototype.explain = function(verbose) {
1382 if (arguments.length === 0) {
1383 this.options.explain = true;
1384 return this;
1385 }
1386 this.options.explain = verbose;
1387 return this;
1388};
1389
1390/**
1391 * Sets the [maxTimeMS](https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS/)
1392 * option. This will tell the MongoDB server to abort if the query or write op
1393 * has been running for more than `ms` milliseconds.
1394 *
1395 * Calling `query.maxTimeMS(v)` is equivalent to `query.setOption({ maxTimeMS: v })`
1396 *
1397 * ####Example:
1398 *
1399 * const query = new Query();
1400 * // Throws an error 'operation exceeded time limit' as long as there's
1401 * // >= 1 doc in the queried collection
1402 * const res = await query.find({ $where: 'sleep(1000) || true' }).maxTimeMS(100);
1403 *
1404 * @param {Number} [ms] The number of milliseconds
1405 * @return {Query} this
1406 * @api public
1407 */
1408
1409Query.prototype.maxTimeMS = function(ms) {
1410 this.options.maxTimeMS = ms;
1411 return this;
1412};
1413
1414/**
1415 * Returns the current query filter (also known as conditions) as a POJO.
1416 *
1417 * ####Example:
1418 *
1419 * const query = new Query();
1420 * query.find({ a: 1 }).where('b').gt(2);
1421 * query.getFilter(); // { a: 1, b: { $gt: 2 } }
1422 *
1423 * @return {Object} current query filter
1424 * @api public
1425 */
1426
1427Query.prototype.getFilter = function() {
1428 return this._conditions;
1429};
1430
1431/**
1432 * Returns the current query filter. Equivalent to `getFilter()`.
1433 *
1434 * You should use `getFilter()` instead of `getQuery()` where possible. `getQuery()`
1435 * will likely be deprecated in a future release.
1436 *
1437 * ####Example:
1438 *
1439 * var query = new Query();
1440 * query.find({ a: 1 }).where('b').gt(2);
1441 * query.getQuery(); // { a: 1, b: { $gt: 2 } }
1442 *
1443 * @return {Object} current query filter
1444 * @api public
1445 */
1446
1447Query.prototype.getQuery = function() {
1448 return this._conditions;
1449};
1450
1451/**
1452 * Sets the query conditions to the provided JSON object.
1453 *
1454 * ####Example:
1455 *
1456 * var query = new Query();
1457 * query.find({ a: 1 })
1458 * query.setQuery({ a: 2 });
1459 * query.getQuery(); // { a: 2 }
1460 *
1461 * @param {Object} new query conditions
1462 * @return {undefined}
1463 * @api public
1464 */
1465
1466Query.prototype.setQuery = function(val) {
1467 this._conditions = val;
1468};
1469
1470/**
1471 * Returns the current update operations as a JSON object.
1472 *
1473 * ####Example:
1474 *
1475 * var query = new Query();
1476 * query.update({}, { $set: { a: 5 } });
1477 * query.getUpdate(); // { $set: { a: 5 } }
1478 *
1479 * @return {Object} current update operations
1480 * @api public
1481 */
1482
1483Query.prototype.getUpdate = function() {
1484 return this._update;
1485};
1486
1487/**
1488 * Sets the current update operation to new value.
1489 *
1490 * ####Example:
1491 *
1492 * var query = new Query();
1493 * query.update({}, { $set: { a: 5 } });
1494 * query.setUpdate({ $set: { b: 6 } });
1495 * query.getUpdate(); // { $set: { b: 6 } }
1496 *
1497 * @param {Object} new update operation
1498 * @return {undefined}
1499 * @api public
1500 */
1501
1502Query.prototype.setUpdate = function(val) {
1503 this._update = val;
1504};
1505
1506/**
1507 * Returns fields selection for this query.
1508 *
1509 * @method _fieldsForExec
1510 * @return {Object}
1511 * @api private
1512 * @receiver Query
1513 */
1514
1515Query.prototype._fieldsForExec = function() {
1516 return utils.clone(this._fields);
1517};
1518
1519
1520/**
1521 * Return an update document with corrected `$set` operations.
1522 *
1523 * @method _updateForExec
1524 * @api private
1525 * @receiver Query
1526 */
1527
1528Query.prototype._updateForExec = function() {
1529 const update = utils.clone(this._update, {
1530 transform: false,
1531 depopulate: true
1532 });
1533 const ops = Object.keys(update);
1534 let i = ops.length;
1535 const ret = {};
1536
1537 while (i--) {
1538 const op = ops[i];
1539
1540 if (this.options.overwrite) {
1541 ret[op] = update[op];
1542 continue;
1543 }
1544
1545 if ('$' !== op[0]) {
1546 // fix up $set sugar
1547 if (!ret.$set) {
1548 if (update.$set) {
1549 ret.$set = update.$set;
1550 } else {
1551 ret.$set = {};
1552 }
1553 }
1554 ret.$set[op] = update[op];
1555 ops.splice(i, 1);
1556 if (!~ops.indexOf('$set')) ops.push('$set');
1557 } else if ('$set' === op) {
1558 if (!ret.$set) {
1559 ret[op] = update[op];
1560 }
1561 } else {
1562 ret[op] = update[op];
1563 }
1564 }
1565
1566 return ret;
1567};
1568
1569/**
1570 * Makes sure _path is set.
1571 *
1572 * @method _ensurePath
1573 * @param {String} method
1574 * @api private
1575 * @receiver Query
1576 */
1577
1578/**
1579 * Determines if `conds` can be merged using `mquery().merge()`
1580 *
1581 * @method canMerge
1582 * @memberOf Query
1583 * @instance
1584 * @param {Object} conds
1585 * @return {Boolean}
1586 * @api private
1587 */
1588
1589/**
1590 * Returns default options for this query.
1591 *
1592 * @param {Model} model
1593 * @api private
1594 */
1595
1596Query.prototype._optionsForExec = function(model) {
1597 const options = utils.clone(this.options);
1598
1599 delete options.populate;
1600 model = model || this.model;
1601
1602 if (!model) {
1603 return options;
1604 }
1605
1606 const safe = get(model, 'schema.options.safe', null);
1607 if (!('safe' in options) && safe != null) {
1608 setSafe(options, safe);
1609 }
1610
1611 // Apply schema-level `writeConcern` option
1612 applyWriteConcern(model.schema, options);
1613
1614 const readPreference = get(model, 'schema.options.read');
1615 if (!('readPreference' in options) && readPreference) {
1616 options.readPreference = readPreference;
1617 }
1618
1619 if (options.upsert !== void 0) {
1620 options.upsert = !!options.upsert;
1621 }
1622
1623 return options;
1624};
1625
1626/*!
1627 * ignore
1628 */
1629
1630const safeDeprecationWarning = 'Mongoose: the `safe` option is deprecated. ' +
1631 'Use write concerns instead: http://bit.ly/mongoose-w';
1632
1633const setSafe = util.deprecate(function setSafe(options, safe) {
1634 options.safe = safe;
1635}, safeDeprecationWarning);
1636
1637/**
1638 * Sets the lean option.
1639 *
1640 * Documents returned from queries with the `lean` option enabled are plain
1641 * javascript objects, not [Mongoose Documents](#document-js). They have no
1642 * `save` method, getters/setters, virtuals, or other Mongoose features.
1643 *
1644 * ####Example:
1645 *
1646 * new Query().lean() // true
1647 * new Query().lean(true)
1648 * new Query().lean(false)
1649 *
1650 * const docs = await Model.find().lean();
1651 * docs[0] instanceof mongoose.Document; // false
1652 *
1653 * [Lean is great for high-performance, read-only cases](/docs/tutorials/lean.html),
1654 * especially when combined
1655 * with [cursors](/docs/queries.html#streaming).
1656 *
1657 * @param {Boolean|Object} bool defaults to true
1658 * @return {Query} this
1659 * @api public
1660 */
1661
1662Query.prototype.lean = function(v) {
1663 this._mongooseOptions.lean = arguments.length ? v : true;
1664 return this;
1665};
1666
1667/**
1668 * Adds a `$set` to this query's update without changing the operation.
1669 * This is useful for query middleware so you can add an update regardless
1670 * of whether you use `updateOne()`, `updateMany()`, `findOneAndUpdate()`, etc.
1671 *
1672 * ####Example:
1673 *
1674 * // Updates `{ $set: { updatedAt: new Date() } }`
1675 * new Query().updateOne({}, {}).set('updatedAt', new Date());
1676 * new Query().updateMany({}, {}).set({ updatedAt: new Date() });
1677 *
1678 * @param {String|Object} path path or object of key/value pairs to set
1679 * @param {Any} [val] the value to set
1680 * @return {Query} this
1681 * @api public
1682 */
1683
1684Query.prototype.set = function(path, val) {
1685 if (typeof path === 'object') {
1686 const keys = Object.keys(path);
1687 for (const key of keys) {
1688 this.set(key, path[key]);
1689 }
1690 return this;
1691 }
1692
1693 this._update = this._update || {};
1694 this._update.$set = this._update.$set || {};
1695 this._update.$set[path] = val;
1696 return this;
1697};
1698
1699/**
1700 * Gets/sets the error flag on this query. If this flag is not null or
1701 * undefined, the `exec()` promise will reject without executing.
1702 *
1703 * ####Example:
1704 *
1705 * Query().error(); // Get current error value
1706 * Query().error(null); // Unset the current error
1707 * Query().error(new Error('test')); // `exec()` will resolve with test
1708 * Schema.pre('find', function() {
1709 * if (!this.getQuery().userId) {
1710 * this.error(new Error('Not allowed to query without setting userId'));
1711 * }
1712 * });
1713 *
1714 * Note that query casting runs **after** hooks, so cast errors will override
1715 * custom errors.
1716 *
1717 * ####Example:
1718 * var TestSchema = new Schema({ num: Number });
1719 * var TestModel = db.model('Test', TestSchema);
1720 * TestModel.find({ num: 'not a number' }).error(new Error('woops')).exec(function(error) {
1721 * // `error` will be a cast error because `num` failed to cast
1722 * });
1723 *
1724 * @param {Error|null} err if set, `exec()` will fail fast before sending the query to MongoDB
1725 * @return {Query} this
1726 * @api public
1727 */
1728
1729Query.prototype.error = function error(err) {
1730 if (arguments.length === 0) {
1731 return this._error;
1732 }
1733
1734 this._error = err;
1735 return this;
1736};
1737
1738/*!
1739 * ignore
1740 */
1741
1742Query.prototype._unsetCastError = function _unsetCastError() {
1743 if (this._error != null && !(this._error instanceof CastError)) {
1744 return;
1745 }
1746 return this.error(null);
1747};
1748
1749/**
1750 * Getter/setter around the current mongoose-specific options for this query
1751 * Below are the current Mongoose-specific options.
1752 *
1753 * - `populate`: an array representing what paths will be populated. Should have one entry for each call to [`Query.prototype.populate()`](/docs/api.html#query_Query-populate)
1754 * - `lean`: if truthy, Mongoose will not [hydrate](/docs/api.html#model_Model.hydrate) any documents that are returned from this query. See [`Query.prototype.lean()`](/docs/api.html#query_Query-lean) for more information.
1755 * - `strict`: controls how Mongoose handles keys that aren't in the schema for updates. This option is `true` by default, which means Mongoose will silently strip any paths in the update that aren't in the schema. See the [`strict` mode docs](/docs/guide.html#strict) for more information.
1756 * - `strictQuery`: controls how Mongoose handles keys that aren't in the schema for the query `filter`. This option is `false` by default for backwards compatibility, which means Mongoose will allow `Model.find({ foo: 'bar' })` even if `foo` is not in the schema. See the [`strictQuery` docs](/docs/guide.html#strictQuery) for more information.
1757 * - `useFindAndModify`: used to work around the [`findAndModify()` deprecation warning](/docs/deprecations.html#-findandmodify-)
1758 * - `omitUndefined`: delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
1759 * - `nearSphere`: use `$nearSphere` instead of `near()`. See the [`Query.prototype.nearSphere()` docs](/docs/api.html#query_Query-nearSphere)
1760 *
1761 * Mongoose maintains a separate object for internal options because
1762 * Mongoose sends `Query.prototype.options` to the MongoDB server, and the
1763 * above options are not relevant for the MongoDB server.
1764 *
1765 * @param {Object} options if specified, overwrites the current options
1766 * @return {Object} the options
1767 * @api public
1768 */
1769
1770Query.prototype.mongooseOptions = function(v) {
1771 if (arguments.length > 0) {
1772 this._mongooseOptions = v;
1773 }
1774 return this._mongooseOptions;
1775};
1776
1777/*!
1778 * ignore
1779 */
1780
1781Query.prototype._castConditions = function() {
1782 try {
1783 this.cast(this.model);
1784 this._unsetCastError();
1785 } catch (err) {
1786 this.error(err);
1787 }
1788};
1789
1790/*!
1791 * ignore
1792 */
1793
1794function _castArrayFilters(query) {
1795 try {
1796 castArrayFilters(query);
1797 } catch (err) {
1798 query.error(err);
1799 }
1800}
1801
1802/**
1803 * Thunk around find()
1804 *
1805 * @param {Function} [callback]
1806 * @return {Query} this
1807 * @api private
1808 */
1809Query.prototype._find = wrapThunk(function(callback) {
1810 this._castConditions();
1811
1812 if (this.error() != null) {
1813 callback(this.error());
1814 return null;
1815 }
1816
1817 callback = _wrapThunkCallback(this, callback);
1818
1819 this._applyPaths();
1820 this._fields = this._castFields(this._fields);
1821
1822 const fields = this._fieldsForExec();
1823 const mongooseOptions = this._mongooseOptions;
1824 const _this = this;
1825 const userProvidedFields = _this._userProvidedFields || {};
1826
1827 applyGlobalMaxTimeMS(this.options, this.model);
1828
1829 // Separate options to pass down to `completeMany()` in case we need to
1830 // set a session on the document
1831 const completeManyOptions = Object.assign({}, {
1832 session: get(this, 'options.session', null)
1833 });
1834
1835 const cb = (err, docs) => {
1836 if (err) {
1837 return callback(err);
1838 }
1839
1840 if (docs.length === 0) {
1841 return callback(null, docs);
1842 }
1843 if (this.options.explain) {
1844 return callback(null, docs);
1845 }
1846
1847 if (!mongooseOptions.populate) {
1848 return mongooseOptions.lean ?
1849 callback(null, docs) :
1850 completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
1851 }
1852
1853 const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
1854 completeManyOptions.populated = pop;
1855 _this.model.populate(docs, pop, function(err, docs) {
1856 if (err) return callback(err);
1857 return mongooseOptions.lean ?
1858 callback(null, docs) :
1859 completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
1860 });
1861 };
1862
1863 const options = this._optionsForExec();
1864 options.projection = this._fieldsForExec();
1865 const filter = this._conditions;
1866
1867 this._collection.find(filter, options, cb);
1868 return null;
1869});
1870
1871/**
1872 * Find all documents that match `selector`. The result will be an array of documents.
1873 *
1874 * If there are too many documents in the result to fit in memory, use
1875 * [`Query.prototype.cursor()`](api.html#query_Query-cursor)
1876 *
1877 * ####Example
1878 *
1879 * // Using async/await
1880 * const arr = await Movie.find({ year: { $gte: 1980, $lte: 1989 } });
1881 *
1882 * // Using callbacks
1883 * Movie.find({ year: { $gte: 1980, $lte: 1989 } }, function(err, arr) {});
1884 *
1885 * @param {Object} [filter] mongodb selector. If not specified, returns all documents.
1886 * @param {Function} [callback]
1887 * @return {Query} this
1888 * @api public
1889 */
1890
1891Query.prototype.find = function(conditions, callback) {
1892 if (typeof conditions === 'function') {
1893 callback = conditions;
1894 conditions = {};
1895 }
1896
1897 conditions = utils.toObject(conditions);
1898
1899 if (mquery.canMerge(conditions)) {
1900 this.merge(conditions);
1901
1902 prepareDiscriminatorCriteria(this);
1903 } else if (conditions != null) {
1904 this.error(new ObjectParameterError(conditions, 'filter', 'find'));
1905 }
1906
1907 // if we don't have a callback, then just return the query object
1908 if (!callback) {
1909 return Query.base.find.call(this);
1910 }
1911
1912 this._find(callback);
1913
1914 return this;
1915};
1916
1917/**
1918 * Merges another Query or conditions object into this one.
1919 *
1920 * When a Query is passed, conditions, field selection and options are merged.
1921 *
1922 * @param {Query|Object} source
1923 * @return {Query} this
1924 */
1925
1926Query.prototype.merge = function(source) {
1927 if (!source) {
1928 return this;
1929 }
1930
1931 const opts = { overwrite: true };
1932
1933 if (source instanceof Query) {
1934 // if source has a feature, apply it to ourselves
1935
1936 if (source._conditions) {
1937 utils.merge(this._conditions, source._conditions, opts);
1938 }
1939
1940 if (source._fields) {
1941 this._fields || (this._fields = {});
1942 utils.merge(this._fields, source._fields, opts);
1943 }
1944
1945 if (source.options) {
1946 this.options || (this.options = {});
1947 utils.merge(this.options, source.options, opts);
1948 }
1949
1950 if (source._update) {
1951 this._update || (this._update = {});
1952 utils.mergeClone(this._update, source._update);
1953 }
1954
1955 if (source._distinct) {
1956 this._distinct = source._distinct;
1957 }
1958
1959 utils.merge(this._mongooseOptions, source._mongooseOptions);
1960
1961 return this;
1962 }
1963
1964 // plain object
1965 utils.merge(this._conditions, source, opts);
1966
1967 return this;
1968};
1969
1970/**
1971 * Adds a collation to this op (MongoDB 3.4 and up)
1972 *
1973 * @param {Object} value
1974 * @return {Query} this
1975 * @see MongoDB docs https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation
1976 * @api public
1977 */
1978
1979Query.prototype.collation = function(value) {
1980 if (this.options == null) {
1981 this.options = {};
1982 }
1983 this.options.collation = value;
1984 return this;
1985};
1986
1987/**
1988 * Hydrate a single doc from `findOne()`, `findOneAndUpdate()`, etc.
1989 *
1990 * @api private
1991 */
1992
1993Query.prototype._completeOne = function(doc, res, callback) {
1994 if (!doc && !this.options.rawResult) {
1995 return callback(null, null);
1996 }
1997
1998 const model = this.model;
1999 const projection = utils.clone(this._fields);
2000 const userProvidedFields = this._userProvidedFields || {};
2001 // `populate`, `lean`
2002 const mongooseOptions = this._mongooseOptions;
2003 // `rawResult`
2004 const options = this.options;
2005
2006 if (options.explain) {
2007 return callback(null, doc);
2008 }
2009
2010 if (!mongooseOptions.populate) {
2011 return mongooseOptions.lean ?
2012 _completeOneLean(doc, res, options, callback) :
2013 completeOne(model, doc, res, options, projection, userProvidedFields,
2014 null, callback);
2015 }
2016
2017 const pop = helpers.preparePopulationOptionsMQ(this, this._mongooseOptions);
2018 model.populate(doc, pop, (err, doc) => {
2019 if (err) {
2020 return callback(err);
2021 }
2022 return mongooseOptions.lean ?
2023 _completeOneLean(doc, res, options, callback) :
2024 completeOne(model, doc, res, options, projection, userProvidedFields,
2025 pop, callback);
2026 });
2027};
2028
2029/**
2030 * Thunk around findOne()
2031 *
2032 * @param {Function} [callback]
2033 * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
2034 * @api private
2035 */
2036
2037Query.prototype._findOne = wrapThunk(function(callback) {
2038 this._castConditions();
2039
2040 if (this.error()) {
2041 callback(this.error());
2042 return null;
2043 }
2044
2045 this._applyPaths();
2046 this._fields = this._castFields(this._fields);
2047
2048 applyGlobalMaxTimeMS(this.options, this.model);
2049
2050 // don't pass in the conditions because we already merged them in
2051 Query.base.findOne.call(this, {}, (err, doc) => {
2052 if (err) {
2053 callback(err);
2054 return null;
2055 }
2056
2057 this._completeOne(doc, null, _wrapThunkCallback(this, callback));
2058 });
2059});
2060
2061/**
2062 * Declares the query a findOne operation. When executed, the first found document is passed to the callback.
2063 *
2064 * Passing a `callback` executes the query. The result of the query is a single document.
2065 *
2066 * * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
2067 * mongoose will send an empty `findOne` command to MongoDB, which will return
2068 * an arbitrary document. If you're querying by `_id`, use `Model.findById()`
2069 * instead.
2070 *
2071 * This function triggers the following middleware.
2072 *
2073 * - `findOne()`
2074 *
2075 * ####Example
2076 *
2077 * var query = Kitten.where({ color: 'white' });
2078 * query.findOne(function (err, kitten) {
2079 * if (err) return handleError(err);
2080 * if (kitten) {
2081 * // doc may be null if no document matched
2082 * }
2083 * });
2084 *
2085 * @param {Object} [filter] mongodb selector
2086 * @param {Object} [projection] optional fields to return
2087 * @param {Object} [options] see [`setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
2088 * @param {Function} [callback] optional params are (error, document)
2089 * @return {Query} this
2090 * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
2091 * @see Query.select #query_Query-select
2092 * @api public
2093 */
2094
2095Query.prototype.findOne = function(conditions, projection, options, callback) {
2096 if (typeof conditions === 'function') {
2097 callback = conditions;
2098 conditions = null;
2099 projection = null;
2100 options = null;
2101 } else if (typeof projection === 'function') {
2102 callback = projection;
2103 options = null;
2104 projection = null;
2105 } else if (typeof options === 'function') {
2106 callback = options;
2107 options = null;
2108 }
2109
2110 // make sure we don't send in the whole Document to merge()
2111 conditions = utils.toObject(conditions);
2112
2113 this.op = 'findOne';
2114
2115 if (options) {
2116 this.setOptions(options);
2117 }
2118
2119 if (projection) {
2120 this.select(projection);
2121 }
2122
2123 if (mquery.canMerge(conditions)) {
2124 this.merge(conditions);
2125
2126 prepareDiscriminatorCriteria(this);
2127 } else if (conditions != null) {
2128 this.error(new ObjectParameterError(conditions, 'filter', 'findOne'));
2129 }
2130
2131 if (!callback) {
2132 // already merged in the conditions, don't need to send them in.
2133 return Query.base.findOne.call(this);
2134 }
2135
2136 this._findOne(callback);
2137
2138 return this;
2139};
2140
2141/**
2142 * Thunk around count()
2143 *
2144 * @param {Function} [callback]
2145 * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
2146 * @api private
2147 */
2148
2149Query.prototype._count = wrapThunk(function(callback) {
2150 try {
2151 this.cast(this.model);
2152 } catch (err) {
2153 this.error(err);
2154 }
2155
2156 if (this.error()) {
2157 return callback(this.error());
2158 }
2159
2160 const conds = this._conditions;
2161 const options = this._optionsForExec();
2162
2163 this._collection.count(conds, options, utils.tick(callback));
2164});
2165
2166/**
2167 * Thunk around countDocuments()
2168 *
2169 * @param {Function} [callback]
2170 * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments
2171 * @api private
2172 */
2173
2174Query.prototype._countDocuments = wrapThunk(function(callback) {
2175 try {
2176 this.cast(this.model);
2177 } catch (err) {
2178 this.error(err);
2179 }
2180
2181 if (this.error()) {
2182 return callback(this.error());
2183 }
2184
2185 const conds = this._conditions;
2186 const options = this._optionsForExec();
2187
2188 this._collection.collection.countDocuments(conds, options, utils.tick(callback));
2189});
2190
2191/**
2192 * Thunk around estimatedDocumentCount()
2193 *
2194 * @param {Function} [callback]
2195 * @see estimatedDocumentCount http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount
2196 * @api private
2197 */
2198
2199Query.prototype._estimatedDocumentCount = wrapThunk(function(callback) {
2200 if (this.error()) {
2201 return callback(this.error());
2202 }
2203
2204 const options = this._optionsForExec();
2205
2206 this._collection.collection.estimatedDocumentCount(options, utils.tick(callback));
2207});
2208
2209/**
2210 * Specifies this query as a `count` query.
2211 *
2212 * This method is deprecated. If you want to count the number of documents in
2213 * a collection, e.g. `count({})`, use the [`estimatedDocumentCount()` function](/docs/api.html#query_Query-estimatedDocumentCount)
2214 * instead. Otherwise, use the [`countDocuments()`](/docs/api.html#query_Query-countDocuments) function instead.
2215 *
2216 * Passing a `callback` executes the query.
2217 *
2218 * This function triggers the following middleware.
2219 *
2220 * - `count()`
2221 *
2222 * ####Example:
2223 *
2224 * var countQuery = model.where({ 'color': 'black' }).count();
2225 *
2226 * query.count({ color: 'black' }).count(callback)
2227 *
2228 * query.count({ color: 'black' }, callback)
2229 *
2230 * query.where('color', 'black').count(function (err, count) {
2231 * if (err) return handleError(err);
2232 * console.log('there are %d kittens', count);
2233 * })
2234 *
2235 * @deprecated
2236 * @param {Object} [filter] count documents that match this object
2237 * @param {Function} [callback] optional params are (error, count)
2238 * @return {Query} this
2239 * @see count http://docs.mongodb.org/manual/reference/method/db.collection.count/
2240 * @api public
2241 */
2242
2243Query.prototype.count = function(filter, callback) {
2244 if (typeof filter === 'function') {
2245 callback = filter;
2246 filter = undefined;
2247 }
2248
2249 filter = utils.toObject(filter);
2250
2251 if (mquery.canMerge(filter)) {
2252 this.merge(filter);
2253 }
2254
2255 this.op = 'count';
2256 if (!callback) {
2257 return this;
2258 }
2259
2260 this._count(callback);
2261
2262 return this;
2263};
2264
2265/**
2266 * Specifies this query as a `estimatedDocumentCount()` query. Faster than
2267 * using `countDocuments()` for large collections because
2268 * `estimatedDocumentCount()` uses collection metadata rather than scanning
2269 * the entire collection.
2270 *
2271 * `estimatedDocumentCount()` does **not** accept a filter. `Model.find({ foo: bar }).estimatedDocumentCount()`
2272 * is equivalent to `Model.find().estimatedDocumentCount()`
2273 *
2274 * This function triggers the following middleware.
2275 *
2276 * - `estimatedDocumentCount()`
2277 *
2278 * ####Example:
2279 *
2280 * await Model.find().estimatedDocumentCount();
2281 *
2282 * @param {Object} [options] passed transparently to the [MongoDB driver](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount)
2283 * @param {Function} [callback] optional params are (error, count)
2284 * @return {Query} this
2285 * @see estimatedDocumentCount http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#estimatedDocumentCount
2286 * @api public
2287 */
2288
2289Query.prototype.estimatedDocumentCount = function(options, callback) {
2290 if (typeof options === 'function') {
2291 callback = options;
2292 options = undefined;
2293 }
2294
2295 if (typeof options === 'object' && options != null) {
2296 this.setOptions(options);
2297 }
2298
2299 this.op = 'estimatedDocumentCount';
2300 if (!callback) {
2301 return this;
2302 }
2303
2304 this._estimatedDocumentCount(callback);
2305
2306 return this;
2307};
2308
2309/**
2310 * Specifies this query as a `countDocuments()` query. Behaves like `count()`,
2311 * except it always does a full collection scan when passed an empty filter `{}`.
2312 *
2313 * There are also minor differences in how `countDocuments()` handles
2314 * [`$where` and a couple geospatial operators](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
2315 * versus `count()`.
2316 *
2317 * Passing a `callback` executes the query.
2318 *
2319 * This function triggers the following middleware.
2320 *
2321 * - `countDocuments()`
2322 *
2323 * ####Example:
2324 *
2325 * const countQuery = model.where({ 'color': 'black' }).countDocuments();
2326 *
2327 * query.countDocuments({ color: 'black' }).count(callback);
2328 *
2329 * query.countDocuments({ color: 'black' }, callback);
2330 *
2331 * query.where('color', 'black').countDocuments(function(err, count) {
2332 * if (err) return handleError(err);
2333 * console.log('there are %d kittens', count);
2334 * });
2335 *
2336 * The `countDocuments()` function is similar to `count()`, but there are a
2337 * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
2338 * Below are the operators that `count()` supports but `countDocuments()` does not,
2339 * and the suggested replacement:
2340 *
2341 * - `$where`: [`$expr`](https://docs.mongodb.com/manual/reference/operator/query/expr/)
2342 * - `$near`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$center`](https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center)
2343 * - `$nearSphere`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
2344 *
2345 * @param {Object} [filter] mongodb selector
2346 * @param {Function} [callback] optional params are (error, count)
2347 * @return {Query} this
2348 * @see countDocuments http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments
2349 * @api public
2350 */
2351
2352Query.prototype.countDocuments = function(conditions, callback) {
2353 if (typeof conditions === 'function') {
2354 callback = conditions;
2355 conditions = undefined;
2356 }
2357
2358 conditions = utils.toObject(conditions);
2359
2360 if (mquery.canMerge(conditions)) {
2361 this.merge(conditions);
2362 }
2363
2364 this.op = 'countDocuments';
2365 if (!callback) {
2366 return this;
2367 }
2368
2369 this._countDocuments(callback);
2370
2371 return this;
2372};
2373
2374/**
2375 * Thunk around findOne()
2376 *
2377 * @param {Function} [callback]
2378 * @see findOne http://docs.mongodb.org/manual/reference/method/db.collection.findOne/
2379 * @api private
2380 */
2381
2382Query.prototype.__distinct = wrapThunk(function __distinct(callback) {
2383 this._castConditions();
2384
2385 if (this.error()) {
2386 callback(this.error());
2387 return null;
2388 }
2389
2390 // don't pass in the conditions because we already merged them in
2391 this._collection.collection.
2392 distinct(this._distinct, this._conditions, callback);
2393});
2394
2395/**
2396 * Declares or executes a distinct() operation.
2397 *
2398 * Passing a `callback` executes the query.
2399 *
2400 * This function does not trigger any middleware.
2401 *
2402 * ####Example
2403 *
2404 * distinct(field, conditions, callback)
2405 * distinct(field, conditions)
2406 * distinct(field, callback)
2407 * distinct(field)
2408 * distinct(callback)
2409 * distinct()
2410 *
2411 * @param {String} [field]
2412 * @param {Object|Query} [filter]
2413 * @param {Function} [callback] optional params are (error, arr)
2414 * @return {Query} this
2415 * @see distinct http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
2416 * @api public
2417 */
2418
2419Query.prototype.distinct = function(field, conditions, callback) {
2420 if (!callback) {
2421 if (typeof conditions === 'function') {
2422 callback = conditions;
2423 conditions = undefined;
2424 } else if (typeof field === 'function') {
2425 callback = field;
2426 field = undefined;
2427 conditions = undefined;
2428 }
2429 }
2430
2431 conditions = utils.toObject(conditions);
2432
2433 if (mquery.canMerge(conditions)) {
2434 this.merge(conditions);
2435
2436 prepareDiscriminatorCriteria(this);
2437 } else if (conditions != null) {
2438 this.error(new ObjectParameterError(conditions, 'filter', 'distinct'));
2439 }
2440
2441 if (field != null) {
2442 this._distinct = field;
2443 }
2444 this.op = 'distinct';
2445
2446 if (callback != null) {
2447 this.__distinct(callback);
2448 }
2449
2450 return this;
2451};
2452
2453/**
2454 * Sets the sort order
2455 *
2456 * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
2457 *
2458 * If a string is passed, it must be a space delimited list of path names. The
2459 * sort order of each path is ascending unless the path name is prefixed with `-`
2460 * which will be treated as descending.
2461 *
2462 * ####Example
2463 *
2464 * // sort by "field" ascending and "test" descending
2465 * query.sort({ field: 'asc', test: -1 });
2466 *
2467 * // equivalent
2468 * query.sort('field -test');
2469 *
2470 * ####Note
2471 *
2472 * Cannot be used with `distinct()`
2473 *
2474 * @param {Object|String} arg
2475 * @return {Query} this
2476 * @see cursor.sort http://docs.mongodb.org/manual/reference/method/cursor.sort/
2477 * @api public
2478 */
2479
2480Query.prototype.sort = function(arg) {
2481 if (arguments.length > 1) {
2482 throw new Error('sort() only takes 1 Argument');
2483 }
2484
2485 return Query.base.sort.call(this, arg);
2486};
2487
2488/**
2489 * Declare and/or execute this query as a remove() operation. `remove()` is
2490 * deprecated, you should use [`deleteOne()`](#query_Query-deleteOne)
2491 * or [`deleteMany()`](#query_Query-deleteMany) instead.
2492 *
2493 * This function does not trigger any middleware
2494 *
2495 * ####Example
2496 *
2497 * Character.remove({ name: /Stark/ }, callback);
2498 *
2499 * This function calls the MongoDB driver's [`Collection#remove()` function](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#remove).
2500 * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
2501 * object that contains 3 properties:
2502 *
2503 * - `ok`: `1` if no errors occurred
2504 * - `deletedCount`: the number of documents deleted
2505 * - `n`: the number of documents deleted. Equal to `deletedCount`.
2506 *
2507 * ####Example
2508 *
2509 * const res = await Character.remove({ name: /Stark/ });
2510 * // Number of docs deleted
2511 * res.deletedCount;
2512 *
2513 * ####Note
2514 *
2515 * Calling `remove()` creates a [Mongoose query](./queries.html), and a query
2516 * does not execute until you either pass a callback, call [`Query#then()`](#query_Query-then),
2517 * or call [`Query#exec()`](#query_Query-exec).
2518 *
2519 * // not executed
2520 * const query = Character.remove({ name: /Stark/ });
2521 *
2522 * // executed
2523 * Character.remove({ name: /Stark/ }, callback);
2524 * Character.remove({ name: /Stark/ }).remove(callback);
2525 *
2526 * // executed without a callback
2527 * Character.exec();
2528 *
2529 * @param {Object|Query} [filter] mongodb selector
2530 * @param {Function} [callback] optional params are (error, mongooseDeleteResult)
2531 * @return {Query} this
2532 * @deprecated
2533 * @see deleteWriteOpResult http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~deleteWriteOpResult
2534 * @see MongoDB driver remove http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#remove
2535 * @api public
2536 */
2537
2538Query.prototype.remove = function(filter, callback) {
2539 if (typeof filter === 'function') {
2540 callback = filter;
2541 filter = null;
2542 }
2543
2544 filter = utils.toObject(filter);
2545
2546 if (mquery.canMerge(filter)) {
2547 this.merge(filter);
2548
2549 prepareDiscriminatorCriteria(this);
2550 } else if (filter != null) {
2551 this.error(new ObjectParameterError(filter, 'filter', 'remove'));
2552 }
2553
2554 if (!callback) {
2555 return Query.base.remove.call(this);
2556 }
2557
2558 this._remove(callback);
2559 return this;
2560};
2561
2562/*!
2563 * ignore
2564 */
2565
2566Query.prototype._remove = wrapThunk(function(callback) {
2567 this._castConditions();
2568
2569 if (this.error() != null) {
2570 callback(this.error());
2571 return this;
2572 }
2573
2574 callback = _wrapThunkCallback(this, callback);
2575
2576 return Query.base.remove.call(this, helpers.handleDeleteWriteOpResult(callback));
2577});
2578
2579/**
2580 * Declare and/or execute this query as a `deleteOne()` operation. Works like
2581 * remove, except it deletes at most one document regardless of the `single`
2582 * option.
2583 *
2584 * This function does not trigger any middleware.
2585 *
2586 * ####Example
2587 *
2588 * Character.deleteOne({ name: 'Eddard Stark' }, callback);
2589 * Character.deleteOne({ name: 'Eddard Stark' }).then(next);
2590 *
2591 * This function calls the MongoDB driver's [`Collection#deleteOne()` function](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#deleteOne).
2592 * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
2593 * object that contains 3 properties:
2594 *
2595 * - `ok`: `1` if no errors occurred
2596 * - `deletedCount`: the number of documents deleted
2597 * - `n`: the number of documents deleted. Equal to `deletedCount`.
2598 *
2599 * ####Example
2600 *
2601 * const res = await Character.deleteOne({ name: 'Eddard Stark' });
2602 * // `1` if MongoDB deleted a doc, `0` if no docs matched the filter `{ name: ... }`
2603 * res.deletedCount;
2604 *
2605 * @param {Object|Query} [filter] mongodb selector
2606 * @param {Function} [callback] optional params are (error, mongooseDeleteResult)
2607 * @return {Query} this
2608 * @see deleteWriteOpResult http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~deleteWriteOpResult
2609 * @see MongoDB Driver deleteOne http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#deleteOne
2610 * @api public
2611 */
2612
2613Query.prototype.deleteOne = function(filter, callback) {
2614 if (typeof filter === 'function') {
2615 callback = filter;
2616 filter = null;
2617 }
2618
2619 filter = utils.toObject(filter);
2620
2621 if (mquery.canMerge(filter)) {
2622 this.merge(filter);
2623
2624 prepareDiscriminatorCriteria(this);
2625 } else if (filter != null) {
2626 this.error(new ObjectParameterError(filter, 'filter', 'deleteOne'));
2627 }
2628
2629 if (!callback) {
2630 return Query.base.deleteOne.call(this);
2631 }
2632
2633 this._deleteOne.call(this, callback);
2634
2635 return this;
2636};
2637
2638/*!
2639 * Internal thunk for `deleteOne()`
2640 */
2641
2642Query.prototype._deleteOne = wrapThunk(function(callback) {
2643 this._castConditions();
2644
2645 if (this.error() != null) {
2646 callback(this.error());
2647 return this;
2648 }
2649
2650 callback = _wrapThunkCallback(this, callback);
2651
2652 return Query.base.deleteOne.call(this, helpers.handleDeleteWriteOpResult(callback));
2653});
2654
2655/**
2656 * Declare and/or execute this query as a `deleteMany()` operation. Works like
2657 * remove, except it deletes _every_ document that matches `filter` in the
2658 * collection, regardless of the value of `single`.
2659 *
2660 * This function does not trigger any middleware
2661 *
2662 * ####Example
2663 *
2664 * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, callback)
2665 * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }).then(next)
2666 *
2667 * This function calls the MongoDB driver's [`Collection#deleteMany()` function](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#deleteMany).
2668 * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
2669 * object that contains 3 properties:
2670 *
2671 * - `ok`: `1` if no errors occurred
2672 * - `deletedCount`: the number of documents deleted
2673 * - `n`: the number of documents deleted. Equal to `deletedCount`.
2674 *
2675 * ####Example
2676 *
2677 * const res = await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } });
2678 * // `0` if no docs matched the filter, number of docs deleted otherwise
2679 * res.deletedCount;
2680 *
2681 * @param {Object|Query} [filter] mongodb selector
2682 * @param {Function} [callback] optional params are (error, mongooseDeleteResult)
2683 * @return {Query} this
2684 * @see deleteWriteOpResult http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~deleteWriteOpResult
2685 * @see MongoDB Driver deleteMany http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#deleteMany
2686 * @api public
2687 */
2688
2689Query.prototype.deleteMany = function(filter, callback) {
2690 if (typeof filter === 'function') {
2691 callback = filter;
2692 filter = null;
2693 }
2694
2695 filter = utils.toObject(filter);
2696
2697 if (mquery.canMerge(filter)) {
2698 this.merge(filter);
2699
2700 prepareDiscriminatorCriteria(this);
2701 } else if (filter != null) {
2702 this.error(new ObjectParameterError(filter, 'filter', 'deleteMany'));
2703 }
2704
2705 if (!callback) {
2706 return Query.base.deleteMany.call(this);
2707 }
2708
2709 this._deleteMany.call(this, callback);
2710
2711 return this;
2712};
2713
2714/*!
2715 * Internal thunk around `deleteMany()`
2716 */
2717
2718Query.prototype._deleteMany = wrapThunk(function(callback) {
2719 this._castConditions();
2720
2721 if (this.error() != null) {
2722 callback(this.error());
2723 return this;
2724 }
2725
2726 callback = _wrapThunkCallback(this, callback);
2727
2728 return Query.base.deleteMany.call(this, helpers.handleDeleteWriteOpResult(callback));
2729});
2730
2731/*!
2732 * hydrates a document
2733 *
2734 * @param {Model} model
2735 * @param {Document} doc
2736 * @param {Object} res 3rd parameter to callback
2737 * @param {Object} fields
2738 * @param {Query} self
2739 * @param {Array} [pop] array of paths used in population
2740 * @param {Function} callback
2741 */
2742
2743function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) {
2744 const opts = pop ?
2745 {populated: pop}
2746 : undefined;
2747
2748 if (options.rawResult && doc == null) {
2749 _init(null);
2750 return null;
2751 }
2752
2753 const casted = helpers.createModel(model, doc, fields, userProvidedFields);
2754 try {
2755 casted.init(doc, opts, _init);
2756 } catch (error) {
2757 _init(error);
2758 }
2759
2760 function _init(err) {
2761 if (err) {
2762 return process.nextTick(() => callback(err));
2763 }
2764
2765
2766 if (options.rawResult) {
2767 if (doc && casted) {
2768 casted.$session(options.session);
2769 res.value = casted;
2770 } else {
2771 res.value = null;
2772 }
2773 return process.nextTick(() => callback(null, res));
2774 }
2775 casted.$session(options.session);
2776 process.nextTick(() => callback(null, casted));
2777 }
2778}
2779
2780/*!
2781 * If the model is a discriminator type and not root, then add the key & value to the criteria.
2782 */
2783
2784function prepareDiscriminatorCriteria(query) {
2785 if (!query || !query.model || !query.model.schema) {
2786 return;
2787 }
2788
2789 const schema = query.model.schema;
2790
2791 if (schema && schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
2792 query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
2793 }
2794}
2795
2796/**
2797 * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
2798 *
2799 * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found
2800 * document (if any) to the callback. The query executes if
2801 * `callback` is passed.
2802 *
2803 * This function triggers the following middleware.
2804 *
2805 * - `findOneAndUpdate()`
2806 *
2807 * ####Available options
2808 *
2809 * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
2810 * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
2811 * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
2812 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2813 * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2814 * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
2815 * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
2816 * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2817 * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
2818 *
2819 * ####Callback Signature
2820 * function(error, doc) {
2821 * // error: any errors that occurred
2822 * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
2823 * }
2824 *
2825 * ####Examples
2826 *
2827 * query.findOneAndUpdate(conditions, update, options, callback) // executes
2828 * query.findOneAndUpdate(conditions, update, options) // returns Query
2829 * query.findOneAndUpdate(conditions, update, callback) // executes
2830 * query.findOneAndUpdate(conditions, update) // returns Query
2831 * query.findOneAndUpdate(update, callback) // returns Query
2832 * query.findOneAndUpdate(update) // returns Query
2833 * query.findOneAndUpdate(callback) // executes
2834 * query.findOneAndUpdate() // returns Query
2835 *
2836 * @method findOneAndUpdate
2837 * @memberOf Query
2838 * @instance
2839 * @param {Object|Query} [filter]
2840 * @param {Object} [doc]
2841 * @param {Object} [options]
2842 * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2843 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2844 * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
2845 * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
2846 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
2847 * @param {Function} [callback] optional params are (error, doc), _unless_ `rawResult` is used, in which case params are (error, writeOpResult)
2848 * @see Tutorial /docs/tutorials/findoneandupdate.html
2849 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2850 * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
2851 * @return {Query} this
2852 * @api public
2853 */
2854
2855Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
2856 this.op = 'findOneAndUpdate';
2857 this._validate();
2858
2859 switch (arguments.length) {
2860 case 3:
2861 if (typeof options === 'function') {
2862 callback = options;
2863 options = {};
2864 }
2865 break;
2866 case 2:
2867 if (typeof doc === 'function') {
2868 callback = doc;
2869 doc = criteria;
2870 criteria = undefined;
2871 }
2872 options = undefined;
2873 break;
2874 case 1:
2875 if (typeof criteria === 'function') {
2876 callback = criteria;
2877 criteria = options = doc = undefined;
2878 } else {
2879 doc = criteria;
2880 criteria = options = undefined;
2881 }
2882 }
2883
2884 if (mquery.canMerge(criteria)) {
2885 this.merge(criteria);
2886 }
2887
2888 // apply doc
2889 if (doc) {
2890 this._mergeUpdate(doc);
2891 }
2892
2893 if (options) {
2894 options = utils.clone(options);
2895 if (options.projection) {
2896 this.select(options.projection);
2897 delete options.projection;
2898 }
2899 if (options.fields) {
2900 this.select(options.fields);
2901 delete options.fields;
2902 }
2903
2904 this.setOptions(options);
2905 }
2906
2907 if (!callback) {
2908 return this;
2909 }
2910
2911 this._findOneAndUpdate(callback);
2912
2913 return this;
2914};
2915
2916/*!
2917 * Thunk around findOneAndUpdate()
2918 *
2919 * @param {Function} [callback]
2920 * @api private
2921 */
2922
2923Query.prototype._findOneAndUpdate = wrapThunk(function(callback) {
2924 if (this.error() != null) {
2925 return callback(this.error());
2926 }
2927
2928 this._findAndModify('update', callback);
2929});
2930
2931/**
2932 * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
2933 *
2934 * Finds a matching document, removes it, passing the found document (if any) to
2935 * the callback. Executes if `callback` is passed.
2936 *
2937 * This function triggers the following middleware.
2938 *
2939 * - `findOneAndRemove()`
2940 *
2941 * ####Available options
2942 *
2943 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
2944 * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
2945 * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2946 *
2947 * ####Callback Signature
2948 * function(error, doc) {
2949 * // error: any errors that occurred
2950 * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
2951 * }
2952 *
2953 * ####Examples
2954 *
2955 * A.where().findOneAndRemove(conditions, options, callback) // executes
2956 * A.where().findOneAndRemove(conditions, options) // return Query
2957 * A.where().findOneAndRemove(conditions, callback) // executes
2958 * A.where().findOneAndRemove(conditions) // returns Query
2959 * A.where().findOneAndRemove(callback) // executes
2960 * A.where().findOneAndRemove() // returns Query
2961 *
2962 * @method findOneAndRemove
2963 * @memberOf Query
2964 * @instance
2965 * @param {Object} [conditions]
2966 * @param {Object} [options]
2967 * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
2968 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
2969 * @param {Function} [callback] optional params are (error, document)
2970 * @return {Query} this
2971 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
2972 * @api public
2973 */
2974
2975Query.prototype.findOneAndRemove = function(conditions, options, callback) {
2976 this.op = 'findOneAndRemove';
2977 this._validate();
2978
2979 switch (arguments.length) {
2980 case 2:
2981 if (typeof options === 'function') {
2982 callback = options;
2983 options = {};
2984 }
2985 break;
2986 case 1:
2987 if (typeof conditions === 'function') {
2988 callback = conditions;
2989 conditions = undefined;
2990 options = undefined;
2991 }
2992 break;
2993 }
2994
2995 if (mquery.canMerge(conditions)) {
2996 this.merge(conditions);
2997 }
2998
2999 options && this.setOptions(options);
3000
3001 if (!callback) {
3002 return this;
3003 }
3004
3005 this._findOneAndRemove(callback);
3006
3007 return this;
3008};
3009
3010/**
3011 * Issues a MongoDB [findOneAndDelete](https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndDelete/) command.
3012 *
3013 * Finds a matching document, removes it, and passes the found document (if any)
3014 * to the callback. Executes if `callback` is passed.
3015 *
3016 * This function triggers the following middleware.
3017 *
3018 * - `findOneAndDelete()`
3019 *
3020 * This function differs slightly from `Model.findOneAndRemove()` in that
3021 * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/),
3022 * as opposed to a `findOneAndDelete()` command. For most mongoose use cases,
3023 * this distinction is purely pedantic. You should use `findOneAndDelete()`
3024 * unless you have a good reason not to.
3025 *
3026 * ####Available options
3027 *
3028 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
3029 * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
3030 * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
3031 *
3032 * ####Callback Signature
3033 * function(error, doc) {
3034 * // error: any errors that occurred
3035 * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
3036 * }
3037 *
3038 * ####Examples
3039 *
3040 * A.where().findOneAndDelete(conditions, options, callback) // executes
3041 * A.where().findOneAndDelete(conditions, options) // return Query
3042 * A.where().findOneAndDelete(conditions, callback) // executes
3043 * A.where().findOneAndDelete(conditions) // returns Query
3044 * A.where().findOneAndDelete(callback) // executes
3045 * A.where().findOneAndDelete() // returns Query
3046 *
3047 * @method findOneAndDelete
3048 * @memberOf Query
3049 * @param {Object} [conditions]
3050 * @param {Object} [options]
3051 * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
3052 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3053 * @param {Function} [callback] optional params are (error, document)
3054 * @return {Query} this
3055 * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
3056 * @api public
3057 */
3058
3059Query.prototype.findOneAndDelete = function(conditions, options, callback) {
3060 this.op = 'findOneAndDelete';
3061 this._validate();
3062
3063 switch (arguments.length) {
3064 case 2:
3065 if (typeof options === 'function') {
3066 callback = options;
3067 options = {};
3068 }
3069 break;
3070 case 1:
3071 if (typeof conditions === 'function') {
3072 callback = conditions;
3073 conditions = undefined;
3074 options = undefined;
3075 }
3076 break;
3077 }
3078
3079 if (mquery.canMerge(conditions)) {
3080 this.merge(conditions);
3081 }
3082
3083 options && this.setOptions(options);
3084
3085 if (!callback) {
3086 return this;
3087 }
3088
3089 this._findOneAndDelete(callback);
3090
3091 return this;
3092};
3093
3094/*!
3095 * Thunk around findOneAndDelete()
3096 *
3097 * @param {Function} [callback]
3098 * @return {Query} this
3099 * @api private
3100 */
3101Query.prototype._findOneAndDelete = wrapThunk(function(callback) {
3102 this._castConditions();
3103
3104 if (this.error() != null) {
3105 callback(this.error());
3106 return null;
3107 }
3108
3109 const filter = this._conditions;
3110 const options = this._optionsForExec();
3111 let fields = null;
3112
3113 if (this._fields != null) {
3114 options.projection = this._castFields(utils.clone(this._fields));
3115 fields = options.projection;
3116 if (fields instanceof Error) {
3117 callback(fields);
3118 return null;
3119 }
3120 }
3121
3122 this._collection.collection.findOneAndDelete(filter, options, _wrapThunkCallback(this, (err, res) => {
3123 if (err) {
3124 return callback(err);
3125 }
3126
3127 const doc = res.value;
3128
3129 return this._completeOne(doc, res, callback);
3130 }));
3131});
3132
3133/**
3134 * Issues a MongoDB [findOneAndReplace](https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndReplace/) command.
3135 *
3136 * Finds a matching document, removes it, and passes the found document (if any)
3137 * to the callback. Executes if `callback` is passed.
3138 *
3139 * This function triggers the following middleware.
3140 *
3141 * - `findOneAndReplace()`
3142 *
3143 * ####Available options
3144 *
3145 * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
3146 * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
3147 * - `rawResult`: if true, resolves to the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
3148 *
3149 * ####Callback Signature
3150 * function(error, doc) {
3151 * // error: any errors that occurred
3152 * // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
3153 * }
3154 *
3155 * ####Examples
3156 *
3157 * A.where().findOneAndReplace(filter, replacement, options, callback); // executes
3158 * A.where().findOneAndReplace(filter, replacement, options); // return Query
3159 * A.where().findOneAndReplace(filter, replacement, callback); // executes
3160 * A.where().findOneAndReplace(filter); // returns Query
3161 * A.where().findOneAndReplace(callback); // executes
3162 * A.where().findOneAndReplace(); // returns Query
3163 *
3164 * @method findOneAndReplace
3165 * @memberOf Query
3166 * @param {Object} [filter]
3167 * @param {Object} [replacement]
3168 * @param {Object} [options]
3169 * @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
3170 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3171 * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
3172 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3173 * @param {Function} [callback] optional params are (error, document)
3174 * @return {Query} this
3175 * @api public
3176 */
3177
3178Query.prototype.findOneAndReplace = function(filter, replacement, options, callback) {
3179 this.op = 'findOneAndReplace';
3180 this._validate();
3181
3182 switch (arguments.length) {
3183 case 3:
3184 if (typeof options === 'function') {
3185 callback = options;
3186 options = void 0;
3187 }
3188 break;
3189 case 2:
3190 if (typeof replacement === 'function') {
3191 callback = replacement;
3192 replacement = void 0;
3193 }
3194 break;
3195 case 1:
3196 if (typeof filter === 'function') {
3197 callback = filter;
3198 filter = void 0;
3199 replacement = void 0;
3200 options = void 0;
3201 }
3202 break;
3203 }
3204
3205 if (mquery.canMerge(filter)) {
3206 this.merge(filter);
3207 }
3208
3209 if (replacement != null) {
3210 if (hasDollarKeys(replacement)) {
3211 throw new Error('The replacement document must not contain atomic operators.');
3212 }
3213 this._mergeUpdate(replacement);
3214 }
3215
3216 options && this.setOptions(options);
3217
3218 if (!callback) {
3219 return this;
3220 }
3221
3222 this._findOneAndReplace(callback);
3223
3224 return this;
3225};
3226
3227/*!
3228 * Thunk around findOneAndReplace()
3229 *
3230 * @param {Function} [callback]
3231 * @return {Query} this
3232 * @api private
3233 */
3234Query.prototype._findOneAndReplace = wrapThunk(function(callback) {
3235 this._castConditions();
3236
3237 if (this.error() != null) {
3238 callback(this.error());
3239 return null;
3240 }
3241
3242 const filter = this._conditions;
3243 const options = this._optionsForExec();
3244 let fields = null;
3245
3246 let castedDoc = new this.model(this._update, null, true);
3247
3248 this._applyPaths();
3249 if (this._fields != null) {
3250 options.projection = this._castFields(utils.clone(this._fields));
3251 fields = options.projection;
3252 if (fields instanceof Error) {
3253 callback(fields);
3254 return null;
3255 }
3256 }
3257
3258 castedDoc.validate(err => {
3259 if (err != null) {
3260 return callback(err);
3261 }
3262
3263 if (castedDoc.toBSON) {
3264 castedDoc = castedDoc.toBSON();
3265 }
3266
3267 this._collection.collection.findOneAndReplace(filter, castedDoc, options, (err, res) => {
3268 if (err) {
3269 return callback(err);
3270 }
3271
3272 const doc = res.value;
3273
3274 return this._completeOne(doc, res, callback);
3275 });
3276 });
3277});
3278
3279/*!
3280 * Thunk around findOneAndRemove()
3281 *
3282 * @param {Function} [callback]
3283 * @return {Query} this
3284 * @api private
3285 */
3286Query.prototype._findOneAndRemove = wrapThunk(function(callback) {
3287 if (this.error() != null) {
3288 callback(this.error());
3289 return;
3290 }
3291
3292 this._findAndModify('remove', callback);
3293});
3294
3295/*!
3296 * Get options from query opts, falling back to the base mongoose object.
3297 */
3298
3299function _getOption(query, option, def) {
3300 const opts = query._optionsForExec(query.model);
3301
3302 if (option in opts) {
3303 return opts[option];
3304 }
3305 if (option in query.model.base.options) {
3306 return query.model.base.options[option];
3307 }
3308 return def;
3309}
3310
3311/*!
3312 * Override mquery.prototype._findAndModify to provide casting etc.
3313 *
3314 * @param {String} type - either "remove" or "update"
3315 * @param {Function} callback
3316 * @api private
3317 */
3318
3319Query.prototype._findAndModify = function(type, callback) {
3320 if (typeof callback !== 'function') {
3321 throw new Error('Expected callback in _findAndModify');
3322 }
3323
3324 const model = this.model;
3325 const schema = model.schema;
3326 const _this = this;
3327 let castedDoc = this._update;
3328 let fields;
3329 let doValidate;
3330
3331 const castedQuery = castQuery(this);
3332 if (castedQuery instanceof Error) {
3333 return callback(castedQuery);
3334 }
3335
3336 _castArrayFilters(this);
3337
3338 const opts = this._optionsForExec(model);
3339
3340 if ('strict' in opts) {
3341 this._mongooseOptions.strict = opts.strict;
3342 }
3343
3344 const isOverwriting = this.options.overwrite && !hasDollarKeys(castedDoc);
3345 if (isOverwriting) {
3346 castedDoc = new this.model(castedDoc, null, true);
3347 }
3348
3349 if (type === 'remove') {
3350 opts.remove = true;
3351 } else {
3352 if (!('new' in opts)) {
3353 opts.new = false;
3354 }
3355 if (!('upsert' in opts)) {
3356 opts.upsert = false;
3357 }
3358 if (opts.upsert || opts['new']) {
3359 opts.remove = false;
3360 }
3361
3362 if (isOverwriting) {
3363 doValidate = function(callback) {
3364 castedDoc.validate(callback);
3365 };
3366 } else {
3367 castedDoc = castDoc(this, opts.overwrite);
3368 castedDoc = setDefaultsOnInsert(this._conditions, schema, castedDoc, opts);
3369 if (!castedDoc) {
3370 if (opts.upsert) {
3371 // still need to do the upsert to empty doc
3372 const doc = utils.clone(castedQuery);
3373 delete doc._id;
3374 castedDoc = {$set: doc};
3375 } else {
3376 this.findOne(callback);
3377 return this;
3378 }
3379 } else if (castedDoc instanceof Error) {
3380 return callback(castedDoc);
3381 } else {
3382 // In order to make MongoDB 2.6 happy (see
3383 // https://jira.mongodb.org/browse/SERVER-12266 and related issues)
3384 // if we have an actual update document but $set is empty, junk the $set.
3385 if (castedDoc.$set && Object.keys(castedDoc.$set).length === 0) {
3386 delete castedDoc.$set;
3387 }
3388 }
3389
3390 doValidate = updateValidators(this, schema, castedDoc, opts);
3391 }
3392 }
3393
3394 this._applyPaths();
3395
3396 const options = this._mongooseOptions;
3397
3398 if (this._fields) {
3399 fields = utils.clone(this._fields);
3400 opts.projection = this._castFields(fields);
3401 if (opts.projection instanceof Error) {
3402 return callback(opts.projection);
3403 }
3404 }
3405
3406 if (opts.sort) convertSortToArray(opts);
3407
3408 const cb = function(err, doc, res) {
3409 if (err) {
3410 return callback(err);
3411 }
3412
3413 _this._completeOne(doc, res, callback);
3414 };
3415
3416 let _callback;
3417
3418 let useFindAndModify = true;
3419 const runValidators = _getOption(this, 'runValidators', false);
3420 const base = _this.model && _this.model.base;
3421 const conn = get(model, 'collection.conn', {});
3422 if ('useFindAndModify' in base.options) {
3423 useFindAndModify = base.get('useFindAndModify');
3424 }
3425 if ('useFindAndModify' in conn.config) {
3426 useFindAndModify = conn.config.useFindAndModify;
3427 }
3428 if ('useFindAndModify' in options) {
3429 useFindAndModify = options.useFindAndModify;
3430 }
3431 if (useFindAndModify === false) {
3432 // Bypass mquery
3433 const collection = _this._collection.collection;
3434 if ('new' in opts) {
3435 opts.returnOriginal = !opts['new'];
3436 delete opts['new'];
3437 }
3438
3439 if (type === 'remove') {
3440 collection.findOneAndDelete(castedQuery, opts, _wrapThunkCallback(_this, function(error, res) {
3441 return cb(error, res ? res.value : res, res);
3442 }));
3443
3444 return this;
3445 }
3446
3447 // honors legacy overwrite option for backward compatibility
3448 const updateMethod = isOverwriting ? 'findOneAndReplace' : 'findOneAndUpdate';
3449
3450 if (runValidators && doValidate) {
3451 _callback = function(error) {
3452 if (error) {
3453 return callback(error);
3454 }
3455 if (castedDoc && castedDoc.toBSON) {
3456 castedDoc = castedDoc.toBSON();
3457 }
3458
3459 collection[updateMethod](castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
3460 return cb(error, res ? res.value : res, res);
3461 }));
3462 };
3463
3464 try {
3465 doValidate(_callback);
3466 } catch (error) {
3467 callback(error);
3468 }
3469 } else {
3470 if (castedDoc && castedDoc.toBSON) {
3471 castedDoc = castedDoc.toBSON();
3472 }
3473 collection[updateMethod](castedQuery, castedDoc, opts, _wrapThunkCallback(_this, function(error, res) {
3474 return cb(error, res ? res.value : res, res);
3475 }));
3476 }
3477
3478 return this;
3479 }
3480
3481 if (runValidators && doValidate) {
3482 _callback = function(error) {
3483 if (error) {
3484 return callback(error);
3485 }
3486 _legacyFindAndModify.call(_this, castedQuery, castedDoc, opts, cb);
3487 };
3488
3489 try {
3490 doValidate(_callback);
3491 } catch (error) {
3492 callback(error);
3493 }
3494 } else {
3495 _legacyFindAndModify.call(_this, castedQuery, castedDoc, opts, cb);
3496 }
3497
3498 return this;
3499};
3500
3501/*!
3502 * ignore
3503 */
3504
3505function _completeOneLean(doc, res, opts, callback) {
3506 if (opts.rawResult) {
3507 return callback(null, res);
3508 }
3509 return callback(null, doc);
3510}
3511
3512
3513/*!
3514 * ignore
3515 */
3516
3517const _legacyFindAndModify = util.deprecate(function(filter, update, opts, cb) {
3518 if (update && update.toBSON) {
3519 update = update.toBSON();
3520 }
3521 const collection = this._collection;
3522 const sort = opts != null && Array.isArray(opts.sort) ? opts.sort : [];
3523 const _cb = _wrapThunkCallback(this, function(error, res) {
3524 return cb(error, res ? res.value : res, res);
3525 });
3526 collection.collection._findAndModify(filter, sort, update, opts, _cb);
3527}, 'Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the ' +
3528 '`useFindAndModify` option set to false are deprecated. See: ' +
3529 'https://mongoosejs.com/docs/deprecations.html#-findandmodify-');
3530
3531/*!
3532 * Override mquery.prototype._mergeUpdate to handle mongoose objects in
3533 * updates.
3534 *
3535 * @param {Object} doc
3536 * @api private
3537 */
3538
3539Query.prototype._mergeUpdate = function(doc) {
3540 if (!this._update) this._update = {};
3541 if (doc instanceof Query) {
3542 if (doc._update) {
3543 utils.mergeClone(this._update, doc._update);
3544 }
3545 } else {
3546 utils.mergeClone(this._update, doc);
3547 }
3548};
3549
3550/*!
3551 * The mongodb driver 1.3.23 only supports the nested array sort
3552 * syntax. We must convert it or sorting findAndModify will not work.
3553 */
3554
3555function convertSortToArray(opts) {
3556 if (Array.isArray(opts.sort)) {
3557 return;
3558 }
3559 if (!utils.isObject(opts.sort)) {
3560 return;
3561 }
3562
3563 const sort = [];
3564
3565 for (const key in opts.sort) {
3566 if (utils.object.hasOwnProperty(opts.sort, key)) {
3567 sort.push([key, opts.sort[key]]);
3568 }
3569 }
3570
3571 opts.sort = sort;
3572}
3573
3574/*!
3575 * ignore
3576 */
3577
3578function _updateThunk(op, callback) {
3579 const schema = this.model.schema;
3580 let doValidate;
3581 const _this = this;
3582
3583 this._castConditions();
3584
3585 _castArrayFilters(this);
3586
3587 if (this.error() != null) {
3588 callback(this.error());
3589 return null;
3590 }
3591
3592 callback = _wrapThunkCallback(this, callback);
3593
3594 const castedQuery = this._conditions;
3595 let castedDoc;
3596 const options = this._optionsForExec(this.model);
3597
3598 ++this._executionCount;
3599
3600 this._update = utils.clone(this._update, options);
3601 const isOverwriting = this.options.overwrite && !hasDollarKeys(this._update);
3602 if (isOverwriting) {
3603 if (op === 'updateOne' || op === 'updateMany') {
3604 return callback(new MongooseError('The MongoDB server disallows ' +
3605 'overwriting documents using `' + op + '`. See: ' +
3606 'https://mongoosejs.com/docs/deprecations.html#-update-'));
3607 }
3608 castedDoc = new this.model(this._update, null, true);
3609 } else {
3610 castedDoc = castDoc(this, options.overwrite);
3611
3612 if (castedDoc instanceof Error) {
3613 callback(castedDoc);
3614 return null;
3615 }
3616
3617 if (castedDoc == null || Object.keys(castedDoc).length === 0) {
3618 callback(null, 0);
3619 return null;
3620 }
3621
3622 castedDoc = setDefaultsOnInsert(this._conditions, this.model.schema,
3623 castedDoc, options);
3624 }
3625
3626 const runValidators = _getOption(this, 'runValidators', false);
3627 if (runValidators) {
3628 if (isOverwriting) {
3629 doValidate = function(callback) {
3630 castedDoc.validate(callback);
3631 };
3632 } else {
3633 doValidate = updateValidators(this, schema, castedDoc, options);
3634 }
3635 const _callback = function(err) {
3636 if (err) {
3637 return callback(err);
3638 }
3639
3640 if (castedDoc.toBSON) {
3641 castedDoc = castedDoc.toBSON();
3642 }
3643 _this._collection[op](castedQuery, castedDoc, options, callback);
3644 };
3645 try {
3646 doValidate(_callback);
3647 } catch (err) {
3648 process.nextTick(function() {
3649 callback(err);
3650 });
3651 }
3652 return null;
3653 }
3654
3655 if (castedDoc.toBSON) {
3656 castedDoc = castedDoc.toBSON();
3657 }
3658
3659 this._collection[op](castedQuery, castedDoc, options, callback);
3660 return null;
3661}
3662
3663/*!
3664 * Internal thunk for .update()
3665 *
3666 * @param {Function} callback
3667 * @see Model.update #model_Model.update
3668 * @api private
3669 */
3670Query.prototype._execUpdate = wrapThunk(function(callback) {
3671 return _updateThunk.call(this, 'update', callback);
3672});
3673
3674/*!
3675 * Internal thunk for .updateMany()
3676 *
3677 * @param {Function} callback
3678 * @see Model.update #model_Model.update
3679 * @api private
3680 */
3681Query.prototype._updateMany = wrapThunk(function(callback) {
3682 return _updateThunk.call(this, 'updateMany', callback);
3683});
3684
3685/*!
3686 * Internal thunk for .updateOne()
3687 *
3688 * @param {Function} callback
3689 * @see Model.update #model_Model.update
3690 * @api private
3691 */
3692Query.prototype._updateOne = wrapThunk(function(callback) {
3693 return _updateThunk.call(this, 'updateOne', callback);
3694});
3695
3696/*!
3697 * Internal thunk for .replaceOne()
3698 *
3699 * @param {Function} callback
3700 * @see Model.replaceOne #model_Model.replaceOne
3701 * @api private
3702 */
3703Query.prototype._replaceOne = wrapThunk(function(callback) {
3704 return _updateThunk.call(this, 'replaceOne', callback);
3705});
3706
3707/**
3708 * Declare and/or execute this query as an update() operation.
3709 *
3710 * _All paths passed that are not [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) operations will become `$set` ops._
3711 *
3712 * This function triggers the following middleware.
3713 *
3714 * - `update()`
3715 *
3716 * ####Example
3717 *
3718 * Model.where({ _id: id }).update({ title: 'words' })
3719 *
3720 * // becomes
3721 *
3722 * Model.where({ _id: id }).update({ $set: { title: 'words' }})
3723 *
3724 * ####Valid options:
3725 *
3726 * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
3727 * - `multi` (boolean) whether multiple documents should be updated (false)
3728 * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
3729 * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
3730 * - `strict` (boolean) overrides the `strict` option for this update
3731 * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
3732 * - `context` (string) if set to 'query' and `runValidators` is on, `this` will refer to the query in custom validator functions that update validation runs. Does nothing if `runValidators` is false.
3733 * - `read`
3734 * - `writeConcern`
3735 *
3736 * ####Note
3737 *
3738 * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection.
3739 *
3740 * ####Note
3741 *
3742 * The operation is only executed when a callback is passed. To force execution without a callback, we must first call update() and then execute it by using the `exec()` method.
3743 *
3744 * var q = Model.where({ _id: id });
3745 * q.update({ $set: { name: 'bob' }}).update(); // not executed
3746 *
3747 * q.update({ $set: { name: 'bob' }}).exec(); // executed
3748 *
3749 * // keys that are not [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) ops become `$set`.
3750 * // this executes the same command as the previous example.
3751 * q.update({ name: 'bob' }).exec();
3752 *
3753 * // overwriting with empty docs
3754 * var q = Model.where({ _id: id }).setOptions({ overwrite: true })
3755 * q.update({ }, callback); // executes
3756 *
3757 * // multi update with overwrite to empty doc
3758 * var q = Model.where({ _id: id });
3759 * q.setOptions({ multi: true, overwrite: true })
3760 * q.update({ });
3761 * q.update(callback); // executed
3762 *
3763 * // multi updates
3764 * Model.where()
3765 * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback)
3766 *
3767 * // more multi updates
3768 * Model.where()
3769 * .setOptions({ multi: true })
3770 * .update({ $set: { arr: [] }}, callback)
3771 *
3772 * // single update by default
3773 * Model.where({ email: 'address@example.com' })
3774 * .update({ $inc: { counter: 1 }}, callback)
3775 *
3776 * API summary
3777 *
3778 * update(filter, doc, options, cb) // executes
3779 * update(filter, doc, options)
3780 * update(filter, doc, cb) // executes
3781 * update(filter, doc)
3782 * update(doc, cb) // executes
3783 * update(doc)
3784 * update(cb) // executes
3785 * update(true) // executes
3786 * update()
3787 *
3788 * @param {Object} [filter]
3789 * @param {Object} [doc] the update command
3790 * @param {Object} [options]
3791 * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3792 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3793 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3794 * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3795 * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3796 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3797 * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3798 * @param {Function} [callback] params are (error, writeOpResult)
3799 * @return {Query} this
3800 * @see Model.update #model_Model.update
3801 * @see Query docs https://mongoosejs.com/docs/queries.html
3802 * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
3803 * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3804 * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
3805 * @api public
3806 */
3807
3808Query.prototype.update = function(conditions, doc, options, callback) {
3809 if (typeof options === 'function') {
3810 // .update(conditions, doc, callback)
3811 callback = options;
3812 options = null;
3813 } else if (typeof doc === 'function') {
3814 // .update(doc, callback);
3815 callback = doc;
3816 doc = conditions;
3817 conditions = {};
3818 options = null;
3819 } else if (typeof conditions === 'function') {
3820 // .update(callback)
3821 callback = conditions;
3822 conditions = undefined;
3823 doc = undefined;
3824 options = undefined;
3825 } else if (typeof conditions === 'object' && !doc && !options && !callback) {
3826 // .update(doc)
3827 doc = conditions;
3828 conditions = undefined;
3829 options = undefined;
3830 callback = undefined;
3831 }
3832
3833 return _update(this, 'update', conditions, doc, options, callback);
3834};
3835
3836/**
3837 * Declare and/or execute this query as an updateMany() operation. Same as
3838 * `update()`, except MongoDB will update _all_ documents that match
3839 * `filter` (as opposed to just the first one) regardless of the value of
3840 * the `multi` option.
3841 *
3842 * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')`
3843 * and `post('updateMany')` instead.
3844 *
3845 * ####Example:
3846 * const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true });
3847 * res.n; // Number of documents matched
3848 * res.nModified; // Number of documents modified
3849 *
3850 * This function triggers the following middleware.
3851 *
3852 * - `updateMany()`
3853 *
3854 * @param {Object} [filter]
3855 * @param {Object} [doc] the update command
3856 * @param {Object} [options]
3857 * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3858 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3859 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3860 * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3861 * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3862 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3863 * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3864 * @param {Function} [callback] params are (error, writeOpResult)
3865 * @return {Query} this
3866 * @see Model.update #model_Model.update
3867 * @see Query docs https://mongoosejs.com/docs/queries.html
3868 * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
3869 * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3870 * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
3871 * @api public
3872 */
3873
3874Query.prototype.updateMany = function(conditions, doc, options, callback) {
3875 if (typeof options === 'function') {
3876 // .update(conditions, doc, callback)
3877 callback = options;
3878 options = null;
3879 } else if (typeof doc === 'function') {
3880 // .update(doc, callback);
3881 callback = doc;
3882 doc = conditions;
3883 conditions = {};
3884 options = null;
3885 } else if (typeof conditions === 'function') {
3886 // .update(callback)
3887 callback = conditions;
3888 conditions = undefined;
3889 doc = undefined;
3890 options = undefined;
3891 } else if (typeof conditions === 'object' && !doc && !options && !callback) {
3892 // .update(doc)
3893 doc = conditions;
3894 conditions = undefined;
3895 options = undefined;
3896 callback = undefined;
3897 }
3898
3899 return _update(this, 'updateMany', conditions, doc, options, callback);
3900};
3901
3902/**
3903 * Declare and/or execute this query as an updateOne() operation. Same as
3904 * `update()`, except it does not support the `multi` or `overwrite` options.
3905 *
3906 * - MongoDB will update _only_ the first document that matches `filter` regardless of the value of the `multi` option.
3907 * - Use `replaceOne()` if you want to overwrite an entire document rather than using [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) operators like `$set`.
3908 *
3909 * **Note** updateOne will _not_ fire update middleware. Use `pre('updateOne')`
3910 * and `post('updateOne')` instead.
3911 *
3912 * ####Example:
3913 * const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' });
3914 * res.n; // Number of documents matched
3915 * res.nModified; // Number of documents modified
3916 *
3917 * This function triggers the following middleware.
3918 *
3919 * - `updateOne()`
3920 *
3921 * @param {Object} [filter]
3922 * @param {Object} [doc] the update command
3923 * @param {Object} [options]
3924 * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3925 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3926 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3927 * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3928 * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3929 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3930 * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3931 * @param {Function} [callback] params are (error, writeOpResult)
3932 * @return {Query} this
3933 * @see Model.update #model_Model.update
3934 * @see Query docs https://mongoosejs.com/docs/queries.html
3935 * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
3936 * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
3937 * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
3938 * @api public
3939 */
3940
3941Query.prototype.updateOne = function(conditions, doc, options, callback) {
3942 if (typeof options === 'function') {
3943 // .update(conditions, doc, callback)
3944 callback = options;
3945 options = null;
3946 } else if (typeof doc === 'function') {
3947 // .update(doc, callback);
3948 callback = doc;
3949 doc = conditions;
3950 conditions = {};
3951 options = null;
3952 } else if (typeof conditions === 'function') {
3953 // .update(callback)
3954 callback = conditions;
3955 conditions = undefined;
3956 doc = undefined;
3957 options = undefined;
3958 } else if (typeof conditions === 'object' && !doc && !options && !callback) {
3959 // .update(doc)
3960 doc = conditions;
3961 conditions = undefined;
3962 options = undefined;
3963 callback = undefined;
3964 }
3965
3966 return _update(this, 'updateOne', conditions, doc, options, callback);
3967};
3968
3969/**
3970 * Declare and/or execute this query as a replaceOne() operation. Same as
3971 * `update()`, except MongoDB will replace the existing document and will
3972 * not accept any [atomic](https://docs.mongodb.com/manual/tutorial/model-data-for-atomic-operations/#pattern) operators (`$set`, etc.)
3973 *
3974 * **Note** replaceOne will _not_ fire update middleware. Use `pre('replaceOne')`
3975 * and `post('replaceOne')` instead.
3976 *
3977 * ####Example:
3978 * const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
3979 * res.n; // Number of documents matched
3980 * res.nModified; // Number of documents modified
3981 *
3982 * This function triggers the following middleware.
3983 *
3984 * - `replaceOne()`
3985 *
3986 * @param {Object} [filter]
3987 * @param {Object} [doc] the update command
3988 * @param {Object} [options]
3989 * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
3990 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3991 * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
3992 * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
3993 * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
3994 * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
3995 * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
3996 * @param {Function} [callback] params are (error, writeOpResult)
3997 * @return {Query} this
3998 * @see Model.update #model_Model.update
3999 * @see Query docs https://mongoosejs.com/docs/queries.html
4000 * @see update http://docs.mongodb.org/manual/reference/method/db.collection.update/
4001 * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
4002 * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
4003 * @api public
4004 */
4005
4006Query.prototype.replaceOne = function(conditions, doc, options, callback) {
4007 if (typeof options === 'function') {
4008 // .update(conditions, doc, callback)
4009 callback = options;
4010 options = null;
4011 } else if (typeof doc === 'function') {
4012 // .update(doc, callback);
4013 callback = doc;
4014 doc = conditions;
4015 conditions = {};
4016 options = null;
4017 } else if (typeof conditions === 'function') {
4018 // .update(callback)
4019 callback = conditions;
4020 conditions = undefined;
4021 doc = undefined;
4022 options = undefined;
4023 } else if (typeof conditions === 'object' && !doc && !options && !callback) {
4024 // .update(doc)
4025 doc = conditions;
4026 conditions = undefined;
4027 options = undefined;
4028 callback = undefined;
4029 }
4030
4031 this.setOptions({ overwrite: true });
4032 return _update(this, 'replaceOne', conditions, doc, options, callback);
4033};
4034
4035/*!
4036 * Internal helper for update, updateMany, updateOne, replaceOne
4037 */
4038
4039function _update(query, op, filter, doc, options, callback) {
4040 // make sure we don't send in the whole Document to merge()
4041 query.op = op;
4042 filter = utils.toObject(filter);
4043 doc = doc || {};
4044
4045 const oldCb = callback;
4046 if (oldCb) {
4047 if (typeof oldCb === 'function') {
4048 callback = function(error, result) {
4049 oldCb(error, result ? result.result : {ok: 0, n: 0, nModified: 0});
4050 };
4051 } else {
4052 throw new Error('Invalid callback() argument.');
4053 }
4054 }
4055
4056 // strict is an option used in the update checking, make sure it gets set
4057 if (options != null) {
4058 if ('strict' in options) {
4059 query._mongooseOptions.strict = options.strict;
4060 }
4061 }
4062
4063 if (!(filter instanceof Query) &&
4064 filter != null &&
4065 filter.toString() !== '[object Object]') {
4066 query.error(new ObjectParameterError(filter, 'filter', op));
4067 } else {
4068 query.merge(filter);
4069 }
4070
4071 if (utils.isObject(options)) {
4072 query.setOptions(options);
4073 }
4074
4075 query._mergeUpdate(doc);
4076
4077 // Hooks
4078 if (callback) {
4079 if (op === 'update') {
4080 query._execUpdate(callback);
4081 return query;
4082 }
4083 query['_' + op](callback);
4084 return query;
4085 }
4086
4087 return Query.base[op].call(query, filter, doc, options, callback);
4088}
4089
4090/**
4091 * Runs a function `fn` and treats the return value of `fn` as the new value
4092 * for the query to resolve to.
4093 *
4094 * Any functions you pass to `map()` will run **after** any post hooks.
4095 *
4096 * ####Example:
4097 *
4098 * const res = await MyModel.findOne().map(res => {
4099 * // Sets a `loadedAt` property on the doc that tells you the time the
4100 * // document was loaded.
4101 * return res == null ?
4102 * res :
4103 * Object.assign(res, { loadedAt: new Date() });
4104 * });
4105 *
4106 * @method map
4107 * @memberOf Query
4108 * @instance
4109 * @param {Function} fn function to run to transform the query result
4110 * @return {Query} this
4111 */
4112
4113Query.prototype.map = function(fn) {
4114 this._transforms.push(fn);
4115 return this;
4116};
4117
4118/**
4119 * Make this query throw an error if no documents match the given `filter`.
4120 * This is handy for integrating with async/await, because `orFail()` saves you
4121 * an extra `if` statement to check if no document was found.
4122 *
4123 * ####Example:
4124 *
4125 * // Throws if no doc returned
4126 * await Model.findOne({ foo: 'bar' }).orFail();
4127 *
4128 * // Throws if no document was updated
4129 * await Model.updateOne({ foo: 'bar' }, { name: 'test' }).orFail();
4130 *
4131 * // Throws "No docs found!" error if no docs match `{ foo: 'bar' }`
4132 * await Model.find({ foo: 'bar' }).orFail(new Error('No docs found!'));
4133 *
4134 * // Throws "Not found" error if no document was found
4135 * await Model.findOneAndUpdate({ foo: 'bar' }, { name: 'test' }).
4136 * orFail(() => Error('Not found'));
4137 *
4138 * @method orFail
4139 * @memberOf Query
4140 * @instance
4141 * @param {Function|Error} [err] optional error to throw if no docs match `filter`. If not specified, `orFail()` will throw a `DocumentNotFoundError`
4142 * @return {Query} this
4143 */
4144
4145Query.prototype.orFail = function(err) {
4146 this.map(res => {
4147 switch (this.op) {
4148 case 'find':
4149 if (res.length === 0) {
4150 throw _orFailError(err, this);
4151 }
4152 break;
4153 case 'findOne':
4154 if (res == null) {
4155 throw _orFailError(err, this);
4156 }
4157 break;
4158 case 'update':
4159 case 'updateMany':
4160 case 'updateOne':
4161 if (get(res, 'result.nModified') === 0) {
4162 throw _orFailError(err, this);
4163 }
4164 break;
4165 case 'findOneAndDelete':
4166 if (get(res, 'lastErrorObject.n') === 0) {
4167 throw _orFailError(err, this);
4168 }
4169 break;
4170 case 'findOneAndUpdate':
4171 if (get(res, 'lastErrorObject.updatedExisting') === false) {
4172 throw _orFailError(err, this);
4173 }
4174 break;
4175 case 'deleteMany':
4176 case 'deleteOne':
4177 case 'remove':
4178 if (res.n === 0) {
4179 throw _orFailError(err, this);
4180 }
4181 break;
4182 default:
4183 break;
4184 }
4185
4186 return res;
4187 });
4188 return this;
4189};
4190
4191/*!
4192 * Get the error to throw for `orFail()`
4193 */
4194
4195function _orFailError(err, query) {
4196 if (typeof err === 'function') {
4197 err = err.call(query);
4198 }
4199
4200 if (err == null) {
4201 err = new DocumentNotFoundError(query.getQuery(), query.model.modelName);
4202 }
4203
4204 return err;
4205}
4206
4207/**
4208 * Executes the query
4209 *
4210 * ####Examples:
4211 *
4212 * var promise = query.exec();
4213 * var promise = query.exec('update');
4214 *
4215 * query.exec(callback);
4216 * query.exec('find', callback);
4217 *
4218 * @param {String|Function} [operation]
4219 * @param {Function} [callback] optional params depend on the function being called
4220 * @return {Promise}
4221 * @api public
4222 */
4223
4224Query.prototype.exec = function exec(op, callback) {
4225 const _this = this;
4226
4227 if (typeof op === 'function') {
4228 callback = op;
4229 op = null;
4230 } else if (typeof op === 'string') {
4231 this.op = op;
4232 }
4233
4234 if (callback != null) {
4235 callback = this.model.$wrapCallback(callback);
4236 }
4237
4238 return utils.promiseOrCallback(callback, (cb) => {
4239 if (!_this.op) {
4240 cb();
4241 return;
4242 }
4243
4244 this._hooks.execPre('exec', this, [], (error) => {
4245 if (error) {
4246 return cb(error);
4247 }
4248 this[this.op].call(this, (error, res) => {
4249 if (error) {
4250 return cb(error);
4251 }
4252
4253 this._hooks.execPost('exec', this, [], {}, (error) => {
4254 if (error) {
4255 return cb(error);
4256 }
4257
4258 cb(null, res);
4259 });
4260 });
4261 });
4262 }, this.model.events);
4263};
4264
4265/*!
4266 * ignore
4267 */
4268
4269function _wrapThunkCallback(query, cb) {
4270 return function(error, res) {
4271 if (error != null) {
4272 return cb(error);
4273 }
4274
4275 for (const fn of query._transforms) {
4276 try {
4277 res = fn(res);
4278 } catch (error) {
4279 return cb(error);
4280 }
4281 }
4282
4283 return cb(null, res);
4284 };
4285}
4286
4287/**
4288 * Executes the query returning a `Promise` which will be
4289 * resolved with either the doc(s) or rejected with the error.
4290 *
4291 * @param {Function} [resolve]
4292 * @param {Function} [reject]
4293 * @return {Promise}
4294 * @api public
4295 */
4296
4297Query.prototype.then = function(resolve, reject) {
4298 return this.exec().then(resolve, reject);
4299};
4300
4301/**
4302 * Executes the query returning a `Promise` which will be
4303 * resolved with either the doc(s) or rejected with the error.
4304 * Like `.then()`, but only takes a rejection handler.
4305 *
4306 * @param {Function} [reject]
4307 * @return {Promise}
4308 * @api public
4309 */
4310
4311Query.prototype.catch = function(reject) {
4312 return this.exec().then(null, reject);
4313};
4314
4315/*!
4316 * ignore
4317 */
4318
4319Query.prototype._pre = function(fn) {
4320 this._hooks.pre('exec', fn);
4321 return this;
4322};
4323
4324/*!
4325 * ignore
4326 */
4327
4328Query.prototype._post = function(fn) {
4329 this._hooks.post('exec', fn);
4330 return this;
4331};
4332
4333/*!
4334 * Casts obj for an update command.
4335 *
4336 * @param {Object} obj
4337 * @return {Object} obj after casting its values
4338 * @api private
4339 */
4340
4341Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
4342 let strict;
4343 if ('strict' in this._mongooseOptions) {
4344 strict = this._mongooseOptions.strict;
4345 } else if (this.schema && this.schema.options) {
4346 strict = this.schema.options.strict;
4347 } else {
4348 strict = true;
4349 }
4350
4351 let omitUndefined = false;
4352 if ('omitUndefined' in this._mongooseOptions) {
4353 omitUndefined = this._mongooseOptions.omitUndefined;
4354 }
4355
4356 let useNestedStrict;
4357 if ('useNestedStrict' in this.options) {
4358 useNestedStrict = this.options.useNestedStrict;
4359 }
4360
4361 let schema = this.schema;
4362 const filter = this._conditions;
4363 if (schema != null &&
4364 utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) &&
4365 typeof filter[schema.options.discriminatorKey] !== 'object' &&
4366 schema.discriminators != null) {
4367 const discriminatorValue = filter[schema.options.discriminatorKey];
4368 const byValue = helpers.getDiscriminatorByValue(this.model, discriminatorValue);
4369 schema = schema.discriminators[discriminatorValue] ||
4370 (byValue && byValue.schema) ||
4371 schema;
4372 }
4373
4374 return castUpdate(schema, obj, {
4375 overwrite: overwrite,
4376 strict: strict,
4377 omitUndefined,
4378 useNestedStrict: useNestedStrict
4379 }, this, this._conditions);
4380};
4381
4382/*!
4383 * castQuery
4384 * @api private
4385 */
4386
4387function castQuery(query) {
4388 try {
4389 return query.cast(query.model);
4390 } catch (err) {
4391 return err;
4392 }
4393}
4394
4395/*!
4396 * castDoc
4397 * @api private
4398 */
4399
4400function castDoc(query, overwrite) {
4401 try {
4402 return query._castUpdate(query._update, overwrite);
4403 } catch (err) {
4404 return err;
4405 }
4406}
4407
4408/**
4409 * Specifies paths which should be populated with other documents.
4410 *
4411 * ####Example:
4412 *
4413 * Kitten.findOne().populate('owner').exec(function (err, kitten) {
4414 * console.log(kitten.owner.name) // Max
4415 * })
4416 *
4417 * Kitten.find().populate({
4418 * path: 'owner',
4419 * select: 'name',
4420 * match: { color: 'black' },
4421 * options: { sort: { name: -1 } }
4422 * }).exec(function (err, kittens) {
4423 * console.log(kittens[0].owner.name) // Zoopa
4424 * })
4425 *
4426 * // alternatively
4427 * Kitten.find().populate('owner', 'name', null, {sort: { name: -1 }}).exec(function (err, kittens) {
4428 * console.log(kittens[0].owner.name) // Zoopa
4429 * })
4430 *
4431 * Paths are populated after the query executes and a response is received. A
4432 * separate query is then executed for each path specified for population. After
4433 * a response for each query has also been returned, the results are passed to
4434 * the callback.
4435 *
4436 * @param {Object|String} path either the path to populate or an object specifying all parameters
4437 * @param {Object|String} [select] Field selection for the population query
4438 * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
4439 * @param {Object} [match] Conditions for the population query
4440 * @param {Object} [options] Options for the population query (sort, etc)
4441 * @param {String} [options.path=null] The path to populate.
4442 * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
4443 * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
4444 * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
4445 * @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://docs.mongodb.com/manual/tutorial/query-documents/), or a function that returns a filter object.
4446 * @see population ./populate.html
4447 * @see Query#select #query_Query-select
4448 * @see Model.populate #model_Model.populate
4449 * @return {Query} this
4450 * @api public
4451 */
4452
4453Query.prototype.populate = function() {
4454 // Bail when given no truthy arguments
4455 if (!Array.from(arguments).some(Boolean)) {
4456 return this;
4457 }
4458
4459 const res = utils.populate.apply(null, arguments);
4460
4461 // Propagate readConcern and readPreference and lean from parent query,
4462 // unless one already specified
4463 if (this.options != null) {
4464 const readConcern = this.options.readConcern;
4465 const readPref = this.options.readPreference;
4466
4467 for (let i = 0; i < res.length; ++i) {
4468 if (readConcern != null && get(res[i], 'options.readConcern') == null) {
4469 res[i].options = res[i].options || {};
4470 res[i].options.readConcern = readConcern;
4471 }
4472 if (readPref != null && get(res[i], 'options.readPreference') == null) {
4473 res[i].options = res[i].options || {};
4474 res[i].options.readPreference = readPref;
4475 }
4476 }
4477 }
4478
4479 const opts = this._mongooseOptions;
4480
4481 if (opts.lean != null) {
4482 const lean = opts.lean;
4483 for (let i = 0; i < res.length; ++i) {
4484 if (get(res[i], 'options.lean') == null) {
4485 res[i].options = res[i].options || {};
4486 res[i].options.lean = lean;
4487 }
4488 }
4489 }
4490
4491 if (!utils.isObject(opts.populate)) {
4492 opts.populate = {};
4493 }
4494
4495 const pop = opts.populate;
4496
4497 for (let i = 0; i < res.length; ++i) {
4498 const path = res[i].path;
4499 if (pop[path] && pop[path].populate && res[i].populate) {
4500 res[i].populate = pop[path].populate.concat(res[i].populate);
4501 }
4502 pop[res[i].path] = res[i];
4503 }
4504
4505 return this;
4506};
4507
4508/**
4509 * Gets a list of paths to be populated by this query
4510 *
4511 * ####Example:
4512 * bookSchema.pre('findOne', function() {
4513 * let keys = this.getPopulatedPaths(); // ['author']
4514 * });
4515 * ...
4516 * Book.findOne({}).populate('author');
4517 *
4518 * ####Example:
4519 * // Deep populate
4520 * const q = L1.find().populate({
4521 * path: 'level2',
4522 * populate: { path: 'level3' }
4523 * });
4524 * q.getPopulatedPaths(); // ['level2', 'level2.level3']
4525 *
4526 * @return {Array} an array of strings representing populated paths
4527 * @api public
4528 */
4529
4530Query.prototype.getPopulatedPaths = function getPopulatedPaths() {
4531 const obj = this._mongooseOptions.populate || {};
4532 const ret = Object.keys(obj);
4533 for (const path of Object.keys(obj)) {
4534 const pop = obj[path];
4535 if (!Array.isArray(pop.populate)) {
4536 continue;
4537 }
4538 _getPopulatedPaths(ret, pop.populate, path + '.');
4539 }
4540 return ret;
4541};
4542
4543/*!
4544 * ignore
4545 */
4546
4547function _getPopulatedPaths(list, arr, prefix) {
4548 for (const pop of arr) {
4549 list.push(prefix + pop.path);
4550 if (!Array.isArray(pop.populate)) {
4551 continue;
4552 }
4553 _getPopulatedPaths(list, pop.populate, prefix + pop.path + '.');
4554 }
4555}
4556
4557/**
4558 * Casts this query to the schema of `model`
4559 *
4560 * ####Note
4561 *
4562 * If `obj` is present, it is cast instead of this query.
4563 *
4564 * @param {Model} [model] the model to cast to. If not set, defaults to `this.model`
4565 * @param {Object} [obj]
4566 * @return {Object}
4567 * @api public
4568 */
4569
4570Query.prototype.cast = function(model, obj) {
4571 obj || (obj = this._conditions);
4572
4573 model = model || this.model;
4574
4575 try {
4576 return cast(model.schema, obj, {
4577 upsert: this.options && this.options.upsert,
4578 strict: (this.options && 'strict' in this.options) ?
4579 this.options.strict :
4580 get(model, 'schema.options.strict', null),
4581 strictQuery: (this.options && this.options.strictQuery) ||
4582 get(model, 'schema.options.strictQuery', null)
4583 }, this);
4584 } catch (err) {
4585 // CastError, assign model
4586 if (typeof err.setModel === 'function') {
4587 err.setModel(model);
4588 }
4589 throw err;
4590 }
4591};
4592
4593/**
4594 * Casts selected field arguments for field selection with mongo 2.2
4595 *
4596 * query.select({ ids: { $elemMatch: { $in: [hexString] }})
4597 *
4598 * @param {Object} fields
4599 * @see https://github.com/Automattic/mongoose/issues/1091
4600 * @see http://docs.mongodb.org/manual/reference/projection/elemMatch/
4601 * @api private
4602 */
4603
4604Query.prototype._castFields = function _castFields(fields) {
4605 let selected,
4606 elemMatchKeys,
4607 keys,
4608 key,
4609 out,
4610 i;
4611
4612 if (fields) {
4613 keys = Object.keys(fields);
4614 elemMatchKeys = [];
4615 i = keys.length;
4616
4617 // collect $elemMatch args
4618 while (i--) {
4619 key = keys[i];
4620 if (fields[key].$elemMatch) {
4621 selected || (selected = {});
4622 selected[key] = fields[key];
4623 elemMatchKeys.push(key);
4624 }
4625 }
4626 }
4627
4628 if (selected) {
4629 // they passed $elemMatch, cast em
4630 try {
4631 out = this.cast(this.model, selected);
4632 } catch (err) {
4633 return err;
4634 }
4635
4636 // apply the casted field args
4637 i = elemMatchKeys.length;
4638 while (i--) {
4639 key = elemMatchKeys[i];
4640 fields[key] = out[key];
4641 }
4642 }
4643
4644 return fields;
4645};
4646
4647/**
4648 * Applies schematype selected options to this query.
4649 * @api private
4650 */
4651
4652Query.prototype._applyPaths = function applyPaths() {
4653 this._fields = this._fields || {};
4654 helpers.applyPaths(this._fields, this.model.schema);
4655
4656 let _selectPopulatedPaths = true;
4657
4658 if ('selectPopulatedPaths' in this.model.base.options) {
4659 _selectPopulatedPaths = this.model.base.options.selectPopulatedPaths;
4660 }
4661 if ('selectPopulatedPaths' in this.model.schema.options) {
4662 _selectPopulatedPaths = this.model.schema.options.selectPopulatedPaths;
4663 }
4664
4665 if (_selectPopulatedPaths) {
4666 selectPopulatedFields(this);
4667 }
4668};
4669
4670/**
4671 * Returns a wrapper around a [mongodb driver cursor](http://mongodb.github.io/node-mongodb-native/2.1/api/Cursor.html).
4672 * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function.
4673 *
4674 * The `.cursor()` function triggers pre find hooks, but **not** post find hooks.
4675 *
4676 * ####Example
4677 *
4678 * // There are 2 ways to use a cursor. First, as a stream:
4679 * Thing.
4680 * find({ name: /^hello/ }).
4681 * cursor().
4682 * on('data', function(doc) { console.log(doc); }).
4683 * on('end', function() { console.log('Done!'); });
4684 *
4685 * // Or you can use `.next()` to manually get the next doc in the stream.
4686 * // `.next()` returns a promise, so you can use promises or callbacks.
4687 * var cursor = Thing.find({ name: /^hello/ }).cursor();
4688 * cursor.next(function(error, doc) {
4689 * console.log(doc);
4690 * });
4691 *
4692 * // Because `.next()` returns a promise, you can use co
4693 * // to easily iterate through all documents without loading them
4694 * // all into memory.
4695 * co(function*() {
4696 * const cursor = Thing.find({ name: /^hello/ }).cursor();
4697 * for (let doc = yield cursor.next(); doc != null; doc = yield cursor.next()) {
4698 * console.log(doc);
4699 * }
4700 * });
4701 *
4702 * ####Valid options
4703 *
4704 * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data` and returned by `.next()`.
4705 *
4706 * @return {QueryCursor}
4707 * @param {Object} [options]
4708 * @see QueryCursor
4709 * @api public
4710 */
4711
4712Query.prototype.cursor = function cursor(opts) {
4713 this._applyPaths();
4714 this._fields = this._castFields(this._fields);
4715 this.setOptions({ projection: this._fieldsForExec() });
4716 if (opts) {
4717 this.setOptions(opts);
4718 }
4719
4720 const options = Object.assign({}, this.options, {
4721 projection: this.projection()
4722 });
4723 try {
4724 this.cast(this.model);
4725 } catch (err) {
4726 return (new QueryCursor(this, options))._markError(err);
4727 }
4728
4729 return new QueryCursor(this, options);
4730};
4731
4732// the rest of these are basically to support older Mongoose syntax with mquery
4733
4734/**
4735 * _DEPRECATED_ Alias of `maxScan`
4736 *
4737 * @deprecated
4738 * @see maxScan #query_Query-maxScan
4739 * @method maxscan
4740 * @memberOf Query
4741 * @instance
4742 */
4743
4744Query.prototype.maxscan = Query.base.maxScan;
4745
4746/**
4747 * Sets the tailable option (for use with capped collections).
4748 *
4749 * ####Example
4750 *
4751 * query.tailable() // true
4752 * query.tailable(true)
4753 * query.tailable(false)
4754 *
4755 * ####Note
4756 *
4757 * Cannot be used with `distinct()`
4758 *
4759 * @param {Boolean} bool defaults to true
4760 * @param {Object} [opts] options to set
4761 * @param {Number} [opts.numberOfRetries] if cursor is exhausted, retry this many times before giving up
4762 * @param {Number} [opts.tailableRetryInterval] if cursor is exhausted, wait this many milliseconds before retrying
4763 * @see tailable http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/
4764 * @api public
4765 */
4766
4767Query.prototype.tailable = function(val, opts) {
4768 // we need to support the tailable({ awaitdata : true }) as well as the
4769 // tailable(true, {awaitdata :true}) syntax that mquery does not support
4770 if (val && val.constructor.name === 'Object') {
4771 opts = val;
4772 val = true;
4773 }
4774
4775 if (val === undefined) {
4776 val = true;
4777 }
4778
4779 if (opts && typeof opts === 'object') {
4780 for (const key in opts) {
4781 if (key === 'awaitdata') {
4782 // For backwards compatibility
4783 this.options[key] = !!opts[key];
4784 } else {
4785 this.options[key] = opts[key];
4786 }
4787 }
4788 }
4789
4790 return Query.base.tailable.call(this, val);
4791};
4792
4793/**
4794 * Declares an intersects query for `geometry()`.
4795 *
4796 * ####Example
4797 *
4798 * query.where('path').intersects().geometry({
4799 * type: 'LineString'
4800 * , coordinates: [[180.0, 11.0], [180, 9.0]]
4801 * })
4802 *
4803 * query.where('path').intersects({
4804 * type: 'LineString'
4805 * , coordinates: [[180.0, 11.0], [180, 9.0]]
4806 * })
4807 *
4808 * ####NOTE:
4809 *
4810 * **MUST** be used after `where()`.
4811 *
4812 * ####NOTE:
4813 *
4814 * In Mongoose 3.7, `intersects` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
4815 *
4816 * @method intersects
4817 * @memberOf Query
4818 * @instance
4819 * @param {Object} [arg]
4820 * @return {Query} this
4821 * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
4822 * @see geoIntersects http://docs.mongodb.org/manual/reference/operator/geoIntersects/
4823 * @api public
4824 */
4825
4826/**
4827 * Specifies a `$geometry` condition
4828 *
4829 * ####Example
4830 *
4831 * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
4832 * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
4833 *
4834 * // or
4835 * var polyB = [[ 0, 0 ], [ 1, 1 ]]
4836 * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
4837 *
4838 * // or
4839 * var polyC = [ 0, 0 ]
4840 * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
4841 *
4842 * // or
4843 * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
4844 *
4845 * The argument is assigned to the most recent path passed to `where()`.
4846 *
4847 * ####NOTE:
4848 *
4849 * `geometry()` **must** come after either `intersects()` or `within()`.
4850 *
4851 * The `object` argument must contain `type` and `coordinates` properties.
4852 * - type {String}
4853 * - coordinates {Array}
4854 *
4855 * @method geometry
4856 * @memberOf Query
4857 * @instance
4858 * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
4859 * @return {Query} this
4860 * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
4861 * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
4862 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
4863 * @api public
4864 */
4865
4866/**
4867 * Specifies a `$near` or `$nearSphere` condition
4868 *
4869 * These operators return documents sorted by distance.
4870 *
4871 * ####Example
4872 *
4873 * query.where('loc').near({ center: [10, 10] });
4874 * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
4875 * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
4876 * query.near('loc', { center: [10, 10], maxDistance: 5 });
4877 *
4878 * @method near
4879 * @memberOf Query
4880 * @instance
4881 * @param {String} [path]
4882 * @param {Object} val
4883 * @return {Query} this
4884 * @see $near http://docs.mongodb.org/manual/reference/operator/near/
4885 * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
4886 * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
4887 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
4888 * @api public
4889 */
4890
4891/*!
4892 * Overwriting mquery is needed to support a couple different near() forms found in older
4893 * versions of mongoose
4894 * near([1,1])
4895 * near(1,1)
4896 * near(field, [1,2])
4897 * near(field, 1, 2)
4898 * In addition to all of the normal forms supported by mquery
4899 */
4900
4901Query.prototype.near = function() {
4902 const params = [];
4903 const sphere = this._mongooseOptions.nearSphere;
4904
4905 // TODO refactor
4906
4907 if (arguments.length === 1) {
4908 if (Array.isArray(arguments[0])) {
4909 params.push({center: arguments[0], spherical: sphere});
4910 } else if (typeof arguments[0] === 'string') {
4911 // just passing a path
4912 params.push(arguments[0]);
4913 } else if (utils.isObject(arguments[0])) {
4914 if (typeof arguments[0].spherical !== 'boolean') {
4915 arguments[0].spherical = sphere;
4916 }
4917 params.push(arguments[0]);
4918 } else {
4919 throw new TypeError('invalid argument');
4920 }
4921 } else if (arguments.length === 2) {
4922 if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
4923 params.push({center: [arguments[0], arguments[1]], spherical: sphere});
4924 } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) {
4925 params.push(arguments[0]);
4926 params.push({center: arguments[1], spherical: sphere});
4927 } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) {
4928 params.push(arguments[0]);
4929 if (typeof arguments[1].spherical !== 'boolean') {
4930 arguments[1].spherical = sphere;
4931 }
4932 params.push(arguments[1]);
4933 } else {
4934 throw new TypeError('invalid argument');
4935 }
4936 } else if (arguments.length === 3) {
4937 if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number'
4938 && typeof arguments[2] === 'number') {
4939 params.push(arguments[0]);
4940 params.push({center: [arguments[1], arguments[2]], spherical: sphere});
4941 } else {
4942 throw new TypeError('invalid argument');
4943 }
4944 } else {
4945 throw new TypeError('invalid argument');
4946 }
4947
4948 return Query.base.near.apply(this, params);
4949};
4950
4951/**
4952 * _DEPRECATED_ Specifies a `$nearSphere` condition
4953 *
4954 * ####Example
4955 *
4956 * query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 });
4957 *
4958 * **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`.
4959 *
4960 * ####Example
4961 *
4962 * query.where('loc').near({ center: [10, 10], spherical: true });
4963 *
4964 * @deprecated
4965 * @see near() #query_Query-near
4966 * @see $near http://docs.mongodb.org/manual/reference/operator/near/
4967 * @see $nearSphere http://docs.mongodb.org/manual/reference/operator/nearSphere/
4968 * @see $maxDistance http://docs.mongodb.org/manual/reference/operator/maxDistance/
4969 */
4970
4971Query.prototype.nearSphere = function() {
4972 this._mongooseOptions.nearSphere = true;
4973 this.near.apply(this, arguments);
4974 return this;
4975};
4976
4977/**
4978 * Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
4979 * This function *only* works for `find()` queries.
4980 * You do not need to call this function explicitly, the JavaScript runtime
4981 * will call it for you.
4982 *
4983 * ####Example
4984 *
4985 * for await (const doc of Model.aggregate([{ $sort: { name: 1 } }])) {
4986 * console.log(doc.name);
4987 * }
4988 *
4989 * Node.js 10.x supports async iterators natively without any flags. You can
4990 * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
4991 *
4992 * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
4993 * `Symbol.asyncIterator` is undefined, that means your Node.js version does not
4994 * support async iterators.
4995 *
4996 * @method Symbol.asyncIterator
4997 * @memberOf Query
4998 * @instance
4999 * @api public
5000 */
5001
5002if (Symbol.asyncIterator != null) {
5003 Query.prototype[Symbol.asyncIterator] = function() {
5004 return this.cursor().transformNull().map(doc => {
5005 return doc == null ? { done: true } : { value: doc, done: false };
5006 });
5007 };
5008}
5009
5010/**
5011 * Specifies a `$polygon` condition
5012 *
5013 * ####Example
5014 *
5015 * query.where('loc').within().polygon([10,20], [13, 25], [7,15])
5016 * query.polygon('loc', [10,20], [13, 25], [7,15])
5017 *
5018 * @method polygon
5019 * @memberOf Query
5020 * @instance
5021 * @param {String|Array} [path]
5022 * @param {Array|Object} [coordinatePairs...]
5023 * @return {Query} this
5024 * @see $polygon http://docs.mongodb.org/manual/reference/operator/polygon/
5025 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
5026 * @api public
5027 */
5028
5029/**
5030 * Specifies a `$box` condition
5031 *
5032 * ####Example
5033 *
5034 * var lowerLeft = [40.73083, -73.99756]
5035 * var upperRight= [40.741404, -73.988135]
5036 *
5037 * query.where('loc').within().box(lowerLeft, upperRight)
5038 * query.box({ ll : lowerLeft, ur : upperRight })
5039 *
5040 * @method box
5041 * @memberOf Query
5042 * @instance
5043 * @see $box http://docs.mongodb.org/manual/reference/operator/box/
5044 * @see within() Query#within #query_Query-within
5045 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
5046 * @param {Object} val
5047 * @param [Array] Upper Right Coords
5048 * @return {Query} this
5049 * @api public
5050 */
5051
5052/*!
5053 * this is needed to support the mongoose syntax of:
5054 * box(field, { ll : [x,y], ur : [x2,y2] })
5055 * box({ ll : [x,y], ur : [x2,y2] })
5056 */
5057
5058Query.prototype.box = function(ll, ur) {
5059 if (!Array.isArray(ll) && utils.isObject(ll)) {
5060 ur = ll.ur;
5061 ll = ll.ll;
5062 }
5063 return Query.base.box.call(this, ll, ur);
5064};
5065
5066/**
5067 * Specifies a `$center` or `$centerSphere` condition.
5068 *
5069 * ####Example
5070 *
5071 * var area = { center: [50, 50], radius: 10, unique: true }
5072 * query.where('loc').within().circle(area)
5073 * // alternatively
5074 * query.circle('loc', area);
5075 *
5076 * // spherical calculations
5077 * var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
5078 * query.where('loc').within().circle(area)
5079 * // alternatively
5080 * query.circle('loc', area);
5081 *
5082 * @method circle
5083 * @memberOf Query
5084 * @instance
5085 * @param {String} [path]
5086 * @param {Object} area
5087 * @return {Query} this
5088 * @see $center http://docs.mongodb.org/manual/reference/operator/center/
5089 * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
5090 * @see $geoWithin http://docs.mongodb.org/manual/reference/operator/geoWithin/
5091 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
5092 * @api public
5093 */
5094
5095/**
5096 * _DEPRECATED_ Alias for [circle](#query_Query-circle)
5097 *
5098 * **Deprecated.** Use [circle](#query_Query-circle) instead.
5099 *
5100 * @deprecated
5101 * @method center
5102 * @memberOf Query
5103 * @instance
5104 * @api public
5105 */
5106
5107Query.prototype.center = Query.base.circle;
5108
5109/**
5110 * _DEPRECATED_ Specifies a `$centerSphere` condition
5111 *
5112 * **Deprecated.** Use [circle](#query_Query-circle) instead.
5113 *
5114 * ####Example
5115 *
5116 * var area = { center: [50, 50], radius: 10 };
5117 * query.where('loc').within().centerSphere(area);
5118 *
5119 * @deprecated
5120 * @param {String} [path]
5121 * @param {Object} val
5122 * @return {Query} this
5123 * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
5124 * @see $centerSphere http://docs.mongodb.org/manual/reference/operator/centerSphere/
5125 * @api public
5126 */
5127
5128Query.prototype.centerSphere = function() {
5129 if (arguments[0] && arguments[0].constructor.name === 'Object') {
5130 arguments[0].spherical = true;
5131 }
5132
5133 if (arguments[1] && arguments[1].constructor.name === 'Object') {
5134 arguments[1].spherical = true;
5135 }
5136
5137 Query.base.circle.apply(this, arguments);
5138};
5139
5140/**
5141 * Determines if field selection has been made.
5142 *
5143 * @method selected
5144 * @memberOf Query
5145 * @instance
5146 * @return {Boolean}
5147 * @api public
5148 */
5149
5150/**
5151 * Determines if inclusive field selection has been made.
5152 *
5153 * query.selectedInclusively() // false
5154 * query.select('name')
5155 * query.selectedInclusively() // true
5156 *
5157 * @method selectedInclusively
5158 * @memberOf Query
5159 * @instance
5160 * @return {Boolean}
5161 * @api public
5162 */
5163
5164Query.prototype.selectedInclusively = function selectedInclusively() {
5165 return isInclusive(this._fields);
5166};
5167
5168/**
5169 * Determines if exclusive field selection has been made.
5170 *
5171 * query.selectedExclusively() // false
5172 * query.select('-name')
5173 * query.selectedExclusively() // true
5174 * query.selectedInclusively() // false
5175 *
5176 * @method selectedExclusively
5177 * @memberOf Query
5178 * @instance
5179 * @return {Boolean}
5180 * @api public
5181 */
5182
5183Query.prototype.selectedExclusively = function selectedExclusively() {
5184 if (!this._fields) {
5185 return false;
5186 }
5187
5188 const keys = Object.keys(this._fields);
5189 if (keys.length === 0) {
5190 return false;
5191 }
5192
5193 for (let i = 0; i < keys.length; ++i) {
5194 const key = keys[i];
5195 if (key === '_id') {
5196 continue;
5197 }
5198 if (this._fields[key] === 0 || this._fields[key] === false) {
5199 return true;
5200 }
5201 }
5202
5203 return false;
5204};
5205
5206/*!
5207 * Export
5208 */
5209
5210module.exports = Query;