mongoose 6.4.7 → 6.5.2
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 +23 -3
- package/lib/cursor/ChangeStream.js +39 -1
- package/lib/document.js +129 -154
- package/lib/error/index.js +1 -0
- 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/helpers/populate/markArraySubdocsPopulated.js +1 -0
- package/lib/helpers/projection/hasIncludedChildren.js +1 -0
- package/lib/helpers/promiseOrCallback.js +24 -15
- package/lib/helpers/update/applyTimestampsToChildren.js +6 -2
- package/lib/index.js +2 -1
- package/lib/internal.js +3 -1
- package/lib/model.js +237 -95
- package/lib/options/SchemaArrayOptions.js +19 -0
- package/lib/options/SchemaNumberOptions.js +2 -0
- package/lib/options/SchemaObjectIdOptions.js +1 -0
- package/lib/plugins/trackTransaction.js +2 -2
- package/lib/query.js +30 -11
- package/lib/schema/SubdocumentPath.js +10 -0
- package/lib/schema/array.js +2 -1
- package/lib/schema/documentarray.js +14 -1
- package/lib/schema/string.js +3 -0
- package/lib/schema.js +22 -3
- package/lib/schematype.js +5 -1
- package/lib/statemachine.js +23 -9
- package/lib/types/buffer.js +23 -21
- package/lib/types/map.js +2 -0
- package/lib/utils.js +8 -0
- package/lib/validoptions.js +1 -0
- package/package.json +14 -14
- package/{build-browser.js → scripts/build-browser.js} +1 -1
- package/types/connection.d.ts +7 -1
- package/types/document.d.ts +8 -1
- package/types/expressions.d.ts +1 -1
- package/types/index.d.ts +31 -28
- package/types/inferschematype.d.ts +3 -20
- package/types/models.d.ts +53 -49
- 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
|
});
|
|
@@ -1102,7 +1117,7 @@ Connection.prototype.collection = function(name, options) {
|
|
|
1102
1117
|
* @param {Function} fn plugin callback
|
|
1103
1118
|
* @param {Object} [opts] optional options
|
|
1104
1119
|
* @return {Connection} this
|
|
1105
|
-
* @see plugins
|
|
1120
|
+
* @see plugins /docs/plugins
|
|
1106
1121
|
* @api public
|
|
1107
1122
|
*/
|
|
1108
1123
|
|
|
@@ -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 != null && 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,69 @@ 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
|
+
*
|
|
1691
|
+
* const schema = new Schema({ counter: Number });
|
|
1692
|
+
* const Test = db.model('Test', schema);
|
|
1693
|
+
*
|
|
1694
|
+
* const doc = await Test.create({ counter: 0 });
|
|
1695
|
+
* doc.$inc('counter', 2);
|
|
1696
|
+
* await doc.save(); // Sends a `{ $inc: { counter: 2 } }` to MongoDB
|
|
1697
|
+
* doc.counter; // 2
|
|
1698
|
+
*
|
|
1699
|
+
* doc.counter += 2;
|
|
1700
|
+
* await doc.save(); // Sends a `{ $set: { counter: 2 } }` to MongoDB
|
|
1701
|
+
*
|
|
1702
|
+
* @param {String|Array} path path or paths to update
|
|
1703
|
+
* @param {Number} val increment `path` by this value
|
|
1704
|
+
* @return {Document} this
|
|
1705
|
+
*/
|
|
1706
|
+
|
|
1707
|
+
Document.prototype.$inc = function $inc(path, val) {
|
|
1708
|
+
if (val == null) {
|
|
1709
|
+
val = 1;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
if (Array.isArray(path)) {
|
|
1713
|
+
path.forEach((p) => this.$inc(p, val));
|
|
1714
|
+
return this;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
const schemaType = this.$__path(path);
|
|
1718
|
+
if (schemaType == null) {
|
|
1719
|
+
if (this.$__.strictMode === 'throw') {
|
|
1720
|
+
throw new StrictModeError(path);
|
|
1721
|
+
} else if (this.$__.strictMode === true) {
|
|
1722
|
+
return this;
|
|
1723
|
+
}
|
|
1724
|
+
} else if (schemaType.instance !== 'Number') {
|
|
1725
|
+
this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path));
|
|
1726
|
+
return this;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
try {
|
|
1730
|
+
val = castNumber(val);
|
|
1731
|
+
} catch (err) {
|
|
1732
|
+
this.invalidate(path, new MongooseError.CastError('number', val, path, err));
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
const currentValue = this.$__getValue(path);
|
|
1736
|
+
|
|
1737
|
+
this.$__setValue(path, currentValue + val);
|
|
1738
|
+
|
|
1739
|
+
this.$__.primitiveAtomics = this.$__.primitiveAtomics || {};
|
|
1740
|
+
this.$__.primitiveAtomics[path] = { $inc: val };
|
|
1741
|
+
this.markModified(path);
|
|
1742
|
+
|
|
1743
|
+
return this;
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1775
1746
|
/**
|
|
1776
1747
|
* Sets a raw value for a path (no casting, setters, transformations)
|
|
1777
1748
|
*
|
|
@@ -1981,7 +1952,7 @@ Document.prototype.$ignore = function(path) {
|
|
|
1981
1952
|
*/
|
|
1982
1953
|
|
|
1983
1954
|
Document.prototype.directModifiedPaths = function() {
|
|
1984
|
-
return Object.keys(this.$__.activePaths.
|
|
1955
|
+
return Object.keys(this.$__.activePaths.getStatePaths('modify'));
|
|
1985
1956
|
};
|
|
1986
1957
|
|
|
1987
1958
|
/**
|
|
@@ -2065,7 +2036,7 @@ function _isEmpty(v) {
|
|
|
2065
2036
|
Document.prototype.modifiedPaths = function(options) {
|
|
2066
2037
|
options = options || {};
|
|
2067
2038
|
|
|
2068
|
-
const directModifiedPaths = Object.keys(this.$__.activePaths.
|
|
2039
|
+
const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
|
|
2069
2040
|
const result = new Set();
|
|
2070
2041
|
|
|
2071
2042
|
let i = 0;
|
|
@@ -2144,7 +2115,7 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
|
|
|
2144
2115
|
|
|
2145
2116
|
Document.prototype.isModified = function(paths, modifiedPaths) {
|
|
2146
2117
|
if (paths) {
|
|
2147
|
-
const directModifiedPaths = Object.keys(this.$__.activePaths.
|
|
2118
|
+
const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
|
|
2148
2119
|
if (directModifiedPaths.length === 0) {
|
|
2149
2120
|
return false;
|
|
2150
2121
|
}
|
|
@@ -2202,7 +2173,7 @@ Document.prototype.$isDefault = function(path) {
|
|
|
2202
2173
|
}
|
|
2203
2174
|
|
|
2204
2175
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2205
|
-
return this.$__.activePaths.
|
|
2176
|
+
return this.$__.activePaths.getStatePaths('default').hasOwnProperty(path);
|
|
2206
2177
|
}
|
|
2207
2178
|
|
|
2208
2179
|
let paths = path;
|
|
@@ -2210,7 +2181,7 @@ Document.prototype.$isDefault = function(path) {
|
|
|
2210
2181
|
paths = paths.split(' ');
|
|
2211
2182
|
}
|
|
2212
2183
|
|
|
2213
|
-
return paths.some(path => this.$__.activePaths.
|
|
2184
|
+
return paths.some(path => this.$__.activePaths.getStatePaths('default').hasOwnProperty(path));
|
|
2214
2185
|
};
|
|
2215
2186
|
|
|
2216
2187
|
/**
|
|
@@ -2264,7 +2235,7 @@ Document.prototype.isDirectModified = function(path) {
|
|
|
2264
2235
|
}
|
|
2265
2236
|
|
|
2266
2237
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2267
|
-
return this.$__.activePaths.
|
|
2238
|
+
return this.$__.activePaths.getStatePaths('modify').hasOwnProperty(path);
|
|
2268
2239
|
}
|
|
2269
2240
|
|
|
2270
2241
|
let paths = path;
|
|
@@ -2272,7 +2243,7 @@ Document.prototype.isDirectModified = function(path) {
|
|
|
2272
2243
|
paths = paths.split(' ');
|
|
2273
2244
|
}
|
|
2274
2245
|
|
|
2275
|
-
return paths.some(path => this.$__.activePaths.
|
|
2246
|
+
return paths.some(path => this.$__.activePaths.getStatePaths('modify').hasOwnProperty(path));
|
|
2276
2247
|
};
|
|
2277
2248
|
|
|
2278
2249
|
/**
|
|
@@ -2289,7 +2260,7 @@ Document.prototype.isInit = function(path) {
|
|
|
2289
2260
|
}
|
|
2290
2261
|
|
|
2291
2262
|
if (typeof path === 'string' && path.indexOf(' ') === -1) {
|
|
2292
|
-
return this.$__.activePaths.
|
|
2263
|
+
return this.$__.activePaths.getStatePaths('init').hasOwnProperty(path);
|
|
2293
2264
|
}
|
|
2294
2265
|
|
|
2295
2266
|
let paths = path;
|
|
@@ -2297,7 +2268,7 @@ Document.prototype.isInit = function(path) {
|
|
|
2297
2268
|
paths = paths.split(' ');
|
|
2298
2269
|
}
|
|
2299
2270
|
|
|
2300
|
-
return paths.some(path => this.$__.activePaths.
|
|
2271
|
+
return paths.some(path => this.$__.activePaths.getStatePaths('init').hasOwnProperty(path));
|
|
2301
2272
|
};
|
|
2302
2273
|
|
|
2303
2274
|
/**
|
|
@@ -2533,7 +2504,7 @@ Document.prototype.$validate = Document.prototype.validate;
|
|
|
2533
2504
|
*/
|
|
2534
2505
|
|
|
2535
2506
|
function _evaluateRequiredFunctions(doc) {
|
|
2536
|
-
const requiredFields = Object.keys(doc.$__.activePaths.
|
|
2507
|
+
const requiredFields = Object.keys(doc.$__.activePaths.getStatePaths('require'));
|
|
2537
2508
|
let i = 0;
|
|
2538
2509
|
const len = requiredFields.length;
|
|
2539
2510
|
for (i = 0; i < len; ++i) {
|
|
@@ -2561,7 +2532,7 @@ function _getPathsToValidate(doc) {
|
|
|
2561
2532
|
|
|
2562
2533
|
_evaluateRequiredFunctions(doc);
|
|
2563
2534
|
// only validate required fields when necessary
|
|
2564
|
-
let paths = new Set(Object.keys(doc.$__.activePaths.
|
|
2535
|
+
let paths = new Set(Object.keys(doc.$__.activePaths.getStatePaths('require')).filter(function(path) {
|
|
2565
2536
|
if (!doc.$__isSelected(path) && !doc.$isModified(path)) {
|
|
2566
2537
|
return false;
|
|
2567
2538
|
}
|
|
@@ -2571,9 +2542,9 @@ function _getPathsToValidate(doc) {
|
|
|
2571
2542
|
return true;
|
|
2572
2543
|
}));
|
|
2573
2544
|
|
|
2574
|
-
Object.keys(doc.$__.activePaths.
|
|
2575
|
-
Object.keys(doc.$__.activePaths.
|
|
2576
|
-
Object.keys(doc.$__.activePaths.
|
|
2545
|
+
Object.keys(doc.$__.activePaths.getStatePaths('init')).forEach(addToPaths);
|
|
2546
|
+
Object.keys(doc.$__.activePaths.getStatePaths('modify')).forEach(addToPaths);
|
|
2547
|
+
Object.keys(doc.$__.activePaths.getStatePaths('default')).forEach(addToPaths);
|
|
2577
2548
|
function addToPaths(p) { paths.add(p); }
|
|
2578
2549
|
|
|
2579
2550
|
const subdocs = doc.$getAllSubdocs();
|
|
@@ -3296,8 +3267,8 @@ Document.prototype.$__reset = function reset() {
|
|
|
3296
3267
|
|
|
3297
3268
|
this.$__.backup = {};
|
|
3298
3269
|
this.$__.backup.activePaths = {
|
|
3299
|
-
modify: Object.assign({}, this.$__.activePaths.
|
|
3300
|
-
default: Object.assign({}, this.$__.activePaths.
|
|
3270
|
+
modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')),
|
|
3271
|
+
default: Object.assign({}, this.$__.activePaths.getStatePaths('default'))
|
|
3301
3272
|
};
|
|
3302
3273
|
this.$__.backup.validationError = this.$__.validationError;
|
|
3303
3274
|
this.$__.backup.errors = this.$errors;
|
|
@@ -4246,6 +4217,7 @@ Document.prototype.equals = function(doc) {
|
|
|
4246
4217
|
*
|
|
4247
4218
|
* #### Example:
|
|
4248
4219
|
*
|
|
4220
|
+
* // Given a document, `populate()` lets you pull in referenced docs
|
|
4249
4221
|
* await doc.populate([
|
|
4250
4222
|
* 'stories',
|
|
4251
4223
|
* { path: 'fans', sort: { name: -1 } }
|
|
@@ -4254,12 +4226,15 @@ Document.prototype.equals = function(doc) {
|
|
|
4254
4226
|
* doc.stories[0].title; // 'Casino Royale'
|
|
4255
4227
|
* doc.populated('fans'); // Array of ObjectIds
|
|
4256
4228
|
*
|
|
4257
|
-
*
|
|
4258
|
-
*
|
|
4229
|
+
* // If the referenced doc has been deleted, `populate()` will
|
|
4230
|
+
* // remove that entry from the array.
|
|
4231
|
+
* await Story.delete({ title: 'Casino Royale' });
|
|
4232
|
+
* await doc.populate('stories'); // Empty array
|
|
4259
4233
|
*
|
|
4260
|
-
*
|
|
4261
|
-
*
|
|
4262
|
-
* doc.fans
|
|
4234
|
+
* // You can also pass additional query options to `populate()`,
|
|
4235
|
+
* // like projections:
|
|
4236
|
+
* await doc.populate('fans', '-email');
|
|
4237
|
+
* doc.fans[0].email // undefined because of 2nd param `select`
|
|
4263
4238
|
*
|
|
4264
4239
|
* @param {String|Object|Array} path either the path to populate or an object specifying all parameters, or either an array of those
|
|
4265
4240
|
* @param {Object|String} [select] Field selection for the population query
|
|
@@ -4275,7 +4250,7 @@ Document.prototype.equals = function(doc) {
|
|
|
4275
4250
|
* @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
|
|
4276
4251
|
* @param {Object} [options.options=null] Additional options like `limit` and `lean`.
|
|
4277
4252
|
* @param {Function} [callback] Callback
|
|
4278
|
-
* @see population
|
|
4253
|
+
* @see population /docs/populate
|
|
4279
4254
|
* @see Query#select #query_Query-select
|
|
4280
4255
|
* @see Model.populate #model_Model-populate
|
|
4281
4256
|
* @memberOf Document
|
package/lib/error/index.js
CHANGED
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
|
}
|