mongoose 6.4.7 → 6.5.0

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
@@ -491,6 +491,9 @@ Connection.prototype.transaction = function transaction(fn, options) {
491
491
  doc.set(doc.schema.options.versionKey, state.versionKey);
492
492
  }
493
493
 
494
+ if (state.modifiedPaths.length > 0 && doc.$__.activePaths.states.modify == null) {
495
+ doc.$__.activePaths.states.modify = {};
496
+ }
494
497
  for (const path of state.modifiedPaths) {
495
498
  doc.$__.activePaths.paths[path] = 'modify';
496
499
  doc.$__.activePaths.states.modify[path] = true;
@@ -550,8 +553,8 @@ Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) {
550
553
  // init-ed. It is sufficiently common to call `dropDatabase()` after
551
554
  // `mongoose.connect()` but before creating models that we want to
552
555
  // support this. See gh-6796
553
- for (const name of Object.keys(this.models)) {
554
- delete this.models[name].$init;
556
+ for (const model of Object.values(this.models)) {
557
+ delete model.$init;
555
558
  }
556
559
  this.db.dropDatabase(cb);
557
560
  });
@@ -840,6 +843,11 @@ Connection.prototype.openUri = function(uri, options, callback) {
840
843
  );
841
844
  }
842
845
 
846
+ for (const model of Object.values(this.models)) {
847
+ // Errors handled internally, so safe to ignore error
848
+ model.init(function $modelInitNoop() {});
849
+ }
850
+
843
851
  return this.$initialConnection;
844
852
  };
845
853
 
@@ -961,6 +969,13 @@ Connection.prototype.close = function(force, callback) {
961
969
  this.$wasForceClosed = !!force;
962
970
  }
963
971
 
972
+ for (const model of Object.values(this.models)) {
973
+ // If manually disconnecting, make sure to clear each model's `$init`
974
+ // promise, so Mongoose knows to re-run `init()` in case the
975
+ // connection is re-opened. See gh-12047.
976
+ delete model.$init;
977
+ }
978
+
964
979
  return promiseOrCallback(callback, cb => {
965
980
  this._close(force, false, cb);
966
981
  });
@@ -1462,6 +1477,11 @@ Connection.prototype.setClient = function setClient(client) {
1462
1477
  this._connectionString = client.s.url;
1463
1478
  _setClient(this, client, {}, client.s.options.dbName);
1464
1479
 
1480
+ for (const model of Object.values(this.models)) {
1481
+ // Errors handled internally, so safe to ignore error
1482
+ model.init(function $modelInitNoop() {});
1483
+ }
1484
+
1465
1485
  return this;
1466
1486
  };
1467
1487
 
@@ -20,6 +20,13 @@ class ChangeStream extends EventEmitter {
20
20
  this.pipeline = pipeline;
21
21
  this.options = options;
22
22
 
23
+ if (options && options.hydrate && !options.model) {
24
+ throw new Error(
25
+ 'Cannot create change stream with `hydrate: true` ' +
26
+ 'unless calling `Model.watch()`'
27
+ );
28
+ }
29
+
23
30
  // This wrapper is necessary because of buffering.
24
31
  changeStreamThunk((err, driverChangeStream) => {
25
32
  if (err != null) {
@@ -46,7 +53,12 @@ class ChangeStream extends EventEmitter {
46
53
  });
47
54
 
48
55
  ['close', 'change', 'end', 'error'].forEach(ev => {
49
- this.driverChangeStream.on(ev, data => this.emit(ev, data));
56
+ this.driverChangeStream.on(ev, data => {
57
+ if (data.fullDocument != null && this.options && this.options.hydrate) {
58
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
59
+ }
60
+ this.emit(ev, data);
61
+ });
50
62
  });
51
63
  });
52
64
 
@@ -69,6 +81,32 @@ class ChangeStream extends EventEmitter {
69
81
  }
70
82
 
71
83
  next(cb) {
84
+ if (this.options && this.options.hydrate) {
85
+ if (cb != null) {
86
+ const originalCb = cb;
87
+ cb = (err, data) => {
88
+ if (err != null) {
89
+ return originalCb(err);
90
+ }
91
+ if (data.fullDocument != null) {
92
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
93
+ }
94
+ return originalCb(null, data);
95
+ };
96
+ }
97
+
98
+ let maybePromise = this.driverChangeStream.next(cb);
99
+ if (maybePromise && typeof maybePromise.then === 'function') {
100
+ maybePromise = maybePromise.then(data => {
101
+ if (data.fullDocument != null) {
102
+ data.fullDocument = this.options.model.hydrate(data.fullDocument);
103
+ }
104
+ return data;
105
+ });
106
+ }
107
+ return maybePromise;
108
+ }
109
+
72
110
  return this.driverChangeStream.next(cb);
73
111
  }
74
112
 
package/lib/document.js CHANGED
@@ -18,6 +18,8 @@ const ValidatorError = require('./error/validator');
18
18
  const VirtualType = require('./virtualtype');
19
19
  const $__hasIncludedChildren = require('./helpers/projection/hasIncludedChildren');
20
20
  const promiseOrCallback = require('./helpers/promiseOrCallback');
21
+ const castNumber = require('./cast/number');
22
+ const applyDefaults = require('./helpers/document/applyDefaults');
21
23
  const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths');
22
24
  const compile = require('./helpers/document/compile').compile;
23
25
  const defineKey = require('./helpers/document/compile').defineKey;
@@ -93,7 +95,11 @@ function Document(obj, fields, skipId, options) {
93
95
  }
94
96
 
95
97
  this.$__ = new InternalCache();
96
- this.$isNew = 'isNew' in options ? options.isNew : true;
98
+
99
+ // Avoid setting `isNew` to `true`, because it is `true` by default
100
+ if (options.isNew != null && options.isNew !== true) {
101
+ this.$isNew = options.isNew;
102
+ }
97
103
 
98
104
  if (options.priorDoc != null) {
99
105
  this.$__.priorDoc = options.priorDoc;
@@ -116,13 +122,12 @@ function Document(obj, fields, skipId, options) {
116
122
  const schema = this.$__schema;
117
123
 
118
124
  if (typeof fields === 'boolean' || fields === 'throw') {
119
- this.$__.strictMode = fields;
125
+ if (fields !== true) {
126
+ this.$__.strictMode = fields;
127
+ }
120
128
  fields = undefined;
121
- } else {
129
+ } else if (schema.options.strict !== true) {
122
130
  this.$__.strictMode = schema.options.strict;
123
- if (fields != null) {
124
- this.$__.selected = fields;
125
- }
126
131
  }
127
132
 
128
133
  const requiredPaths = schema.requiredPaths(true);
@@ -134,9 +139,9 @@ function Document(obj, fields, skipId, options) {
134
139
 
135
140
  // determine if this doc is a result of a query with
136
141
  // excluded fields
137
- if (utils.isPOJO(fields)) {
142
+ if (utils.isPOJO(fields) && Object.keys(fields).length > 0) {
138
143
  exclude = isExclusive(fields);
139
- this.$__.fields = fields;
144
+ this.$__.selected = fields;
140
145
  this.$__.exclude = exclude;
141
146
  }
142
147
 
@@ -150,7 +155,7 @@ function Document(obj, fields, skipId, options) {
150
155
  // By default, defaults get applied **before** setting initial values
151
156
  // Re: gh-6155
152
157
  if (defaults) {
153
- $__applyDefaults(this, fields, exclude, hasIncludedChildren, true, null);
158
+ applyDefaults(this, fields, exclude, hasIncludedChildren, true, null);
154
159
  }
155
160
  }
156
161
  if (obj) {
@@ -174,7 +179,7 @@ function Document(obj, fields, skipId, options) {
174
179
  this.$__.skipDefaults = options.skipDefaults;
175
180
  }
176
181
  } else if (defaults) {
177
- $__applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults);
182
+ applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults);
178
183
  }
179
184
 
180
185
  if (!this.$__.strictMode && obj) {
@@ -260,6 +265,12 @@ Object.defineProperty(Document.prototype, 'errors', {
260
265
  }
261
266
  });
262
267
 
268
+ /*!
269
+ * ignore
270
+ */
271
+
272
+ Document.prototype.$isNew = true;
273
+
263
274
  /*!
264
275
  * Document exposes the NodeJS event emitter API, so you can use
265
276
  * `on`, `once`, etc.
@@ -441,122 +452,6 @@ Object.defineProperty(Document.prototype, '$op', {
441
452
  }
442
453
  });
443
454
 
444
- /*!
445
- * ignore
446
- */
447
-
448
- function $__applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip) {
449
- const paths = Object.keys(doc.$__schema.paths);
450
- const plen = paths.length;
451
-
452
- for (let i = 0; i < plen; ++i) {
453
- let def;
454
- let curPath = '';
455
- const p = paths[i];
456
-
457
- if (p === '_id' && doc.$__.skipId) {
458
- continue;
459
- }
460
-
461
- const type = doc.$__schema.paths[p];
462
- const path = type.splitPath();
463
- const len = path.length;
464
- let included = false;
465
- let doc_ = doc._doc;
466
- for (let j = 0; j < len; ++j) {
467
- if (doc_ == null) {
468
- break;
469
- }
470
-
471
- const piece = path[j];
472
- curPath += (!curPath.length ? '' : '.') + piece;
473
-
474
- if (exclude === true) {
475
- if (curPath in fields) {
476
- break;
477
- }
478
- } else if (exclude === false && fields && !included) {
479
- const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
480
- if (curPath in fields || (hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
481
- included = true;
482
- } else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) {
483
- break;
484
- }
485
- }
486
-
487
- if (j === len - 1) {
488
- if (doc_[piece] !== void 0) {
489
- break;
490
- }
491
-
492
- if (typeof type.defaultValue === 'function') {
493
- if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
494
- break;
495
- }
496
- if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
497
- break;
498
- }
499
- } else if (!isBeforeSetters) {
500
- // Non-function defaults should always run **before** setters
501
- continue;
502
- }
503
-
504
- if (pathsToSkip && pathsToSkip[curPath]) {
505
- break;
506
- }
507
-
508
- if (fields && exclude !== null) {
509
- if (exclude === true) {
510
- // apply defaults to all non-excluded fields
511
- if (p in fields) {
512
- continue;
513
- }
514
-
515
- try {
516
- def = type.getDefault(doc, false);
517
- } catch (err) {
518
- doc.invalidate(p, err);
519
- break;
520
- }
521
-
522
- if (typeof def !== 'undefined') {
523
- doc_[piece] = def;
524
- doc.$__.activePaths.default(p);
525
- }
526
- } else if (included) {
527
- // selected field
528
- try {
529
- def = type.getDefault(doc, false);
530
- } catch (err) {
531
- doc.invalidate(p, err);
532
- break;
533
- }
534
-
535
- if (typeof def !== 'undefined') {
536
- doc_[piece] = def;
537
- doc.$__.activePaths.default(p);
538
- }
539
- }
540
- } else {
541
- try {
542
- def = type.getDefault(doc, false);
543
- } catch (err) {
544
- doc.invalidate(p, err);
545
- break;
546
- }
547
-
548
- if (typeof def !== 'undefined') {
549
- doc_[piece] = def;
550
- doc.$__.activePaths.default(p);
551
- }
552
- }
553
- } else {
554
- doc_ = doc_[piece];
555
- }
556
- }
557
- }
558
- }
559
-
560
455
  /*!
561
456
  * ignore
562
457
  */
@@ -792,10 +687,11 @@ Document.prototype.$__init = function(doc, opts) {
792
687
  this.$emit('init', this);
793
688
  this.constructor.emit('init', this);
794
689
 
795
- const hasIncludedChildren = this.$__.exclude === false && this.$__.fields ?
796
- $__hasIncludedChildren(this.$__.fields) :
690
+ const hasIncludedChildren = this.$__.exclude === false && this.$__.selected ?
691
+ $__hasIncludedChildren(this.$__.selected) :
797
692
  null;
798
- $__applyDefaults(this, this.$__.fields, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults);
693
+
694
+ applyDefaults(this, this.$__.selected, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults);
799
695
 
800
696
  return this;
801
697
  };
@@ -863,7 +759,13 @@ function init(self, obj, doc, opts, prefix) {
863
759
 
864
760
  if (schemaType && !wasPopulated) {
865
761
  try {
866
- doc[i] = schemaType.cast(obj[i], self, true);
762
+ if (opts && opts.setters) {
763
+ // Call applySetters with `init = false` because otherwise setters are a noop
764
+ const overrideInit = false;
765
+ doc[i] = schemaType.applySetters(obj[i], self, overrideInit);
766
+ } else {
767
+ doc[i] = schemaType.cast(obj[i], self, true);
768
+ }
867
769
  } catch (e) {
868
770
  self.invalidate(e.path, new ValidatorError({
869
771
  path: e.path,
@@ -1649,7 +1551,7 @@ Document.prototype.$__shouldModify = function(pathToMark, path, options, constru
1649
1551
  return true;
1650
1552
  }
1651
1553
 
1652
- if (val === void 0 && path in this.$__.activePaths.states.default) {
1554
+ if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) {
1653
1555
  // we're just unsetting the default value which was never saved
1654
1556
  return false;
1655
1557
  }
@@ -1669,7 +1571,7 @@ Document.prototype.$__shouldModify = function(pathToMark, path, options, constru
1669
1571
  if (!constructing &&
1670
1572
  val !== null &&
1671
1573
  val !== undefined &&
1672
- path in this.$__.activePaths.states.default &&
1574
+ path in this.$__.activePaths.getStatePaths('default') &&
1673
1575
  deepEqual(val, schema.getDefault(this, constructing))) {
1674
1576
  // a path with a default was $unset on the server
1675
1577
  // and the user is setting it to the same value again
@@ -1702,6 +1604,12 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
1702
1604
  schema, val, priorVal);
1703
1605
 
1704
1606
  if (shouldModify) {
1607
+ if (this.$__.primitiveAtomics && this.$__.primitiveAtomics[path]) {
1608
+ delete this.$__.primitiveAtomics[path];
1609
+ if (Object.keys(this.$__.primitiveAtomics).length === 0) {
1610
+ delete this.$__.primitiveAtomics;
1611
+ }
1612
+ }
1705
1613
  this.markModified(pathToMark);
1706
1614
 
1707
1615
  // handle directly setting arrays (gh-1126)
@@ -1772,6 +1680,68 @@ Document.prototype.$__getValue = function(path) {
1772
1680
  return utils.getValue(path, this._doc);
1773
1681
  };
1774
1682
 
1683
+ /**
1684
+ * Increments the numeric value at `path` by the given `val`.
1685
+ * When you call `save()` on this document, Mongoose will send a
1686
+ * [`$inc`](https://www.mongodb.com/docs/manual/reference/operator/update/inc/)
1687
+ * as opposed to a `$set`.
1688
+ *
1689
+ * #### Example:
1690
+ * const schema = new Schema({ counter: Number });
1691
+ * const Test = db.model('Test', schema);
1692
+ *
1693
+ * const doc = await Test.create({ counter: 0 });
1694
+ * doc.$inc('counter', 2);
1695
+ * await doc.save(); // Sends a `{ $inc: { counter: 2 } }` to MongoDB
1696
+ * doc.counter; // 2
1697
+ *
1698
+ * doc.counter += 2;
1699
+ * await doc.save(); // Sends a `{ $set: { counter: 2 } }` to MongoDB
1700
+ *
1701
+ * @param {String|Array} path path or paths to update
1702
+ * @param {Number} val increment `path` by this value
1703
+ * @return {Document} this
1704
+ */
1705
+
1706
+ Document.prototype.$inc = function $inc(path, val) {
1707
+ if (val == null) {
1708
+ val = 1;
1709
+ }
1710
+
1711
+ if (Array.isArray(path)) {
1712
+ path.forEach((p) => this.$inc(p, val));
1713
+ return this;
1714
+ }
1715
+
1716
+ const schemaType = this.$__path(path);
1717
+ if (schemaType == null) {
1718
+ if (this.$__.strictMode === 'throw') {
1719
+ throw new StrictModeError(path);
1720
+ } else if (this.$__.strictMode === true) {
1721
+ return this;
1722
+ }
1723
+ } else if (schemaType.instance !== 'Number') {
1724
+ this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path));
1725
+ return this;
1726
+ }
1727
+
1728
+ try {
1729
+ val = castNumber(val);
1730
+ } catch (err) {
1731
+ this.invalidate(path, new MongooseError.CastError('number', val, path, err));
1732
+ }
1733
+
1734
+ const currentValue = this.$__getValue(path);
1735
+
1736
+ this.$__setValue(path, currentValue + val);
1737
+
1738
+ this.$__.primitiveAtomics = this.$__.primitiveAtomics || {};
1739
+ this.$__.primitiveAtomics[path] = { $inc: val };
1740
+ this.markModified(path);
1741
+
1742
+ return this;
1743
+ };
1744
+
1775
1745
  /**
1776
1746
  * Sets a raw value for a path (no casting, setters, transformations)
1777
1747
  *
@@ -1981,7 +1951,7 @@ Document.prototype.$ignore = function(path) {
1981
1951
  */
1982
1952
 
1983
1953
  Document.prototype.directModifiedPaths = function() {
1984
- return Object.keys(this.$__.activePaths.states.modify);
1954
+ return Object.keys(this.$__.activePaths.getStatePaths('modify'));
1985
1955
  };
1986
1956
 
1987
1957
  /**
@@ -2065,7 +2035,7 @@ function _isEmpty(v) {
2065
2035
  Document.prototype.modifiedPaths = function(options) {
2066
2036
  options = options || {};
2067
2037
 
2068
- const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
2038
+ const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
2069
2039
  const result = new Set();
2070
2040
 
2071
2041
  let i = 0;
@@ -2144,7 +2114,7 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
2144
2114
 
2145
2115
  Document.prototype.isModified = function(paths, modifiedPaths) {
2146
2116
  if (paths) {
2147
- const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
2117
+ const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
2148
2118
  if (directModifiedPaths.length === 0) {
2149
2119
  return false;
2150
2120
  }
@@ -2202,7 +2172,7 @@ Document.prototype.$isDefault = function(path) {
2202
2172
  }
2203
2173
 
2204
2174
  if (typeof path === 'string' && path.indexOf(' ') === -1) {
2205
- return this.$__.activePaths.states.default.hasOwnProperty(path);
2175
+ return this.$__.activePaths.getStatePaths('default').hasOwnProperty(path);
2206
2176
  }
2207
2177
 
2208
2178
  let paths = path;
@@ -2210,7 +2180,7 @@ Document.prototype.$isDefault = function(path) {
2210
2180
  paths = paths.split(' ');
2211
2181
  }
2212
2182
 
2213
- return paths.some(path => this.$__.activePaths.states.default.hasOwnProperty(path));
2183
+ return paths.some(path => this.$__.activePaths.getStatePaths('default').hasOwnProperty(path));
2214
2184
  };
2215
2185
 
2216
2186
  /**
@@ -2264,7 +2234,7 @@ Document.prototype.isDirectModified = function(path) {
2264
2234
  }
2265
2235
 
2266
2236
  if (typeof path === 'string' && path.indexOf(' ') === -1) {
2267
- return this.$__.activePaths.states.modify.hasOwnProperty(path);
2237
+ return this.$__.activePaths.getStatePaths('modify').hasOwnProperty(path);
2268
2238
  }
2269
2239
 
2270
2240
  let paths = path;
@@ -2272,7 +2242,7 @@ Document.prototype.isDirectModified = function(path) {
2272
2242
  paths = paths.split(' ');
2273
2243
  }
2274
2244
 
2275
- return paths.some(path => this.$__.activePaths.states.modify.hasOwnProperty(path));
2245
+ return paths.some(path => this.$__.activePaths.getStatePaths('modify').hasOwnProperty(path));
2276
2246
  };
2277
2247
 
2278
2248
  /**
@@ -2289,7 +2259,7 @@ Document.prototype.isInit = function(path) {
2289
2259
  }
2290
2260
 
2291
2261
  if (typeof path === 'string' && path.indexOf(' ') === -1) {
2292
- return this.$__.activePaths.states.init.hasOwnProperty(path);
2262
+ return this.$__.activePaths.getStatePaths('init').hasOwnProperty(path);
2293
2263
  }
2294
2264
 
2295
2265
  let paths = path;
@@ -2297,7 +2267,7 @@ Document.prototype.isInit = function(path) {
2297
2267
  paths = paths.split(' ');
2298
2268
  }
2299
2269
 
2300
- return paths.some(path => this.$__.activePaths.states.init.hasOwnProperty(path));
2270
+ return paths.some(path => this.$__.activePaths.getStatePaths('init').hasOwnProperty(path));
2301
2271
  };
2302
2272
 
2303
2273
  /**
@@ -2533,7 +2503,7 @@ Document.prototype.$validate = Document.prototype.validate;
2533
2503
  */
2534
2504
 
2535
2505
  function _evaluateRequiredFunctions(doc) {
2536
- const requiredFields = Object.keys(doc.$__.activePaths.states.require);
2506
+ const requiredFields = Object.keys(doc.$__.activePaths.getStatePaths('require'));
2537
2507
  let i = 0;
2538
2508
  const len = requiredFields.length;
2539
2509
  for (i = 0; i < len; ++i) {
@@ -2561,7 +2531,7 @@ function _getPathsToValidate(doc) {
2561
2531
 
2562
2532
  _evaluateRequiredFunctions(doc);
2563
2533
  // only validate required fields when necessary
2564
- let paths = new Set(Object.keys(doc.$__.activePaths.states.require).filter(function(path) {
2534
+ let paths = new Set(Object.keys(doc.$__.activePaths.getStatePaths('require')).filter(function(path) {
2565
2535
  if (!doc.$__isSelected(path) && !doc.$isModified(path)) {
2566
2536
  return false;
2567
2537
  }
@@ -2571,9 +2541,9 @@ function _getPathsToValidate(doc) {
2571
2541
  return true;
2572
2542
  }));
2573
2543
 
2574
- Object.keys(doc.$__.activePaths.states.init).forEach(addToPaths);
2575
- Object.keys(doc.$__.activePaths.states.modify).forEach(addToPaths);
2576
- Object.keys(doc.$__.activePaths.states.default).forEach(addToPaths);
2544
+ Object.keys(doc.$__.activePaths.getStatePaths('init')).forEach(addToPaths);
2545
+ Object.keys(doc.$__.activePaths.getStatePaths('modify')).forEach(addToPaths);
2546
+ Object.keys(doc.$__.activePaths.getStatePaths('default')).forEach(addToPaths);
2577
2547
  function addToPaths(p) { paths.add(p); }
2578
2548
 
2579
2549
  const subdocs = doc.$getAllSubdocs();
@@ -3296,8 +3266,8 @@ Document.prototype.$__reset = function reset() {
3296
3266
 
3297
3267
  this.$__.backup = {};
3298
3268
  this.$__.backup.activePaths = {
3299
- modify: Object.assign({}, this.$__.activePaths.states.modify),
3300
- default: Object.assign({}, this.$__.activePaths.states.default)
3269
+ modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')),
3270
+ default: Object.assign({}, this.$__.activePaths.getStatePaths('default'))
3301
3271
  };
3302
3272
  this.$__.backup.validationError = this.$__.validationError;
3303
3273
  this.$__.backup.errors = this.$errors;
@@ -52,6 +52,15 @@ class ValidationError extends MongooseError {
52
52
  * add message
53
53
  */
54
54
  addError(path, error) {
55
+ if (error instanceof ValidationError) {
56
+ const { errors } = error;
57
+ for (const errorPath of Object.keys(errors)) {
58
+ this.addError(`${path}.${errorPath}`, errors[errorPath]);
59
+ }
60
+
61
+ return;
62
+ }
63
+
55
64
  this.errors[path] = error;
56
65
  this.message = this._message + ': ' + _generateMessage(this);
57
66
  }
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip) {
4
+ const paths = Object.keys(doc.$__schema.paths);
5
+ const plen = paths.length;
6
+
7
+ for (let i = 0; i < plen; ++i) {
8
+ let def;
9
+ let curPath = '';
10
+ const p = paths[i];
11
+
12
+ if (p === '_id' && doc.$__.skipId) {
13
+ continue;
14
+ }
15
+
16
+ const type = doc.$__schema.paths[p];
17
+ const path = type.splitPath();
18
+ const len = path.length;
19
+ let included = false;
20
+ let doc_ = doc._doc;
21
+ for (let j = 0; j < len; ++j) {
22
+ if (doc_ == null) {
23
+ break;
24
+ }
25
+
26
+ const piece = path[j];
27
+ curPath += (!curPath.length ? '' : '.') + piece;
28
+
29
+ if (exclude === true) {
30
+ if (curPath in fields) {
31
+ break;
32
+ }
33
+ } else if (exclude === false && fields && !included) {
34
+ const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
35
+ if (curPath in fields || (hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
36
+ included = true;
37
+ } else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) {
38
+ break;
39
+ }
40
+ }
41
+
42
+ if (j === len - 1) {
43
+ if (doc_[piece] !== void 0) {
44
+ break;
45
+ }
46
+
47
+ if (isBeforeSetters != null) {
48
+ if (typeof type.defaultValue === 'function') {
49
+ if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
50
+ break;
51
+ }
52
+ if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
53
+ break;
54
+ }
55
+ } else if (!isBeforeSetters) {
56
+ // Non-function defaults should always run **before** setters
57
+ continue;
58
+ }
59
+ }
60
+
61
+ if (pathsToSkip && pathsToSkip[curPath]) {
62
+ break;
63
+ }
64
+
65
+ if (fields && exclude !== null) {
66
+ if (exclude === true) {
67
+ // apply defaults to all non-excluded fields
68
+ if (p in fields) {
69
+ continue;
70
+ }
71
+
72
+ try {
73
+ def = type.getDefault(doc, false);
74
+ } catch (err) {
75
+ doc.invalidate(p, err);
76
+ break;
77
+ }
78
+
79
+ if (typeof def !== 'undefined') {
80
+ doc_[piece] = def;
81
+ doc.$__.activePaths.default(p);
82
+ }
83
+ } else if (included) {
84
+ // selected field
85
+ try {
86
+ def = type.getDefault(doc, false);
87
+ } catch (err) {
88
+ doc.invalidate(p, err);
89
+ break;
90
+ }
91
+
92
+ if (typeof def !== 'undefined') {
93
+ doc_[piece] = def;
94
+ doc.$__.activePaths.default(p);
95
+ }
96
+ }
97
+ } else {
98
+ try {
99
+ def = type.getDefault(doc, false);
100
+ } catch (err) {
101
+ doc.invalidate(p, err);
102
+ break;
103
+ }
104
+
105
+ if (typeof def !== 'undefined') {
106
+ doc_[piece] = def;
107
+ doc.$__.activePaths.default(p);
108
+ }
109
+ }
110
+ } else {
111
+ doc_ = doc_[piece];
112
+ }
113
+ }
114
+ }
115
+ };