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 +22 -2
- package/lib/cursor/ChangeStream.js +39 -1
- package/lib/document.js +118 -148
- package/lib/error/validation.js +9 -0
- package/lib/helpers/document/applyDefaults.js +115 -0
- package/lib/helpers/document/cleanModifiedSubpaths.js +3 -3
- package/lib/helpers/document/compile.js +7 -6
- package/lib/helpers/firstKey.js +8 -0
- package/lib/helpers/model/applyDefaultsToPOJO.js +52 -0
- package/lib/helpers/model/castBulkWrite.js +1 -1
- package/lib/helpers/model/pushNestedArrayPaths.js +15 -0
- package/lib/internal.js +3 -1
- package/lib/model.js +200 -84
- package/lib/options/SchemaArrayOptions.js +19 -0
- package/lib/plugins/trackTransaction.js +2 -2
- package/lib/query.js +3 -0
- package/lib/schema/array.js +2 -1
- package/lib/schema/documentarray.js +12 -1
- package/lib/schema.js +20 -2
- package/lib/schematype.js +5 -1
- package/lib/statemachine.js +23 -9
- package/lib/utils.js +8 -0
- package/lib/validoptions.js +1 -0
- package/package.json +3 -3
- package/types/document.d.ts +7 -0
- package/types/index.d.ts +3 -2
- package/types/inferschematype.d.ts +3 -20
- package/types/models.d.ts +50 -46
- package/types/mongooseoptions.d.ts +6 -0
- package/types/schemaoptions.d.ts +15 -4
- package/types/utility.d.ts +19 -0
- package/types/virtuals.d.ts +14 -0
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
|
|
554
|
-
delete
|
|
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 =>
|
|
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
|
-
|
|
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
|
-
|
|
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.$__.
|
|
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
|
-
|
|
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
|
-
|
|
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.$__.
|
|
796
|
-
$__hasIncludedChildren(this.$__.
|
|
690
|
+
const hasIncludedChildren = this.$__.exclude === false && this.$__.selected ?
|
|
691
|
+
$__hasIncludedChildren(this.$__.selected) :
|
|
797
692
|
null;
|
|
798
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
2575
|
-
Object.keys(doc.$__.activePaths.
|
|
2576
|
-
Object.keys(doc.$__.activePaths.
|
|
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.
|
|
3300
|
-
default: Object.assign({}, this.$__.activePaths.
|
|
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;
|
package/lib/error/validation.js
CHANGED
|
@@ -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
|
+
};
|