mongoose 8.2.4 → 8.3.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.
package/lib/document.js CHANGED
@@ -2756,47 +2756,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2756
2756
 
2757
2757
  // gh-661: if a whole array is modified, make sure to run validation on all
2758
2758
  // the children as well
2759
- for (const path of paths) {
2760
- const _pathType = doc.$__schema.path(path);
2761
- if (!_pathType) {
2762
- continue;
2763
- }
2764
-
2765
- if (!_pathType.$isMongooseArray ||
2766
- // To avoid potential performance issues, skip doc arrays whose children
2767
- // are not required. `getPositionalPathType()` may be slow, so avoid
2768
- // it unless we have a case of #6364
2769
- (!Array.isArray(_pathType) &&
2770
- _pathType.$isMongooseDocumentArray &&
2771
- !(_pathType && _pathType.schemaOptions && _pathType.schemaOptions.required))) {
2772
- continue;
2773
- }
2774
-
2775
- // gh-11380: optimization. If the array isn't a document array and there's no validators
2776
- // on the array type, there's no need to run validation on the individual array elements.
2777
- if (_pathType.$isMongooseArray &&
2778
- !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2779
- !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2780
- _pathType.$embeddedSchemaType.validators.length === 0) {
2781
- continue;
2782
- }
2783
-
2784
- const val = doc.$__getValue(path);
2785
- _pushNestedArrayPaths(val, paths, path);
2786
- }
2787
-
2788
- function _pushNestedArrayPaths(val, paths, path) {
2789
- if (val != null) {
2790
- const numElements = val.length;
2791
- for (let j = 0; j < numElements; ++j) {
2792
- if (Array.isArray(val[j])) {
2793
- _pushNestedArrayPaths(val[j], paths, path + '.' + j);
2794
- } else {
2795
- paths.add(path + '.' + j);
2796
- }
2797
- }
2798
- }
2799
- }
2759
+ _addArrayPathsToValidate(doc, paths);
2800
2760
 
2801
2761
  const flattenOptions = { skipArrays: true };
2802
2762
  for (const pathToCheck of paths) {
@@ -2841,12 +2801,58 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) {
2841
2801
  return [paths, doValidateOptions];
2842
2802
  }
2843
2803
 
2804
+ function _addArrayPathsToValidate(doc, paths) {
2805
+ for (const path of paths) {
2806
+ const _pathType = doc.$__schema.path(path);
2807
+ if (!_pathType) {
2808
+ continue;
2809
+ }
2810
+
2811
+ if (!_pathType.$isMongooseArray ||
2812
+ // To avoid potential performance issues, skip doc arrays whose children
2813
+ // are not required. `getPositionalPathType()` may be slow, so avoid
2814
+ // it unless we have a case of #6364
2815
+ (!Array.isArray(_pathType) &&
2816
+ _pathType.$isMongooseDocumentArray &&
2817
+ !(_pathType && _pathType.schemaOptions && _pathType.schemaOptions.required))) {
2818
+ continue;
2819
+ }
2820
+
2821
+ // gh-11380: optimization. If the array isn't a document array and there's no validators
2822
+ // on the array type, there's no need to run validation on the individual array elements.
2823
+ if (_pathType.$isMongooseArray &&
2824
+ !_pathType.$isMongooseDocumentArray && // Skip document arrays...
2825
+ !_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
2826
+ _pathType.$embeddedSchemaType.validators.length === 0) {
2827
+ continue;
2828
+ }
2829
+
2830
+ const val = doc.$__getValue(path);
2831
+ _pushNestedArrayPaths(val, paths, path);
2832
+ }
2833
+ }
2834
+
2835
+ function _pushNestedArrayPaths(val, paths, path) {
2836
+ if (val != null) {
2837
+ const numElements = val.length;
2838
+ for (let j = 0; j < numElements; ++j) {
2839
+ if (Array.isArray(val[j])) {
2840
+ _pushNestedArrayPaths(val[j], paths, path + '.' + j);
2841
+ } else {
2842
+ paths.add(path + '.' + j);
2843
+ }
2844
+ }
2845
+ }
2846
+ }
2847
+
2844
2848
  /*!
2845
2849
  * ignore
2846
2850
  */
2847
2851
 
2848
2852
  Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2849
- if (typeof pathsToValidate === 'function') {
2853
+ if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) {
2854
+ pathsToValidate = [...this.$__.saveOptions.pathsToSave];
2855
+ } else if (typeof pathsToValidate === 'function') {
2850
2856
  callback = pathsToValidate;
2851
2857
  options = null;
2852
2858
  pathsToValidate = null;
@@ -2868,6 +2874,19 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2868
2874
  shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly;
2869
2875
  }
2870
2876
 
2877
+ const validateAllPaths = options && options.validateAllPaths;
2878
+ if (validateAllPaths) {
2879
+ if (pathsToSkip) {
2880
+ throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`');
2881
+ }
2882
+ if (pathsToValidate) {
2883
+ throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`');
2884
+ }
2885
+ if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) {
2886
+ throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`');
2887
+ }
2888
+ }
2889
+
2871
2890
  const _this = this;
2872
2891
  const _complete = () => {
2873
2892
  let validationError = this.$__.validationError;
@@ -2905,11 +2924,33 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2905
2924
  };
2906
2925
 
2907
2926
  // only validate required fields when necessary
2908
- const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip);
2909
- const paths = shouldValidateModifiedOnly ?
2910
- pathDetails[0].filter((path) => this.$isModified(path)) :
2911
- pathDetails[0];
2912
- const doValidateOptionsByPath = pathDetails[1];
2927
+ let paths;
2928
+ let doValidateOptionsByPath;
2929
+ if (validateAllPaths) {
2930
+ paths = new Set(Object.keys(this.$__schema.paths));
2931
+ // gh-661: if a whole array is modified, make sure to run validation on all
2932
+ // the children as well
2933
+ for (const path of paths) {
2934
+ const schemaType = this.$__schema.path(path);
2935
+ if (!schemaType || !schemaType.$isMongooseArray) {
2936
+ continue;
2937
+ }
2938
+ const val = this.$__getValue(path);
2939
+ if (!val) {
2940
+ continue;
2941
+ }
2942
+ _pushNestedArrayPaths(val, paths, path);
2943
+ }
2944
+ paths = [...paths];
2945
+ doValidateOptionsByPath = {};
2946
+ } else {
2947
+ const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip);
2948
+ paths = shouldValidateModifiedOnly ?
2949
+ pathDetails[0].filter((path) => this.$isModified(path)) :
2950
+ pathDetails[0];
2951
+ doValidateOptionsByPath = pathDetails[1];
2952
+ }
2953
+
2913
2954
  if (typeof pathsToValidate === 'string') {
2914
2955
  pathsToValidate = pathsToValidate.split(' ');
2915
2956
  }
@@ -2929,8 +2970,19 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2929
2970
  const validated = {};
2930
2971
  let total = 0;
2931
2972
 
2932
- for (const path of paths) {
2933
- validatePath(path);
2973
+ let pathsToSave = this.$__.saveOptions?.pathsToSave;
2974
+ if (Array.isArray(pathsToSave)) {
2975
+ pathsToSave = new Set(pathsToSave);
2976
+ for (const path of paths) {
2977
+ if (!pathsToSave.has(path)) {
2978
+ continue;
2979
+ }
2980
+ validatePath(path);
2981
+ }
2982
+ } else {
2983
+ for (const path of paths) {
2984
+ validatePath(path);
2985
+ }
2934
2986
  }
2935
2987
 
2936
2988
  function validatePath(path) {
@@ -2979,7 +3031,8 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
2979
3031
  const doValidateOptions = {
2980
3032
  ...doValidateOptionsByPath[path],
2981
3033
  path: path,
2982
- validateModifiedOnly: shouldValidateModifiedOnly
3034
+ validateModifiedOnly: shouldValidateModifiedOnly,
3035
+ validateAllPaths
2983
3036
  };
2984
3037
 
2985
3038
  schemaType.doValidate(val, function(err) {
@@ -3097,6 +3150,16 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3097
3150
 
3098
3151
  let pathsToSkip = options && options.pathsToSkip;
3099
3152
 
3153
+ const validateAllPaths = options && options.validateAllPaths;
3154
+ if (validateAllPaths) {
3155
+ if (pathsToSkip) {
3156
+ throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`');
3157
+ }
3158
+ if (pathsToValidate) {
3159
+ throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`');
3160
+ }
3161
+ }
3162
+
3100
3163
  if (typeof pathsToValidate === 'string') {
3101
3164
  const isOnePathOnly = pathsToValidate.indexOf(' ') === -1;
3102
3165
  pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' ');
@@ -3105,11 +3168,32 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3105
3168
  }
3106
3169
 
3107
3170
  // only validate required fields when necessary
3108
- const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip);
3109
- const paths = shouldValidateModifiedOnly ?
3110
- pathDetails[0].filter((path) => this.$isModified(path)) :
3111
- pathDetails[0];
3112
- const skipSchemaValidators = pathDetails[1];
3171
+ let paths;
3172
+ let skipSchemaValidators;
3173
+ if (validateAllPaths) {
3174
+ paths = new Set(Object.keys(this.$__schema.paths));
3175
+ // gh-661: if a whole array is modified, make sure to run validation on all
3176
+ // the children as well
3177
+ for (const path of paths) {
3178
+ const schemaType = this.$__schema.path(path);
3179
+ if (!schemaType || !schemaType.$isMongooseArray) {
3180
+ continue;
3181
+ }
3182
+ const val = this.$__getValue(path);
3183
+ if (!val) {
3184
+ continue;
3185
+ }
3186
+ _pushNestedArrayPaths(val, paths, path);
3187
+ }
3188
+ paths = [...paths];
3189
+ skipSchemaValidators = {};
3190
+ } else {
3191
+ const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip);
3192
+ paths = shouldValidateModifiedOnly ?
3193
+ pathDetails[0].filter((path) => this.$isModified(path)) :
3194
+ pathDetails[0];
3195
+ skipSchemaValidators = pathDetails[1];
3196
+ }
3113
3197
 
3114
3198
  const validating = {};
3115
3199
 
@@ -3134,7 +3218,8 @@ Document.prototype.validateSync = function(pathsToValidate, options) {
3134
3218
  const err = p.doValidateSync(val, _this, {
3135
3219
  skipSchemaValidators: skipSchemaValidators[path],
3136
3220
  path: path,
3137
- validateModifiedOnly: shouldValidateModifiedOnly
3221
+ validateModifiedOnly: shouldValidateModifiedOnly,
3222
+ validateAllPaths
3138
3223
  });
3139
3224
  if (err) {
3140
3225
  const isSubdoc = p.$isSingleNested ||
@@ -2,7 +2,7 @@
2
2
 
3
3
  module.exports = applyEmbeddedDiscriminators;
4
4
 
5
- function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
5
+ function applyEmbeddedDiscriminators(schema, seen = new WeakSet(), overwriteExisting = false) {
6
6
  if (seen.has(schema)) {
7
7
  return;
8
8
  }
@@ -16,13 +16,17 @@ function applyEmbeddedDiscriminators(schema, seen = new WeakSet()) {
16
16
  if (!schemaType.schema._applyDiscriminators) {
17
17
  continue;
18
18
  }
19
- if (schemaType._appliedDiscriminators) {
19
+ if (schemaType._appliedDiscriminators && !overwriteExisting) {
20
20
  continue;
21
21
  }
22
22
  for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) {
23
23
  const discriminatorSchema = schemaType.schema._applyDiscriminators.get(discriminatorKey);
24
24
  applyEmbeddedDiscriminators(discriminatorSchema, seen);
25
- schemaType.discriminator(discriminatorKey, discriminatorSchema);
25
+ schemaType.discriminator(
26
+ discriminatorKey,
27
+ discriminatorSchema,
28
+ overwriteExisting ? { overwriteExisting: true } : null
29
+ );
26
30
  }
27
31
  schemaType._appliedDiscriminators = true;
28
32
  }
@@ -21,7 +21,7 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
21
21
  * ignore
22
22
  */
23
23
 
24
- module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks) {
24
+ module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks, overwriteExisting) {
25
25
  if (!(schema && schema.instanceOfSchema)) {
26
26
  throw new Error('You must pass a valid discriminator Schema');
27
27
  }
@@ -205,7 +205,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
205
205
 
206
206
  model.schema.discriminators[name] = schema;
207
207
 
208
- if (model.discriminators[name] && !schema.options.overwriteModels) {
208
+ if (model.discriminators[name] && !schema.options.overwriteModels && !overwriteExisting) {
209
209
  throw new Error('Discriminator with name "' + name + '" already exists');
210
210
  }
211
211
 
package/lib/model.js CHANGED
@@ -23,6 +23,7 @@ const VersionError = require('./error/version');
23
23
  const ParallelSaveError = require('./error/parallelSave');
24
24
  const applyDefaultsHelper = require('./helpers/document/applyDefaults');
25
25
  const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO');
26
+ const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators');
26
27
  const applyHooks = require('./helpers/model/applyHooks');
27
28
  const applyMethods = require('./helpers/model/applyMethods');
28
29
  const applyProjection = require('./helpers/projection/applyProjection');
@@ -298,7 +299,6 @@ Model.prototype.$__handleSave = function(options, callback) {
298
299
  if (!saveOptions.hasOwnProperty('session') && session != null) {
299
300
  saveOptions.session = session;
300
301
  }
301
-
302
302
  if (this.$isNew) {
303
303
  // send entire doc
304
304
  const obj = this.toObject(saveToObjectOptions);
@@ -335,6 +335,18 @@ Model.prototype.$__handleSave = function(options, callback) {
335
335
  // since it already exists
336
336
  this.$__.inserting = false;
337
337
  const delta = this.$__delta();
338
+
339
+ if (options.pathsToSave) {
340
+ for (const key in delta[1]['$set']) {
341
+ if (options.pathsToSave.includes(key)) {
342
+ continue;
343
+ } else if (options.pathsToSave.some(pathToSave => key.slice(0, pathToSave.length) === pathToSave && key.charAt(pathToSave.length) === '.')) {
344
+ continue;
345
+ } else {
346
+ delete delta[1]['$set'][key];
347
+ }
348
+ }
349
+ }
338
350
  if (delta) {
339
351
  if (delta instanceof MongooseError) {
340
352
  callback(delta);
@@ -364,7 +376,9 @@ Model.prototype.$__handleSave = function(options, callback) {
364
376
  }
365
377
  minimize(updateOp[key]);
366
378
  if (Object.keys(updateOp[key]).length === 0) {
367
- updateOp[key] = null;
379
+ delete updateOp[key];
380
+ update.$unset = update.$unset || {};
381
+ update.$unset[key] = 1;
368
382
  }
369
383
  }
370
384
  }
@@ -521,6 +535,7 @@ function generateVersionError(doc, modifiedPaths) {
521
535
  * @param {Number} [options.wtimeout] sets a [timeout for the write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern).
522
536
  * @param {Boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#mongodb-limit-Restrictions-on-Field-Names)
523
537
  * @param {Boolean} [options.timestamps=true] if `false` and [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this `save()`.
538
+ * @param {Array} [options.pathsToSave] An array of paths that tell mongoose to only validate and save the paths in `pathsToSave`.
524
539
  * @throws {DocumentNotFoundError} if this [save updates an existing document](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating).
525
540
  * @return {Promise}
526
541
  * @api public
@@ -747,7 +762,6 @@ function handleAtomics(self, where, delta, data, value) {
747
762
 
748
763
  Model.prototype.$__delta = function() {
749
764
  const dirty = this.$__dirty();
750
-
751
765
  const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
752
766
  if (optimisticConcurrency) {
753
767
  if (Array.isArray(optimisticConcurrency)) {
@@ -4882,6 +4896,34 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4882
4896
  return model;
4883
4897
  };
4884
4898
 
4899
+ /**
4900
+ * Update this model to use the new connection, including updating all internal
4901
+ * references and creating a new `Collection` instance using the new connection.
4902
+ * Not for external use, only used by `setDriver()` to ensure that you can still
4903
+ * call `setDriver()` after creating a model using `mongoose.model()`.
4904
+ *
4905
+ * @param {Connection} newConnection the new connection to use
4906
+ * @api private
4907
+ */
4908
+
4909
+ Model.$__updateConnection = function $__updateConnection(newConnection) {
4910
+ this.db = newConnection;
4911
+ this.prototype.db = newConnection;
4912
+ this.prototype[modelDbSymbol] = newConnection;
4913
+
4914
+ const collection = newConnection.collection(
4915
+ this.collection.collectionName,
4916
+ this.collection.opts
4917
+ );
4918
+
4919
+ this.prototype.collection = collection;
4920
+ this.prototype.$collection = collection;
4921
+ this.prototype[modelCollectionSymbol] = collection;
4922
+
4923
+ this.collection = collection;
4924
+ this.$__collection = collection;
4925
+ };
4926
+
4885
4927
  /**
4886
4928
  * Register custom query methods for this model
4887
4929
  *
@@ -4990,6 +5032,14 @@ Model.__subclass = function subclass(conn, schema, collection) {
4990
5032
 
4991
5033
  Model.recompileSchema = function recompileSchema() {
4992
5034
  this.prototype.$__setSchema(this.schema);
5035
+
5036
+ if (this.schema._applyDiscriminators != null) {
5037
+ for (const disc of this.schema._applyDiscriminators.keys()) {
5038
+ this.discriminator(disc, this.schema._applyDiscriminators.get(disc));
5039
+ }
5040
+ }
5041
+
5042
+ applyEmbeddedDiscriminators(this.schema, new WeakSet(), true);
4993
5043
  };
4994
5044
 
4995
5045
  /**
package/lib/mongoose.js CHANGED
@@ -166,9 +166,19 @@ Mongoose.prototype.setDriver = function setDriver(driver) {
166
166
  _mongoose.__driver = driver;
167
167
 
168
168
  const Connection = driver.Connection;
169
+ const oldDefaultConnection = _mongoose.connections[0];
169
170
  _mongoose.connections = [new Connection(_mongoose)];
170
171
  _mongoose.connections[0].models = _mongoose.models;
171
172
 
173
+ // Update all models that pointed to the old default connection to
174
+ // the new default connection, including collections
175
+ for (const model of Object.values(_mongoose.models)) {
176
+ if (model.db !== oldDefaultConnection) {
177
+ continue;
178
+ }
179
+ model.$__updateConnection(_mongoose.connections[0]);
180
+ }
181
+
172
182
  return _mongoose;
173
183
  };
174
184
 
package/lib/query.js CHANGED
@@ -2861,19 +2861,27 @@ Query.prototype.distinct = function(field, conditions) {
2861
2861
  * Cannot be used with `distinct()`
2862
2862
  *
2863
2863
  * @param {Object|String|Array<Array<(string | number)>>} arg
2864
+ * @param {Object} [options]
2865
+ * @param {Boolean} [options.override=false] If true, replace existing sort options with `arg`
2864
2866
  * @return {Query} this
2865
2867
  * @see cursor.sort https://www.mongodb.com/docs/manual/reference/method/cursor.sort/
2866
2868
  * @api public
2867
2869
  */
2868
2870
 
2869
- Query.prototype.sort = function(arg) {
2870
- if (arguments.length > 1) {
2871
- throw new Error('sort() only takes 1 Argument');
2871
+ Query.prototype.sort = function(arg, options) {
2872
+ if (arguments.length > 2) {
2873
+ throw new Error('sort() takes at most 2 arguments');
2874
+ }
2875
+ if (options != null && typeof options !== 'object') {
2876
+ throw new Error('sort() options argument must be an object or nullish');
2872
2877
  }
2873
2878
 
2874
2879
  if (this.options.sort == null) {
2875
2880
  this.options.sort = {};
2876
2881
  }
2882
+ if (options && options.override) {
2883
+ this.options.sort = {};
2884
+ }
2877
2885
  const sort = this.options.sort;
2878
2886
  if (typeof arg === 'string') {
2879
2887
  const properties = arg.indexOf(' ') === -1 ? [arg] : arg.split(' ');
@@ -279,7 +279,7 @@ SchemaDocumentArray.prototype.doValidate = function(array, fn, scope, options) {
279
279
  continue;
280
280
  }
281
281
 
282
- doc.$__validate(callback);
282
+ doc.$__validate(null, options, callback);
283
283
  }
284
284
  }
285
285
  };
@@ -330,7 +330,7 @@ SchemaDocumentArray.prototype.doValidateSync = function(array, scope, options) {
330
330
  continue;
331
331
  }
332
332
 
333
- const subdocValidateError = doc.validateSync();
333
+ const subdocValidateError = doc.validateSync(options);
334
334
 
335
335
  if (subdocValidateError && resultError == null) {
336
336
  resultError = subdocValidateError;
@@ -325,7 +325,7 @@ SchemaSubdocument.prototype.discriminator = function(name, schema, options) {
325
325
  schema = schema.clone();
326
326
  }
327
327
 
328
- schema = discriminator(this.caster, name, schema, value);
328
+ schema = discriminator(this.caster, name, schema, value, null, null, options.overwriteExisting);
329
329
 
330
330
  this.caster.discriminators[name] = _createConstructor(schema, this.caster);
331
331
 
package/lib/schema.js CHANGED
@@ -1300,6 +1300,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1300
1300
  return clone;
1301
1301
  }
1302
1302
 
1303
+
1303
1304
  // If this schema has an associated Mongoose object, use the Mongoose object's
1304
1305
  // copy of SchemaTypes re: gh-7158 gh-6933
1305
1306
  const MongooseTypes = this.base != null ? this.base.Schema.Types : Schema.Types;
@@ -1365,9 +1366,13 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1365
1366
  }
1366
1367
  return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast);
1367
1368
  }
1368
-
1369
- if (Array.isArray(cast)) {
1370
- return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj);
1369
+ if (typeof cast !== 'undefined') {
1370
+ if (Array.isArray(cast) || cast.type === Array || cast.type == 'Array') {
1371
+ if (cast && cast.type == 'Array') {
1372
+ cast.type = Array;
1373
+ }
1374
+ return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj);
1375
+ }
1371
1376
  }
1372
1377
 
1373
1378
  // Handle both `new Schema({ arr: [{ subpath: String }] })` and `new Schema({ arr: [{ type: { subpath: string } }] })`
@@ -1418,7 +1423,6 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
1418
1423
  type = cast[options.typeKey] && (options.typeKey !== 'type' || !cast.type.type)
1419
1424
  ? cast[options.typeKey]
1420
1425
  : cast;
1421
-
1422
1426
  if (Array.isArray(type)) {
1423
1427
  return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj);
1424
1428
  }
@@ -1948,6 +1952,7 @@ Schema.prototype.plugin = function(fn, opts) {
1948
1952
  'got "' + (typeof fn) + '"');
1949
1953
  }
1950
1954
 
1955
+
1951
1956
  if (opts && opts.deduplicate) {
1952
1957
  for (const plugin of this.plugins) {
1953
1958
  if (plugin.fn === fn) {
@@ -2751,7 +2756,7 @@ function isArrayFilter(piece) {
2751
2756
  */
2752
2757
 
2753
2758
  Schema.prototype._preCompile = function _preCompile() {
2754
- idGetter(this);
2759
+ this.plugin(idGetter, { deduplicate: true });
2755
2760
  };
2756
2761
 
2757
2762
  /*!
package/lib/schemaType.js CHANGED
@@ -803,6 +803,21 @@ SchemaType.prototype.get = function(fn) {
803
803
  return this;
804
804
  };
805
805
 
806
+ /**
807
+ * Adds multiple validators for this document path.
808
+ * Calls `validate()` for every element in validators.
809
+ *
810
+ * @param {Array<RegExp|Function|Object>} validators
811
+ * @returns this
812
+ */
813
+
814
+ SchemaType.prototype.validateAll = function(validators) {
815
+ for (let i = 0; i < validators.length; i++) {
816
+ this.validate(validators[i]);
817
+ }
818
+ return this;
819
+ };
820
+
806
821
  /**
807
822
  * Adds validator(s) for this document path.
808
823
  *
@@ -863,7 +878,7 @@ SchemaType.prototype.get = function(fn) {
863
878
  *
864
879
  * schema.path('name').validate({
865
880
  * validator: function() { throw new Error('Oops!'); },
866
- * // `errors['name']` will be "Oops!"
881
+ * // `errors['name'].message` will be "Oops!"
867
882
  * message: function(props) { return props.reason.message; }
868
883
  * });
869
884
  *
@@ -1285,6 +1300,9 @@ SchemaType.prototype.select = function select(val) {
1285
1300
  SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1286
1301
  let err = false;
1287
1302
  const path = this.path;
1303
+ if (typeof fn !== 'function') {
1304
+ throw new TypeError(`Must pass callback function to doValidate(), got ${typeof fn}`);
1305
+ }
1288
1306
 
1289
1307
  // Avoid non-object `validators`
1290
1308
  const validators = this.validators.
@@ -1419,7 +1437,6 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) {
1419
1437
  let i = 0;
1420
1438
  const len = validators.length;
1421
1439
  for (i = 0; i < len; ++i) {
1422
-
1423
1440
  const v = validators[i];
1424
1441
 
1425
1442
  if (v === null || typeof v !== 'object') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.2.4",
4
+ "version": "8.3.1",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -19,17 +19,17 @@
19
19
  ],
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "bson": "^6.2.0",
23
- "kareem": "2.5.1",
24
- "mongodb": "6.3.0",
22
+ "bson": "^6.5.0",
23
+ "kareem": "2.6.3",
24
+ "mongodb": "6.5.0",
25
25
  "mpath": "0.9.0",
26
26
  "mquery": "5.0.0",
27
27
  "ms": "2.1.3",
28
28
  "sift": "16.0.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@babel/core": "7.24.0",
32
- "@babel/preset-env": "7.24.0",
31
+ "@babel/core": "7.24.3",
32
+ "@babel/preset-env": "7.24.3",
33
33
  "@typescript-eslint/eslint-plugin": "^6.2.1",
34
34
  "@typescript-eslint/parser": "^6.2.1",
35
35
  "acquit": "1.3.0",
@@ -46,7 +46,7 @@
46
46
  "dotenv": "16.4.5",
47
47
  "dox": "1.0.0",
48
48
  "eslint": "8.57.0",
49
- "eslint-plugin-markdown": "^3.0.1",
49
+ "eslint-plugin-markdown": "^4.0.1",
50
50
  "eslint-plugin-mocha-no-only": "1.1.1",
51
51
  "express": "^4.18.1",
52
52
  "fs-extra": "~11.2.0",
@@ -56,7 +56,7 @@
56
56
  "markdownlint-cli2": "^0.12.1",
57
57
  "marked": "4.3.0",
58
58
  "mkdirp": "^3.0.1",
59
- "mocha": "10.3.0",
59
+ "mocha": "10.4.0",
60
60
  "moment": "2.x",
61
61
  "mongodb-memory-server": "8.15.1",
62
62
  "ncp": "^2.0.0",
@@ -65,10 +65,10 @@
65
65
  "q": "1.5.1",
66
66
  "sinon": "17.0.1",
67
67
  "stream-browserify": "3.0.0",
68
- "tsd": "0.30.7",
69
- "typescript": "5.3.3",
68
+ "tsd": "0.31.0",
69
+ "typescript": "5.4.3",
70
70
  "uuid": "9.0.1",
71
- "webpack": "5.90.3"
71
+ "webpack": "5.91.0"
72
72
  },
73
73
  "directories": {
74
74
  "lib": "./lib/mongoose"