UNPKG

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