mongoose 8.8.2 → 8.8.4

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
@@ -2711,7 +2711,7 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate
2711
2711
 
2712
2712
  if (!isNestedValidate) {
2713
2713
  // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments
2714
- const subdocs = doc.$getAllSubdocs();
2714
+ const subdocs = doc.$getAllSubdocs({ useCache: true });
2715
2715
  const modifiedPaths = doc.modifiedPaths();
2716
2716
  for (const subdoc of subdocs) {
2717
2717
  if (subdoc.$basePath) {
@@ -3482,7 +3482,7 @@ Document.prototype.$__reset = function reset() {
3482
3482
  let _this = this;
3483
3483
 
3484
3484
  // Skip for subdocuments
3485
- const subdocs = !this.$isSubdocument ? this.$getAllSubdocs() : null;
3485
+ const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null;
3486
3486
  if (subdocs && subdocs.length > 0) {
3487
3487
  for (const subdoc of subdocs) {
3488
3488
  subdoc.$__reset();
@@ -3672,6 +3672,7 @@ Document.prototype.$__getArrayPathsToValidate = function() {
3672
3672
  /**
3673
3673
  * Get all subdocs (by bfs)
3674
3674
  *
3675
+ * @param {Object} [options] options. Currently for internal use.
3675
3676
  * @return {Array}
3676
3677
  * @api public
3677
3678
  * @method $getAllSubdocs
@@ -3679,57 +3680,50 @@ Document.prototype.$__getArrayPathsToValidate = function() {
3679
3680
  * @instance
3680
3681
  */
3681
3682
 
3682
- Document.prototype.$getAllSubdocs = function() {
3683
+ Document.prototype.$getAllSubdocs = function(options) {
3684
+ if (options?.useCache && this.$__.saveOptions?.__subdocs) {
3685
+ return this.$__.saveOptions.__subdocs;
3686
+ }
3687
+
3683
3688
  DocumentArray || (DocumentArray = require('./types/documentArray'));
3684
3689
  Embedded = Embedded || require('./types/arraySubdocument');
3685
3690
 
3686
- function docReducer(doc, seed, path) {
3687
- let val = doc;
3688
- let isNested = false;
3689
- if (path) {
3690
- if (doc instanceof Document && doc[documentSchemaSymbol].paths[path]) {
3691
- val = doc._doc[path];
3692
- } else if (doc instanceof Document && doc[documentSchemaSymbol].nested[path]) {
3693
- val = doc._doc[path];
3694
- isNested = true;
3695
- } else {
3696
- val = doc[path];
3691
+ const subDocs = [];
3692
+ function getSubdocs(doc) {
3693
+ const newSubdocs = [];
3694
+ for (const { path } of doc.$__schema.childSchemas) {
3695
+ const val = doc.$__getValue(path);
3696
+ if (val == null) {
3697
+ continue;
3697
3698
  }
3698
- }
3699
- if (val instanceof Embedded) {
3700
- seed.push(val);
3701
- } else if (val instanceof Map) {
3702
- seed = Array.from(val.keys()).reduce(function(seed, path) {
3703
- return docReducer(val.get(path), seed, null);
3704
- }, seed);
3705
- } else if (val && !Array.isArray(val) && val.$isSingleNested) {
3706
- seed = Object.keys(val._doc).reduce(function(seed, path) {
3707
- return docReducer(val, seed, path);
3708
- }, seed);
3709
- seed.push(val);
3710
- } else if (val && utils.isMongooseDocumentArray(val)) {
3711
- val.forEach(function _docReduce(doc) {
3712
- if (!doc || !doc._doc) {
3713
- return;
3699
+ if (val.$__) {
3700
+ newSubdocs.push(val);
3701
+ }
3702
+ if (Array.isArray(val)) {
3703
+ for (const el of val) {
3704
+ if (el != null && el.$__) {
3705
+ newSubdocs.push(el);
3706
+ }
3714
3707
  }
3715
- seed = Object.keys(doc._doc).reduce(function(seed, path) {
3716
- return docReducer(doc._doc, seed, path);
3717
- }, seed);
3718
- if (doc instanceof Embedded) {
3719
- seed.push(doc);
3708
+ }
3709
+ if (val instanceof Map) {
3710
+ for (const el of val.values()) {
3711
+ if (el != null && el.$__) {
3712
+ newSubdocs.push(el);
3713
+ }
3720
3714
  }
3721
- });
3722
- } else if (isNested && val != null) {
3723
- for (const path of Object.keys(val)) {
3724
- docReducer(val, seed, path);
3725
3715
  }
3726
3716
  }
3727
- return seed;
3717
+ for (const subdoc of newSubdocs) {
3718
+ getSubdocs(subdoc);
3719
+ }
3720
+ subDocs.push(...newSubdocs);
3728
3721
  }
3729
3722
 
3730
- const subDocs = [];
3731
- for (const path of Object.keys(this._doc)) {
3732
- docReducer(this, subDocs, path);
3723
+ getSubdocs(this);
3724
+
3725
+ if (this.$__.saveOptions) {
3726
+ this.$__.saveOptions.__subdocs = subDocs;
3733
3727
  }
3734
3728
 
3735
3729
  return subDocs;
@@ -104,7 +104,8 @@ module.exports = function castBulkWrite(originalModel, op, options) {
104
104
  op['updateOne']['update'] = castUpdate(model.schema, update, {
105
105
  strict: strict,
106
106
  upsert: op['updateOne'].upsert,
107
- arrayFilters: op['updateOne'].arrayFilters
107
+ arrayFilters: op['updateOne'].arrayFilters,
108
+ overwriteDiscriminatorKey: op['updateOne'].overwriteDiscriminatorKey
108
109
  }, model, op['updateOne']['filter']);
109
110
  } catch (error) {
110
111
  return callback(error, null);
@@ -164,7 +165,8 @@ module.exports = function castBulkWrite(originalModel, op, options) {
164
165
  op['updateMany']['update'] = castUpdate(model.schema, op['updateMany']['update'], {
165
166
  strict: strict,
166
167
  upsert: op['updateMany'].upsert,
167
- arrayFilters: op['updateMany'].arrayFilters
168
+ arrayFilters: op['updateMany'].arrayFilters,
169
+ overwriteDiscriminatorKey: op['updateMany'].overwriteDiscriminatorKey
168
170
  }, model, op['updateMany']['filter']);
169
171
  } catch (error) {
170
172
  return callback(error, null);
@@ -249,7 +249,7 @@ function numDocs(v) {
249
249
 
250
250
  function valueFilter(val, assignmentOpts, populateOptions, allIds) {
251
251
  const userSpecifiedTransform = typeof populateOptions.transform === 'function';
252
- const transform = userSpecifiedTransform ? populateOptions.transform : noop;
252
+ const transform = userSpecifiedTransform ? populateOptions.transform : v => v;
253
253
  if (Array.isArray(val)) {
254
254
  // find logic
255
255
  const ret = [];
@@ -341,7 +341,3 @@ function isPopulatedObject(obj) {
341
341
  obj.$__ != null ||
342
342
  leanPopulateMap.has(obj);
343
343
  }
344
-
345
- function noop(v) {
346
- return v;
347
- }
@@ -184,6 +184,15 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
184
184
  if (hasMatchFunction) {
185
185
  match = match.call(doc, doc);
186
186
  }
187
+ if (Array.isArray(match)) {
188
+ for (const item of match) {
189
+ if (item != null && item.$where) {
190
+ throw new MongooseError('Cannot use $where filter with populate() match');
191
+ }
192
+ }
193
+ } else if (match != null && match.$where != null) {
194
+ throw new MongooseError('Cannot use $where filter with populate() match');
195
+ }
187
196
  data.match = match;
188
197
  data.hasMatchFunction = hasMatchFunction;
189
198
  data.isRefPath = isRefPath;
@@ -447,6 +456,16 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
447
456
  data.match = match;
448
457
  data.hasMatchFunction = hasMatchFunction;
449
458
 
459
+ if (Array.isArray(match)) {
460
+ for (const item of match) {
461
+ if (item != null && item.$where) {
462
+ throw new MongooseError('Cannot use $where filter with populate() match');
463
+ }
464
+ }
465
+ } else if (match != null && match.$where != null) {
466
+ throw new MongooseError('Cannot use $where filter with populate() match');
467
+ }
468
+
450
469
  // Get local fields
451
470
  const ret = _getLocalFieldValues(doc, localField, model, options, virtual);
452
471
 
@@ -8,6 +8,7 @@ const ValidationError = require('../../error/validation');
8
8
  const castNumber = require('../../cast/number');
9
9
  const cast = require('../../cast');
10
10
  const getConstructorName = require('../getConstructorName');
11
+ const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
11
12
  const getEmbeddedDiscriminatorPath = require('./getEmbeddedDiscriminatorPath');
12
13
  const handleImmutable = require('./handleImmutable');
13
14
  const moveImmutableProperties = require('../update/moveImmutableProperties');
@@ -62,6 +63,27 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
62
63
  return obj;
63
64
  }
64
65
 
66
+ if (schema != null &&
67
+ filter != null &&
68
+ utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) &&
69
+ typeof filter[schema.options.discriminatorKey] !== 'object' &&
70
+ schema.discriminators != null) {
71
+ const discriminatorValue = filter[schema.options.discriminatorKey];
72
+ const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue);
73
+ schema = schema.discriminators[discriminatorValue] ||
74
+ (byValue && byValue.schema) ||
75
+ schema;
76
+ } else if (schema != null &&
77
+ options.overwriteDiscriminatorKey &&
78
+ utils.hasUserDefinedProperty(obj, schema.options.discriminatorKey) &&
79
+ schema.discriminators != null) {
80
+ const discriminatorValue = obj[schema.options.discriminatorKey];
81
+ const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue);
82
+ schema = schema.discriminators[discriminatorValue] ||
83
+ (byValue && byValue.schema) ||
84
+ schema;
85
+ }
86
+
65
87
  if (options.upsert) {
66
88
  moveImmutableProperties(schema, obj, context);
67
89
  }
@@ -28,7 +28,8 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
28
28
  const updatedPathsByFilter = updatedPathsByArrayFilter(update);
29
29
 
30
30
  for (let i = 0; i < parts.length; ++i) {
31
- const subpath = cleanPositionalOperators(parts.slice(0, i + 1).join('.'));
31
+ const originalSubpath = parts.slice(0, i + 1).join('.');
32
+ const subpath = cleanPositionalOperators(originalSubpath);
32
33
  schematype = schema.path(subpath);
33
34
  if (schematype == null) {
34
35
  continue;
@@ -56,6 +57,11 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
56
57
  discriminatorKey = filter[wrapperPath].$elemMatch[key];
57
58
  }
58
59
 
60
+ const discriminatorKeyUpdatePath = originalSubpath + '.' + key;
61
+ if (discriminatorKeyUpdatePath in update) {
62
+ discriminatorKey = update[discriminatorKeyUpdatePath];
63
+ }
64
+
59
65
  if (discriminatorValuePath in update) {
60
66
  discriminatorKey = update[discriminatorValuePath];
61
67
  }
package/lib/model.js CHANGED
@@ -3146,7 +3146,7 @@ function _setIsNew(doc, val) {
3146
3146
  doc.$emit('isNew', val);
3147
3147
  doc.constructor.emit('isNew', val);
3148
3148
 
3149
- const subdocs = doc.$getAllSubdocs();
3149
+ const subdocs = doc.$getAllSubdocs({ useCache: true });
3150
3150
  for (const subdoc of subdocs) {
3151
3151
  subdoc.$isNew = val;
3152
3152
  subdoc.$emit('isNew', val);
@@ -11,4 +11,6 @@ class SaveOptions {
11
11
  }
12
12
  }
13
13
 
14
+ SaveOptions.prototype.__subdocs = null;
15
+
14
16
  module.exports = SaveOptions;
@@ -15,7 +15,7 @@ module.exports = function saveSubdocs(schema) {
15
15
  }
16
16
 
17
17
  const _this = this;
18
- const subdocs = this.$getAllSubdocs();
18
+ const subdocs = this.$getAllSubdocs({ useCache: true });
19
19
 
20
20
  if (!subdocs.length) {
21
21
  next();
@@ -27,6 +27,10 @@ module.exports = function saveSubdocs(schema) {
27
27
  cb(err);
28
28
  });
29
29
  }, function(error) {
30
+ // Invalidate subdocs cache because subdoc pre hooks can add new subdocuments
31
+ if (_this.$__.saveOptions) {
32
+ _this.$__.saveOptions.__subdocs = null;
33
+ }
30
34
  if (error) {
31
35
  return _this.$__schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
32
36
  next(error);
@@ -64,7 +68,7 @@ module.exports = function saveSubdocs(schema) {
64
68
  }
65
69
 
66
70
  const _this = this;
67
- const subdocs = this.$getAllSubdocs();
71
+ const subdocs = this.$getAllSubdocs({ useCache: true });
68
72
 
69
73
  if (!subdocs.length) {
70
74
  return;
package/lib/query.js CHANGED
@@ -4700,18 +4700,6 @@ Query.prototype._castUpdate = function _castUpdate(obj) {
4700
4700
  upsert = this.options.upsert;
4701
4701
  }
4702
4702
 
4703
- const filter = this._conditions;
4704
- if (schema != null &&
4705
- utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) &&
4706
- typeof filter[schema.options.discriminatorKey] !== 'object' &&
4707
- schema.discriminators != null) {
4708
- const discriminatorValue = filter[schema.options.discriminatorKey];
4709
- const byValue = getDiscriminatorByValue(this.model.discriminators, discriminatorValue);
4710
- schema = schema.discriminators[discriminatorValue] ||
4711
- (byValue && byValue.schema) ||
4712
- schema;
4713
- }
4714
-
4715
4703
  return castUpdate(schema, obj, {
4716
4704
  strict: this._mongooseOptions.strict,
4717
4705
  upsert: upsert,
package/lib/schema.js CHANGED
@@ -1126,6 +1126,13 @@ Schema.prototype.path = function(path, obj) {
1126
1126
 
1127
1127
  this.paths[mapPath] = schemaType.$__schemaType;
1128
1128
  this.mapPaths.push(this.paths[mapPath]);
1129
+ if (schemaType.$__schemaType.$isSingleNested) {
1130
+ this.childSchemas.push({
1131
+ schema: schemaType.$__schemaType.schema,
1132
+ model: schemaType.$__schemaType.caster,
1133
+ path: path
1134
+ });
1135
+ }
1129
1136
  }
1130
1137
 
1131
1138
  if (schemaType.$isSingleNested) {
@@ -1154,7 +1161,8 @@ Schema.prototype.path = function(path, obj) {
1154
1161
  schemaType.caster.base = this.base;
1155
1162
  this.childSchemas.push({
1156
1163
  schema: schemaType.schema,
1157
- model: schemaType.caster
1164
+ model: schemaType.caster,
1165
+ path: path
1158
1166
  });
1159
1167
  } else if (schemaType.$isMongooseDocumentArray) {
1160
1168
  Object.defineProperty(schemaType.schema, 'base', {
@@ -1167,7 +1175,8 @@ Schema.prototype.path = function(path, obj) {
1167
1175
  schemaType.casterConstructor.base = this.base;
1168
1176
  this.childSchemas.push({
1169
1177
  schema: schemaType.schema,
1170
- model: schemaType.casterConstructor
1178
+ model: schemaType.casterConstructor,
1179
+ path: path
1171
1180
  });
1172
1181
  }
1173
1182
 
@@ -1235,7 +1244,9 @@ function gatherChildSchemas(schema) {
1235
1244
  for (const path of Object.keys(schema.paths)) {
1236
1245
  const schematype = schema.paths[path];
1237
1246
  if (schematype.$isMongooseDocumentArray || schematype.$isSingleNested) {
1238
- childSchemas.push({ schema: schematype.schema, model: schematype.caster });
1247
+ childSchemas.push({ schema: schematype.schema, model: schematype.caster, path: path });
1248
+ } else if (schematype.$isSchemaMap && schematype.$__schemaType.$isSingleNested) {
1249
+ childSchemas.push({ schema: schematype.$__schemaType.schema, model: schematype.$__schemaType.caster, path: path });
1239
1250
  }
1240
1251
  }
1241
1252
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.8.2",
4
+ "version": "8.8.4",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -52,8 +52,8 @@
52
52
  "highlight.js": "11.10.0",
53
53
  "lodash.isequal": "4.5.0",
54
54
  "lodash.isequalwith": "4.4.0",
55
- "markdownlint-cli2": "^0.14.0",
56
- "marked": "14.1.3",
55
+ "markdownlint-cli2": "^0.15.0",
56
+ "marked": "15.0.3",
57
57
  "mkdirp": "^3.0.1",
58
58
  "mocha": "10.8.2",
59
59
  "moment": "2.30.1",
@@ -65,8 +65,8 @@
65
65
  "sinon": "19.0.2",
66
66
  "stream-browserify": "3.0.0",
67
67
  "tsd": "0.31.2",
68
- "typescript": "5.6.3",
69
- "uuid": "11.0.2",
68
+ "typescript": "5.7.2",
69
+ "uuid": "11.0.3",
70
70
  "webpack": "5.96.1"
71
71
  },
72
72
  "directories": {