UNPKG

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