mongoose 7.6.2 → 7.6.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/connection.js CHANGED
@@ -563,6 +563,10 @@ function _resetSessionDocuments(session) {
563
563
  doc.$__.activePaths.states.modify = {};
564
564
  }
565
565
  for (const path of state.modifiedPaths) {
566
+ const currentState = doc.$__.activePaths.paths[path];
567
+ if (currentState != null) {
568
+ delete doc.$__.activePaths[currentState][path];
569
+ }
566
570
  doc.$__.activePaths.paths[path] = 'modify';
567
571
  doc.$__.activePaths.states.modify[path] = true;
568
572
  }
package/lib/document.js CHANGED
@@ -53,6 +53,7 @@ const scopeSymbol = require('./helpers/symbols').scopeSymbol;
53
53
  const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
54
54
  const parentPaths = require('./helpers/path/parentPaths');
55
55
  const getDeepestSubdocumentForPath = require('./helpers/document/getDeepestSubdocumentForPath');
56
+ const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
56
57
 
57
58
  let DocumentArray;
58
59
  let MongooseArray;
@@ -1474,7 +1475,16 @@ Document.prototype.$set = function $set(path, val, type, options) {
1474
1475
 
1475
1476
  this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal);
1476
1477
 
1477
- if (savedState != null && savedState.hasOwnProperty(savedStatePath) && utils.deepEqual(val, savedState[savedStatePath])) {
1478
+ const isInTransaction = !!this.$__.session?.transaction;
1479
+ const isModifiedWithinTransaction = this.$__.session &&
1480
+ this.$__.session[sessionNewDocuments] &&
1481
+ this.$__.session[sessionNewDocuments].has(this) &&
1482
+ this.$__.session[sessionNewDocuments].get(this).modifiedPaths &&
1483
+ !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath);
1484
+ if (savedState != null &&
1485
+ savedState.hasOwnProperty(savedStatePath) &&
1486
+ (!isInTransaction || isModifiedWithinTransaction) &&
1487
+ utils.deepEqual(val, savedState[savedStatePath])) {
1478
1488
  this.unmarkModified(path);
1479
1489
  }
1480
1490
  }
@@ -6,6 +6,7 @@ const applyTimestampsToChildren = require('../update/applyTimestampsToChildren')
6
6
  const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
7
7
  const cast = require('../../cast');
8
8
  const castUpdate = require('../query/castUpdate');
9
+ const decorateUpdateWithVersionKey = require('../update/decorateUpdateWithVersionKey');
9
10
  const { inspect } = require('util');
10
11
  const setDefaultsOnInsert = require('../setDefaultsOnInsert');
11
12
 
@@ -33,6 +34,10 @@ module.exports = function castBulkWrite(originalModel, op, options) {
33
34
  if (options.session != null) {
34
35
  doc.$session(options.session);
35
36
  }
37
+ const versionKey = model?.schema?.options?.versionKey;
38
+ if (versionKey && doc[versionKey] == null) {
39
+ doc[versionKey] = 0;
40
+ }
36
41
  op['insertOne']['document'] = doc;
37
42
 
38
43
  if (options.skipValidation || op['insertOne'].skipValidation) {
@@ -81,6 +86,12 @@ module.exports = function castBulkWrite(originalModel, op, options) {
81
86
  });
82
87
  }
83
88
 
89
+ decorateUpdateWithVersionKey(
90
+ op['updateOne']['update'],
91
+ op['updateOne'],
92
+ model.schema.options.versionKey
93
+ );
94
+
84
95
  op['updateOne']['filter'] = cast(model.schema, op['updateOne']['filter'], {
85
96
  strict: strict,
86
97
  upsert: op['updateOne'].upsert
@@ -133,6 +144,12 @@ module.exports = function castBulkWrite(originalModel, op, options) {
133
144
 
134
145
  _addDiscriminatorToObject(schema, op['updateMany']['filter']);
135
146
 
147
+ decorateUpdateWithVersionKey(
148
+ op['updateMany']['update'],
149
+ op['updateMany'],
150
+ model.schema.options.versionKey
151
+ );
152
+
136
153
  op['updateMany']['filter'] = cast(model.schema, op['updateMany']['filter'], {
137
154
  strict: strict,
138
155
  upsert: op['updateMany'].upsert
@@ -173,6 +190,10 @@ module.exports = function castBulkWrite(originalModel, op, options) {
173
190
  if (options.session != null) {
174
191
  doc.$session(options.session);
175
192
  }
193
+ const versionKey = model?.schema?.options?.versionKey;
194
+ if (versionKey && doc[versionKey] == null) {
195
+ doc[versionKey] = 0;
196
+ }
176
197
  op['replaceOne']['replacement'] = doc;
177
198
 
178
199
  if (options.skipValidation || op['replaceOne'].skipValidation) {
@@ -45,7 +45,8 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
45
45
  let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path);
46
46
  allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);
47
47
 
48
- if (allSchemaTypes.length === 0 && options.strictPopulate !== false && options._localModel != null) {
48
+ const isStrictPopulateDisabled = options.strictPopulate === false || options.options?.strictPopulate === false;
49
+ if (!isStrictPopulateDisabled && allSchemaTypes.length === 0 && options._localModel != null) {
49
50
  return new StrictPopulate(options._fullPath || options.path);
50
51
  }
51
52
 
@@ -126,7 +126,8 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
126
126
  Object.keys(filter).length > 0) {
127
127
  // Trick the driver into allowing empty upserts to work around
128
128
  // https://github.com/mongodb/node-mongodb-native/pull/2490
129
- return { $setOnInsert: filter };
129
+ // Shallow clone to avoid passing defaults in re: gh-13962
130
+ return { $setOnInsert: { ...filter } };
130
131
  }
131
132
  return ret;
132
133
  };
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const modifiedPaths = require('./modifiedPaths');
4
+
5
+ /**
6
+ * Decorate the update with a version key, if necessary
7
+ * @api private
8
+ */
9
+
10
+ module.exports = function decorateUpdateWithVersionKey(update, options, versionKey) {
11
+ if (!versionKey || !(options && options.upsert || false)) {
12
+ return;
13
+ }
14
+
15
+ const updatedPaths = modifiedPaths(update);
16
+ if (!updatedPaths[versionKey]) {
17
+ if (options.overwrite) {
18
+ update[versionKey] = 0;
19
+ } else {
20
+ if (!update.$setOnInsert) {
21
+ update.$setOnInsert = {};
22
+ }
23
+ update.$setOnInsert[versionKey] = 0;
24
+ }
25
+ }
26
+ };
package/lib/model.js CHANGED
@@ -33,6 +33,7 @@ const assignVals = require('./helpers/populate/assignVals');
33
33
  const castBulkWrite = require('./helpers/model/castBulkWrite');
34
34
  const clone = require('./helpers/clone');
35
35
  const createPopulateQueryFilter = require('./helpers/populate/createPopulateQueryFilter');
36
+ const decorateUpdateWithVersionKey = require('./helpers/update/decorateUpdateWithVersionKey');
36
37
  const getDefaultBulkwriteResult = require('./helpers/getDefaultBulkwriteResult');
37
38
  const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
38
39
  const discriminator = require('./helpers/model/discriminator');
@@ -54,7 +55,6 @@ const isPathExcluded = require('./helpers/projection/isPathExcluded');
54
55
  const decorateDiscriminatorIndexOptions = require('./helpers/indexes/decorateDiscriminatorIndexOptions');
55
56
  const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
56
57
  const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
57
- const modifiedPaths = require('./helpers/update/modifiedPaths');
58
58
  const parallelLimit = require('./helpers/parallelLimit');
59
59
  const parentPaths = require('./helpers/path/parentPaths');
60
60
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
@@ -2451,7 +2451,7 @@ Model.findOneAndUpdate = function(conditions, update, options) {
2451
2451
  _isNested: true
2452
2452
  });
2453
2453
 
2454
- _decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
2454
+ decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
2455
2455
 
2456
2456
  const mq = new this.Query({}, {}, this, this.$__collection);
2457
2457
  mq.select(fields);
@@ -2459,29 +2459,6 @@ Model.findOneAndUpdate = function(conditions, update, options) {
2459
2459
  return mq.findOneAndUpdate(conditions, update, options);
2460
2460
  };
2461
2461
 
2462
- /**
2463
- * Decorate the update with a version key, if necessary
2464
- * @api private
2465
- */
2466
-
2467
- function _decorateUpdateWithVersionKey(update, options, versionKey) {
2468
- if (!versionKey || !(options && options.upsert || false)) {
2469
- return;
2470
- }
2471
-
2472
- const updatedPaths = modifiedPaths(update);
2473
- if (!updatedPaths[versionKey]) {
2474
- if (options.overwrite) {
2475
- update[versionKey] = 0;
2476
- } else {
2477
- if (!update.$setOnInsert) {
2478
- update.$setOnInsert = {};
2479
- }
2480
- update.$setOnInsert[versionKey] = 0;
2481
- }
2482
- }
2483
- }
2484
-
2485
2462
  /**
2486
2463
  * Issues a mongodb findOneAndUpdate command by a document's _id field.
2487
2464
  * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
@@ -4022,7 +3999,7 @@ function _update(model, op, conditions, doc, options) {
4022
3999
  model.schema &&
4023
4000
  model.schema.options &&
4024
4001
  model.schema.options.versionKey || null;
4025
- _decorateUpdateWithVersionKey(doc, options, versionKey);
4002
+ decorateUpdateWithVersionKey(doc, options, versionKey);
4026
4003
 
4027
4004
  return mq[op](conditions, doc, options);
4028
4005
  }
@@ -4286,9 +4263,7 @@ Model.populate = async function populate(docs, paths) {
4286
4263
  if (typeof paths === 'function' || typeof arguments[2] === 'function') {
4287
4264
  throw new MongooseError('Model.populate() no longer accepts a callback');
4288
4265
  }
4289
-
4290
4266
  const _this = this;
4291
-
4292
4267
  // normalized paths
4293
4268
  paths = utils.populate(paths);
4294
4269
  // data that should persist across subPopulate calls
package/lib/query.js CHANGED
@@ -2305,7 +2305,6 @@ Query.prototype._find = async function _find() {
2305
2305
  _completeManyLean(_this.model.schema, docs, null, completeManyOptions) :
2306
2306
  completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions);
2307
2307
  }
2308
-
2309
2308
  const pop = helpers.preparePopulationOptionsMQ(_this, mongooseOptions);
2310
2309
 
2311
2310
  if (mongooseOptions.lean) {
@@ -56,7 +56,7 @@ function SubdocumentPath(schema, path, options) {
56
56
  this.base = schema.base;
57
57
  SchemaType.call(this, path, options, 'Embedded');
58
58
 
59
- if (schema._applyDiscriminators != null) {
59
+ if (schema._applyDiscriminators != null && !options?._skipApplyDiscriminators) {
60
60
  for (const disc of schema._applyDiscriminators.keys()) {
61
61
  this.discriminator(disc, schema._applyDiscriminators.get(disc));
62
62
  }
@@ -388,8 +388,11 @@ SubdocumentPath.prototype.toJSON = function toJSON() {
388
388
  */
389
389
 
390
390
  SubdocumentPath.prototype.clone = function() {
391
- const options = Object.assign({}, this.options);
392
- const schematype = new this.constructor(this.schema, this.path, options);
391
+ const schematype = new this.constructor(
392
+ this.schema,
393
+ this.path,
394
+ { ...this.options, _skipApplyDiscriminators: true }
395
+ );
393
396
  schematype.validators = this.validators.slice();
394
397
  if (this.requiredValidator !== undefined) {
395
398
  schematype.requiredValidator = this.requiredValidator;
@@ -623,8 +623,8 @@ function cast$elemMatch(val, context) {
623
623
  discriminators[val[discriminatorKey]] != null) {
624
624
  return cast(discriminators[val[discriminatorKey]], val, null, this && this.$$context);
625
625
  }
626
-
627
- return cast(this.casterConstructor.schema, val, null, this && this.$$context);
626
+ const schema = this.casterConstructor.schema ?? context.schema;
627
+ return cast(schema, val, null, this && this.$$context);
628
628
  }
629
629
 
630
630
  const handle = SchemaArray.prototype.$conditionalHandlers = {};
package/lib/schema.js CHANGED
@@ -412,7 +412,9 @@ Schema.prototype._clone = function _clone(Constructor) {
412
412
  s.s.hooks = this.s.hooks.clone();
413
413
 
414
414
  s.tree = clone(this.tree);
415
- s.paths = clone(this.paths);
415
+ s.paths = Object.fromEntries(
416
+ Object.entries(this.paths).map(([key, value]) => ([key, value.clone()]))
417
+ );
416
418
  s.nested = clone(this.nested);
417
419
  s.subpaths = clone(this.subpaths);
418
420
  for (const schemaType of Object.values(s.paths)) {
package/lib/schematype.js CHANGED
@@ -1305,6 +1305,7 @@ SchemaType.prototype.doValidate = function(value, fn, scope, options) {
1305
1305
 
1306
1306
  const validatorProperties = isSimpleValidator(v) ? Object.assign({}, v) : clone(v);
1307
1307
  validatorProperties.path = options && options.path ? options.path : path;
1308
+ validatorProperties.fullPath = this.$fullPath;
1308
1309
  validatorProperties.value = value;
1309
1310
 
1310
1311
  if (validator instanceof RegExp) {
@@ -1426,6 +1427,7 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) {
1426
1427
  const validator = v.validator;
1427
1428
  const validatorProperties = isSimpleValidator(v) ? Object.assign({}, v) : clone(v);
1428
1429
  validatorProperties.path = options && options.path ? options.path : path;
1430
+ validatorProperties.fullPath = this.$fullPath;
1429
1431
  validatorProperties.value = value;
1430
1432
  let ok = false;
1431
1433
 
package/lib/utils.js CHANGED
@@ -31,6 +31,9 @@ exports.isMongooseDocumentArray = isMongooseDocumentArray.isMongooseDocumentArra
31
31
  exports.registerMongooseArray = isMongooseArray.registerMongooseArray;
32
32
  exports.registerMongooseDocumentArray = isMongooseDocumentArray.registerMongooseDocumentArray;
33
33
 
34
+ const oneSpaceRE = /\s/;
35
+ const manySpaceRE = /\s+/;
36
+
34
37
  /**
35
38
  * Produces a collection name from model `name`. By default, just returns
36
39
  * the model name
@@ -572,8 +575,8 @@ exports.populate = function populate(path, select, model, match, options, subPop
572
575
  function makeSingles(arr) {
573
576
  const ret = [];
574
577
  arr.forEach(function(obj) {
575
- if (/[\s]/.test(obj.path)) {
576
- const paths = obj.path.split(' ');
578
+ if (oneSpaceRE.test(obj.path)) {
579
+ const paths = obj.path.split(manySpaceRE);
577
580
  paths.forEach(function(p) {
578
581
  const copy = Object.assign({}, obj);
579
582
  copy.path = p;
@@ -592,9 +595,9 @@ function _populateObj(obj) {
592
595
  if (Array.isArray(obj.populate)) {
593
596
  const ret = [];
594
597
  obj.populate.forEach(function(obj) {
595
- if (/[\s]/.test(obj.path)) {
598
+ if (oneSpaceRE.test(obj.path)) {
596
599
  const copy = Object.assign({}, obj);
597
- const paths = copy.path.split(' ');
600
+ const paths = copy.path.split(manySpaceRE);
598
601
  paths.forEach(function(p) {
599
602
  copy.path = p;
600
603
  ret.push(exports.populate(copy)[0]);
@@ -609,7 +612,7 @@ function _populateObj(obj) {
609
612
  }
610
613
 
611
614
  const ret = [];
612
- const paths = obj.path.split(' ');
615
+ const paths = oneSpaceRE.test(obj.path) ? obj.path.split(manySpaceRE) : [obj.path];
613
616
  if (obj.options != null) {
614
617
  obj.options = clone(obj.options);
615
618
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "7.6.2",
4
+ "version": "7.6.4",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
package/types/index.d.ts CHANGED
@@ -260,7 +260,7 @@ declare module 'mongoose' {
260
260
  /** Returns a copy of this schema */
261
261
  clone<T = this>(): T;
262
262
 
263
- discriminator<DisSchema = Schema>(name: string, schema: DisSchema): this;
263
+ discriminator<DisSchema = Schema>(name: string | number, schema: DisSchema): this;
264
264
 
265
265
  /** Returns a new schema that has the picked `paths` from this schema. */
266
266
  pick<T = this>(paths: string[], options?: SchemaOptions): T;
@@ -4,6 +4,7 @@ declare module 'mongoose' {
4
4
 
5
5
  interface ValidatorProps {
6
6
  path: string;
7
+ fullPath: string;
7
8
  value: any;
8
9
  }
9
10