mongoose 9.1.6 → 9.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const objectIdSymbol = require('../helpers/symbols').objectIdSymbol;
4
+ const symbols = require('../schema/symbols');
4
5
  const utils = require('../utils');
5
6
 
6
7
  /*!
@@ -8,24 +9,34 @@ const utils = require('../utils');
8
9
  */
9
10
 
10
11
  module.exports = function shardingPlugin(schema) {
11
- schema.post('init', function shardingPluginPostInit() {
12
- storeShard.call(this);
13
- return this;
14
- });
15
- schema.pre('save', function shardingPluginPreSave() {
16
- applyWhere.call(this);
17
- });
18
- schema.pre('deleteOne', { document: true, query: false }, function shardingPluginPreDeleteOne() {
19
- applyWhere.call(this);
20
- });
21
- schema.pre('updateOne', { document: true, query: false }, function shardingPluginPreUpdateOne() {
22
- applyWhere.call(this);
23
- });
24
- schema.post('save', function shardingPluginPostSave() {
25
- storeShard.call(this);
26
- });
12
+ schema.post('init', shardingPluginPostInit);
13
+ schema.pre('save', shardingPluginPreSave);
14
+ schema.post('save', shardingPluginPostSave);
15
+ schema.pre('deleteOne', { document: true, query: false }, shardingPluginPreDeleteOne);
16
+ schema.pre('updateOne', { document: true, query: false }, shardingPluginPreUpdateOne);
27
17
  };
28
18
 
19
+ function shardingPluginPostInit() {
20
+ storeShard.call(this);
21
+ return this;
22
+ }
23
+
24
+ function shardingPluginPreSave() {
25
+ applyWhere.call(this);
26
+ }
27
+
28
+ function shardingPluginPostSave() {
29
+ storeShard.call(this);
30
+ }
31
+
32
+ function shardingPluginPreDeleteOne() {
33
+ applyWhere.call(this);
34
+ }
35
+
36
+ function shardingPluginPreUpdateOne() {
37
+ applyWhere.call(this);
38
+ }
39
+
29
40
  /*!
30
41
  * ignore
31
42
  */
@@ -82,3 +93,9 @@ function storeShard() {
82
93
  }
83
94
  }
84
95
  }
96
+
97
+ shardingPluginPostInit[symbols.builtInMiddleware] = true;
98
+ shardingPluginPreSave[symbols.builtInMiddleware] = true;
99
+ shardingPluginPostSave[symbols.builtInMiddleware] = true;
100
+ shardingPluginPreDeleteOne[symbols.builtInMiddleware] = true;
101
+ shardingPluginPreUpdateOne[symbols.builtInMiddleware] = true;
@@ -2,34 +2,37 @@
2
2
 
3
3
  const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol;
4
4
  const sessionNewDocuments = require('../helpers/symbols').sessionNewDocuments;
5
+ const symbols = require('../schema/symbols');
5
6
  const utils = require('../utils');
6
7
 
7
8
  module.exports = function trackTransaction(schema) {
8
- schema.pre('save', function trackTransactionPreSave() {
9
- const session = this.$session();
10
- if (session == null) {
11
- return;
9
+ schema.pre('save', trackTransactionPreSave);
10
+ };
11
+
12
+ function trackTransactionPreSave() {
13
+ const session = this.$session();
14
+ if (session == null) {
15
+ return;
16
+ }
17
+ if (session.transaction == null || session[sessionNewDocuments] == null) {
18
+ return;
19
+ }
20
+
21
+ if (!session[sessionNewDocuments].has(this)) {
22
+ const initialState = {};
23
+ if (this.isNew) {
24
+ initialState.isNew = true;
12
25
  }
13
- if (session.transaction == null || session[sessionNewDocuments] == null) {
14
- return;
26
+ if (this.$__schema.options.versionKey) {
27
+ initialState.versionKey = this.get(this.$__schema.options.versionKey);
15
28
  }
16
29
 
17
- if (!session[sessionNewDocuments].has(this)) {
18
- const initialState = {};
19
- if (this.isNew) {
20
- initialState.isNew = true;
21
- }
22
- if (this.$__schema.options.versionKey) {
23
- initialState.versionKey = this.get(this.$__schema.options.versionKey);
24
- }
25
-
26
- initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.getStatePaths('modify')));
27
- initialState.atomics = _getAtomics(this);
30
+ initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.getStatePaths('modify')));
31
+ initialState.atomics = _getAtomics(this);
28
32
 
29
- session[sessionNewDocuments].set(this, initialState);
30
- }
31
- });
32
- };
33
+ session[sessionNewDocuments].set(this, initialState);
34
+ }
35
+ }
33
36
 
34
37
  function _getAtomics(doc, previous) {
35
38
  const pathToAtomics = new Map();
@@ -82,3 +85,5 @@ function mergeAtomics(destination, source) {
82
85
 
83
86
  return destination;
84
87
  }
88
+
89
+ trackTransactionPreSave[symbols.builtInMiddleware] = true;
@@ -1,41 +1,47 @@
1
1
  'use strict';
2
2
 
3
+ const symbols = require('../schema/symbols');
4
+
3
5
  /*!
4
6
  * ignore
5
7
  */
6
8
 
7
9
  module.exports = function validateBeforeSave(schema) {
8
10
  const unshift = true;
9
- schema.pre('save', false, async function validateBeforeSave(options) {
10
- // Nested docs have their own presave
11
- if (this.$isSubdocument) {
12
- return;
13
- }
11
+ schema.pre('save', false, validateBeforeSavePreSave, null, unshift);
12
+ };
13
+
14
+ async function validateBeforeSavePreSave(options) {
15
+ // Nested docs have their own presave
16
+ if (this.$isSubdocument) {
17
+ return;
18
+ }
19
+
20
+ const hasValidateBeforeSaveOption = options &&
21
+ (typeof options === 'object') &&
22
+ ('validateBeforeSave' in options);
14
23
 
15
- const hasValidateBeforeSaveOption = options &&
24
+ let shouldValidate;
25
+ if (hasValidateBeforeSaveOption) {
26
+ shouldValidate = !!options.validateBeforeSave;
27
+ } else {
28
+ shouldValidate = this.$__schema.options.validateBeforeSave;
29
+ }
30
+
31
+ // Validate
32
+ if (shouldValidate) {
33
+ const hasValidateModifiedOnlyOption = options &&
16
34
  (typeof options === 'object') &&
17
- ('validateBeforeSave' in options);
18
-
19
- let shouldValidate;
20
- if (hasValidateBeforeSaveOption) {
21
- shouldValidate = !!options.validateBeforeSave;
22
- } else {
23
- shouldValidate = this.$__schema.options.validateBeforeSave;
24
- }
25
-
26
- // Validate
27
- if (shouldValidate) {
28
- const hasValidateModifiedOnlyOption = options &&
29
- (typeof options === 'object') &&
30
- ('validateModifiedOnly' in options);
31
- const validateOptions = hasValidateModifiedOnlyOption ?
32
- { validateModifiedOnly: options.validateModifiedOnly } :
33
- null;
34
- await this.$validate(validateOptions).then(
35
- () => {
36
- this.$op = 'save';
37
- }
38
- );
39
- }
40
- }, null, unshift);
41
- };
35
+ ('validateModifiedOnly' in options);
36
+ const validateOptions = hasValidateModifiedOnlyOption ?
37
+ { validateModifiedOnly: options.validateModifiedOnly } :
38
+ null;
39
+ await this.$validate(validateOptions).then(
40
+ () => {
41
+ this.$op = 'save';
42
+ }
43
+ );
44
+ }
45
+ }
46
+
47
+ validateBeforeSavePreSave[symbols.builtInMiddleware] = true;
package/lib/query.js CHANGED
@@ -33,6 +33,7 @@ const parseProjection = require('./helpers/projection/parseProjection');
33
33
  const removeUnusedArrayFilters = require('./helpers/update/removeUnusedArrayFilters');
34
34
  const sanitizeFilter = require('./helpers/query/sanitizeFilter');
35
35
  const sanitizeProjection = require('./helpers/query/sanitizeProjection');
36
+ const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
36
37
  const selectPopulatedFields = require('./helpers/query/selectPopulatedFields');
37
38
  const setDefaultsOnInsert = require('./helpers/setDefaultsOnInsert');
38
39
  const specialProperties = require('./helpers/specialProperties');
@@ -1623,7 +1624,7 @@ Query.prototype.wtimeout = function wtimeout(ms) {
1623
1624
  *
1624
1625
  * @memberOf Query
1625
1626
  * @method readConcern
1626
- * @param {String} level one of the listed read concern level or their aliases
1627
+ * @param {'local'|'available'|'majority'|'snapshot'|'linearizable'|'l'|'a'|'m'|'s'|'lz'} level one of the listed read concern level or their aliases
1627
1628
  * @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/
1628
1629
  * @return {Query} this
1629
1630
  * @api public
@@ -1697,6 +1698,7 @@ Query.prototype.getOptions = function() {
1697
1698
  * - [collation](https://www.mongodb.com/docs/manual/reference/collation/)
1698
1699
  * - [session](https://www.mongodb.com/docs/manual/reference/server-sessions/)
1699
1700
  * - [explain](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/)
1701
+ * - [middleware](https://mongoosejs.com/docs/middleware.html#skipping): set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks
1700
1702
  *
1701
1703
  * @param {Object} options
1702
1704
  * @return {Query} this
@@ -1826,7 +1828,7 @@ Query.prototype.setOptions = function(options, overwrite) {
1826
1828
  * const res = await query.find({ a: 1 }).explain('queryPlanner');
1827
1829
  * console.log(res);
1828
1830
  *
1829
- * @param {String} [verbose] The verbosity mode. Either 'queryPlanner', 'executionStats', or 'allPlansExecution'. The default is 'queryPlanner'
1831
+ * @param {'queryPlanner'|'executionStats'|'allPlansExecution'} [verbose] The verbosity mode. The default is 'queryPlanner'
1830
1832
  * @return {Query} this
1831
1833
  * @api public
1832
1834
  */
@@ -3404,13 +3406,14 @@ function prepareDiscriminatorCriteria(query) {
3404
3406
  * @param {Object} [update]
3405
3407
  * @param {Object} [options]
3406
3408
  * @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
3407
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3409
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3408
3410
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3409
3411
  * @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.
3410
- * @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.
3412
+ * @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. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`.
3411
3413
  * @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).
3412
3414
  * @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.
3413
- * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
3415
+ * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`.
3416
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
3414
3417
  * @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.
3415
3418
  * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3416
3419
  * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
@@ -3463,9 +3466,14 @@ Query.prototype.findOneAndUpdate = function(filter, update, options) {
3463
3466
  delete options.fields;
3464
3467
  }
3465
3468
 
3466
- const returnOriginal = this?.model?.base?.options?.returnOriginal;
3467
- if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
3468
- options.returnOriginal = returnOriginal;
3469
+ const globalReturnDocument = this?.model?.base?.options?.returnDocument;
3470
+ const globalReturnOriginal = this?.model?.base?.options?.returnOriginal;
3471
+ if (options.new == null && options.returnDocument == null && options.returnOriginal == null) {
3472
+ if (globalReturnDocument != null) {
3473
+ options.returnDocument = globalReturnDocument;
3474
+ } else if (globalReturnOriginal != null) {
3475
+ options.returnOriginal = globalReturnOriginal;
3476
+ }
3469
3477
  }
3470
3478
 
3471
3479
  const updatePipeline = this?.model?.base?.options?.updatePipeline;
@@ -3598,7 +3606,7 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
3598
3606
  * @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
3599
3607
  * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
3600
3608
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3601
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3609
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3602
3610
  * @return {Query} this
3603
3611
  * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
3604
3612
  * @api public
@@ -3692,13 +3700,14 @@ Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
3692
3700
  * @param {Object} [options]
3693
3701
  * @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
3694
3702
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3695
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3696
- * @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.
3703
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3704
+ * @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. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`.
3697
3705
  * @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).
3698
3706
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3699
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3707
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3700
3708
  * @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.
3701
- * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
3709
+ * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`.
3710
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
3702
3711
  * @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.
3703
3712
  * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
3704
3713
  * @return {Query} this
@@ -3730,9 +3739,14 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
3730
3739
 
3731
3740
  options = options || {};
3732
3741
 
3733
- const returnOriginal = this?.model?.base?.options?.returnOriginal;
3734
- if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
3735
- options.returnOriginal = returnOriginal;
3742
+ const globalReturnDocument = this?.model?.base?.options?.returnDocument;
3743
+ const globalReturnOriginal = this?.model?.base?.options?.returnOriginal;
3744
+ if (options.new == null && options.returnDocument == null && options.returnOriginal == null) {
3745
+ if (globalReturnDocument != null) {
3746
+ options.returnDocument = globalReturnDocument;
3747
+ } else if (globalReturnOriginal != null) {
3748
+ options.returnOriginal = globalReturnOriginal;
3749
+ }
3736
3750
  }
3737
3751
 
3738
3752
  this.setOptions(options);
@@ -3858,13 +3872,14 @@ Query.prototype.findById = function(id, projection, options) {
3858
3872
  * @param {Object} [doc]
3859
3873
  * @param {Object} [options]
3860
3874
  * @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
3861
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3875
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3862
3876
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3863
3877
  * @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.
3864
- * @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.
3878
+ * @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. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`.
3865
3879
  * @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).
3866
3880
  * @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.
3867
- * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
3881
+ * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`.
3882
+ * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied.
3868
3883
  * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
3869
3884
  * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
3870
3885
  * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
@@ -3895,7 +3910,7 @@ Query.prototype.findByIdAndUpdate = function(id, update, options) {
3895
3910
  * @param {Object} [options]
3896
3911
  * @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
3897
3912
  * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
3898
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3913
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
3899
3914
  * @return {Query} this
3900
3915
  * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
3901
3916
  * @api public
@@ -3913,10 +3928,20 @@ Query.prototype.findByIdAndDelete = function(id, options) {
3913
3928
 
3914
3929
  function convertNewToReturnDocument(options) {
3915
3930
  if ('new' in options) {
3931
+ const replacement = options['new'] ? '\'after\'' : '\'before\'';
3932
+ utils.warn(
3933
+ 'mongoose: the `new` option for `findOneAndUpdate()` and `findOneAndReplace()` is deprecated. ' +
3934
+ 'Use `returnDocument: ' + replacement + '` instead.'
3935
+ );
3916
3936
  options.returnDocument = options['new'] ? 'after' : 'before';
3917
3937
  delete options['new'];
3918
3938
  }
3919
3939
  if ('returnOriginal' in options) {
3940
+ const replacement = options['returnOriginal'] ? '\'before\'' : '\'after\'';
3941
+ utils.warn(
3942
+ 'mongoose: the `returnOriginal` option for `findOneAndUpdate()` and `findOneAndReplace()` is deprecated. ' +
3943
+ 'Use `returnDocument: ' + replacement + '` instead.'
3944
+ );
3920
3945
  options.returnDocument = options['returnOriginal'] ? 'before' : 'after';
3921
3946
  delete options['returnOriginal'];
3922
3947
  }
@@ -4222,7 +4247,7 @@ Query.prototype._replaceOne = async function _replaceOne() {
4222
4247
  * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
4223
4248
  * @param {Object} [options]
4224
4249
  * @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.
4225
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4250
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4226
4251
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4227
4252
  * @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)
4228
4253
  * @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.
@@ -4297,7 +4322,7 @@ Query.prototype.updateMany = function(conditions, doc, options, callback) {
4297
4322
  * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
4298
4323
  * @param {Object} [options]
4299
4324
  * @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.
4300
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4325
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4301
4326
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4302
4327
  * @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)
4303
4328
  * @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.
@@ -4366,7 +4391,7 @@ Query.prototype.updateOne = function(conditions, doc, options, callback) {
4366
4391
  * @param {Object} [doc] the update command
4367
4392
  * @param {Object} [options]
4368
4393
  * @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.
4369
- * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4394
+ * @param {Boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
4370
4395
  * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
4371
4396
  * @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)
4372
4397
  * @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.
@@ -4685,19 +4710,27 @@ Query.prototype.exec = async function exec(op) {
4685
4710
  * ignore
4686
4711
  */
4687
4712
 
4688
- function _executePostHooks(query, res, error, op) {
4713
+ async function _executePostHooks(query, res, error, op) {
4689
4714
  if (query._queryMiddleware == null) {
4690
4715
  if (error != null) {
4691
4716
  throw error;
4692
4717
  }
4693
4718
  return res;
4694
4719
  }
4720
+ const filter = buildMiddlewareFilter(query.options, 'post');
4721
+ const opts = { filter };
4722
+ if (error) {
4723
+ opts.error = error;
4724
+ }
4695
4725
 
4696
- const opts = error ? { error } : {};
4697
- return query._queryMiddleware.execPost(op || query.op, query, [res], opts).then((res) => {
4698
- // `res` is array of return args, but queries only return one result.
4699
- return res[0];
4700
- });
4726
+ // `result` is array of return args, but queries only return one result.
4727
+ const [result] = await query._queryMiddleware.execPost(
4728
+ op || query.op,
4729
+ query,
4730
+ [res],
4731
+ opts
4732
+ );
4733
+ return result;
4701
4734
  }
4702
4735
 
4703
4736
  /*!
@@ -4708,8 +4741,13 @@ function _executePreHooks(query, op) {
4708
4741
  if (query._queryMiddleware == null) {
4709
4742
  return;
4710
4743
  }
4711
-
4712
- return query._queryMiddleware.execPre(op || query.op, query, []);
4744
+ const filter = buildMiddlewareFilter(query.options, 'pre');
4745
+ return query._queryMiddleware.execPre(
4746
+ op || query.op,
4747
+ query,
4748
+ [],
4749
+ { filter }
4750
+ );
4713
4751
  }
4714
4752
 
4715
4753
  /**
@@ -90,7 +90,8 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
90
90
  if (discriminator) {
91
91
  const _fields = clone(userProvidedFields);
92
92
  exports.applyPaths(_fields, discriminator.schema);
93
- return new discriminator(undefined, _fields, { skipId: true });
93
+ const _opts = { strict: options?.strict };
94
+ return new discriminator(undefined, _fields, _opts);
94
95
  }
95
96
  }
96
97
 
@@ -113,9 +114,13 @@ exports.createModel = function createModel(model, doc, fields, userProvidedField
113
114
  */
114
115
 
115
116
  exports.createModelAndInit = function createModelAndInit(model, doc, fields, userProvidedFields, options, populatedIds, callback) {
116
- const initOpts = populatedIds ?
117
- { populated: populatedIds } :
118
- undefined;
117
+ const initOpts = {};
118
+ if (populatedIds) {
119
+ initOpts.populated = populatedIds;
120
+ }
121
+ if (options?.middleware != null) {
122
+ initOpts.middleware = options.middleware;
123
+ }
119
124
 
120
125
  const casted = exports.createModel(model, doc, fields, userProvidedFields, options);
121
126
  try {
package/lib/schema.js CHANGED
@@ -604,12 +604,12 @@ Schema.prototype.omit = function(paths, options) {
604
604
  Schema.prototype.defaultOptions = function(options) {
605
605
  this._userProvidedOptions = options == null ? {} : clone(options);
606
606
  const baseOptions = this.base?.options || {};
607
- const strict = 'strict' in baseOptions ? baseOptions.strict : true;
608
- const strictQuery = 'strictQuery' in baseOptions ? baseOptions.strictQuery : false;
609
- const id = 'id' in baseOptions ? baseOptions.id : true;
607
+ const defaultStrict = baseOptions.strict ?? true;
608
+ const defaultStrictQuery = baseOptions.strictQuery ?? false;
609
+ const defaultId = baseOptions.id ?? true;
610
610
  options = {
611
- strict,
612
- strictQuery,
611
+ strict: defaultStrict,
612
+ strictQuery: defaultStrictQuery,
613
613
  bufferCommands: true,
614
614
  capped: false, // { size, max, autoIndexId }
615
615
  versionKey: '__v',
@@ -623,11 +623,16 @@ Schema.prototype.defaultOptions = function(options) {
623
623
  validateModifiedOnly: false,
624
624
  // the following are only applied at construction time
625
625
  _id: true,
626
- id: id,
626
+ id: defaultId,
627
627
  typeKey: 'type',
628
628
  ...options
629
629
  };
630
630
 
631
+ // Treat `undefined` as "not provided" for these options
632
+ options.strict ??= defaultStrict;
633
+ options.strictQuery ??= defaultStrictQuery;
634
+ options.id ??= defaultId;
635
+
631
636
  if (options.versionKey && typeof options.versionKey !== 'string') {
632
637
  throw new MongooseError('`versionKey` must be falsy or string, got `' + (typeof options.versionKey) + '`');
633
638
  }
@@ -2344,12 +2349,6 @@ Schema.prototype.index = function(fields, options) {
2344
2349
  }
2345
2350
  }
2346
2351
 
2347
- for (const existingIndex of this.indexes()) {
2348
- if (options.name == null && existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) {
2349
- utils.warn(`Duplicate schema index on ${JSON.stringify(fields)} found. This is often due to declaring an index using both "index: true" and "schema.index()". Please remove the duplicate index definition.`);
2350
- }
2351
- }
2352
-
2353
2352
  this._indexes.push([fields, options]);
2354
2353
  return this;
2355
2354
  };
@@ -60,7 +60,11 @@ Subdocument.prototype.toBSON = function() {
60
60
  *
61
61
  * _This is a no-op. Does not actually save the doc to the db._
62
62
  *
63
- * @param {Function} [fn]
63
+ * @param {Object} [options]
64
+ * @param {Boolean} [options.suppressWarning=false] If `true`, suppress the warning about calling `save()` on a subdoc.
65
+ * @param {Boolean|Object} [options.middleware=true] set to `false` to skip all user-defined middleware
66
+ * @param {Boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks
67
+ * @param {Boolean} [options.middleware.post=true] set to `false` to skip only post hooks
64
68
  * @return {Promise} resolved Promise
65
69
  * @api private
66
70
  */
@@ -75,7 +79,7 @@ Subdocument.prototype.save = async function save(options) {
75
79
  'if you\'re sure this behavior is right for your app.');
76
80
  }
77
81
 
78
- return await this.$__save();
82
+ return await this.$__save(options);
79
83
  };
80
84
 
81
85
  /**
@@ -132,15 +136,15 @@ Subdocument.prototype.$__pathRelativeToParent = function(p) {
132
136
  * @api private
133
137
  */
134
138
 
135
- Subdocument.prototype.$__save = async function $__save() {
139
+ Subdocument.prototype.$__save = async function $__save(options) {
136
140
  try {
137
- await this._execDocumentPreHooks('save');
141
+ await this._execDocumentPreHooks('save', options, [options]);
138
142
  } catch (error) {
139
- await this._execDocumentPostHooks('save', error);
143
+ await this._execDocumentPostHooks('save', options, error);
140
144
  return;
141
145
  }
142
146
 
143
- await this._execDocumentPostHooks('save');
147
+ await this._execDocumentPostHooks('save', options);
144
148
  };
145
149
 
146
150
  /*!
@@ -22,6 +22,7 @@ const VALID_OPTIONS = Object.freeze([
22
22
  'maxTimeMS',
23
23
  'objectIdGetter',
24
24
  'overwriteModels',
25
+ 'returnDocument',
25
26
  'returnOriginal',
26
27
  'runValidators',
27
28
  'sanitizeFilter',