UNPKG

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