mongoose 5.5.0 → 5.5.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/History.md +43 -1
- package/lib/aggregate.js +16 -4
- package/lib/connection.js +7 -1
- package/lib/document.js +43 -36
- package/lib/drivers/node-mongodb-native/collection.js +16 -4
- package/lib/drivers/node-mongodb-native/index.js +1 -1
- package/lib/helpers/document/cleanModifiedSubpaths.js +3 -0
- package/lib/helpers/document/getEmbeddedDiscriminatorPath.js +5 -5
- package/lib/helpers/model/applyStaticHooks.js +3 -0
- package/lib/helpers/query/castUpdate.js +0 -2
- package/lib/helpers/schema/setParentPointers.js +5 -5
- package/lib/helpers/symbols.js +8 -11
- package/lib/helpers/update/applyTimestampsToChildren.js +22 -16
- package/lib/internal.js +1 -0
- package/lib/model.js +78 -24
- package/lib/query.js +76 -31
- package/lib/schema/array.js +50 -0
- package/lib/schema/documentarray.js +42 -26
- package/lib/schema.js +6 -5
- package/lib/schematype.js +32 -2
- package/lib/types/array.js +48 -24
- package/lib/types/documentarray.js +16 -13
- package/lib/types/embedded.js +1 -1
- package/lib/types/map.js +5 -0
- package/lib/types/subdocument.js +18 -1
- package/package.json +2 -2
package/History.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
5.5.4 / 2019-04-25
|
|
2
|
+
==================
|
|
3
|
+
* fix(document): avoid calling custom getters when saving #7719
|
|
4
|
+
* fix(timestamps): handle child schema timestamps correctly when reusing child schemas #7712
|
|
5
|
+
* fix(query): pass correct callback for _legacyFindAndModify #7736 [Fonger](https://github.com/Fonger)
|
|
6
|
+
* fix(model+query): allow setting `replacement` parameter for `findOneAndReplace()` #7654
|
|
7
|
+
* fix(map): make `delete()` unset the key in the database #7746 [Fonger](https://github.com/Fonger)
|
|
8
|
+
* fix(array): use symbol for `_schema` property to avoid confusing deep equality checks #7700
|
|
9
|
+
* fix(document): prevent `depopulate()` from removing fields with empty array #7741 #7740 [Fonger](https://github.com/Fonger)
|
|
10
|
+
* fix: make `MongooseArray#includes` support ObjectIds #7732 #6354 [Fonger](https://github.com/Fonger)
|
|
11
|
+
* fix(document): report correct validation error index when pushing onto doc array #7744 [Fonger](https://github.com/Fonger)
|
|
12
|
+
|
|
13
|
+
5.5.3 / 2019-04-22
|
|
14
|
+
==================
|
|
15
|
+
* fix: add findAndModify deprecation warning that references the useFindAndModify option #7644
|
|
16
|
+
* fix(document): handle pushing a doc onto a discriminator that contains a doc array #7704
|
|
17
|
+
* fix(update): run setters on array elements when doing $set #7679
|
|
18
|
+
* fix: correct usage of arguments while buffering commands #7718 [rzymek](https://github.com/rzymek)
|
|
19
|
+
* fix(document): avoid error clearing modified subpaths if doc not defined #7715 [bitflower](https://github.com/bitflower)
|
|
20
|
+
* refactor(array): move `_parent` property behind a symbol #7726 #7700
|
|
21
|
+
* docs(model): list out all operations and options for `bulkWrite()` #7055
|
|
22
|
+
* docs(aggregate): use `eachAsync()` instead of nonexistent `each()` #7699
|
|
23
|
+
* docs(validation): add CastError validation example #7514
|
|
24
|
+
* docs(query+model): list out all options and callback details for Model.updateX() and Query#updateX() #7646
|
|
25
|
+
|
|
26
|
+
5.5.2 / 2019-04-16
|
|
27
|
+
==================
|
|
28
|
+
* fix(document): support setting nested path to non-POJO object #7639
|
|
29
|
+
* perf(connection): remove leaked event handler in `Model.init()` so `deleteModel()` frees all memory #7682
|
|
30
|
+
* fix(timestamps): handle custom statics that conflict with built-in functions (like mongoose-delete plugin) #7698
|
|
31
|
+
* fix(populate): make `Document#populated()` work for populated subdocs #7685
|
|
32
|
+
* fix(document): support `.set()` on document array underneath embedded discriminator path #7656
|
|
33
|
+
|
|
34
|
+
5.5.1 / 2019-04-11
|
|
35
|
+
==================
|
|
36
|
+
* fix(document): correctly overwrite all properties when setting a single nested subdoc #7660 #7681
|
|
37
|
+
* fix(array): allow customization of array required validator #7696 [freewil](https://github.com/freewil)
|
|
38
|
+
* fix(discriminator): handle embedded discriminators when casting array defaults #7687
|
|
39
|
+
* fix(collection): ensure collection functions return a promise even if disconnected #7676
|
|
40
|
+
* fix(schematype): avoid indexing properties with `{ unique: false, index: false }` #7620
|
|
41
|
+
* fix(aggregate): make `Aggregate#model()` with no arguments return the aggregation's model #7608
|
|
42
|
+
|
|
1
43
|
5.5.0 / 2019-04-08
|
|
2
44
|
==================
|
|
3
45
|
* feat(model): support applying hooks to custom static functions #5982
|
|
@@ -9,7 +51,7 @@
|
|
|
9
51
|
* feat(model): print warning when calling create() incorrectly with a session #7535
|
|
10
52
|
* feat(document): add Document#isEmpty() and corresponding helpers for nested paths #5369
|
|
11
53
|
* feat(document): add `getters` option to Document#get() #7233
|
|
12
|
-
feat(query): add Query#projection() to get or overwrite the current projection #7384
|
|
54
|
+
* feat(query): add Query#projection() to get or overwrite the current projection #7384
|
|
13
55
|
* fix(document): set full validator path on validatorProperties if `propsParameter` set on validator #7447
|
|
14
56
|
* feat(document): add Document#directModifiedPaths() #7373
|
|
15
57
|
* feat(document): add $locals property #7691
|
package/lib/aggregate.js
CHANGED
|
@@ -80,14 +80,26 @@ function Aggregate(pipeline) {
|
|
|
80
80
|
Aggregate.prototype.options;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
83
|
+
* Get/set the model that this aggregation will execute on.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* ####Example:
|
|
86
|
+
* const aggregate = MyModel.aggregate([{ $match: { answer: 42 } }]);
|
|
87
|
+
* aggregate.model() === MyModel; // true
|
|
88
|
+
*
|
|
89
|
+
* // Change the model. There's rarely any reason to do this.
|
|
90
|
+
* aggregate.model(SomeOtherModel);
|
|
91
|
+
* aggregate.model() === SomeOtherModel; // true
|
|
92
|
+
*
|
|
93
|
+
* @param {Model} [model] the model to which the aggregate is to be bound
|
|
94
|
+
* @return {Aggregate|Model} if model is passed, will return `this`, otherwise will return the model
|
|
87
95
|
* @api public
|
|
88
96
|
*/
|
|
89
97
|
|
|
90
98
|
Aggregate.prototype.model = function(model) {
|
|
99
|
+
if (arguments.length === 0) {
|
|
100
|
+
return this._model;
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
this._model = model;
|
|
92
104
|
if (model.schema != null) {
|
|
93
105
|
if (this.options.readPreference == null &&
|
|
@@ -790,7 +802,7 @@ Aggregate.prototype.option = function(value) {
|
|
|
790
802
|
* ####Example:
|
|
791
803
|
*
|
|
792
804
|
* var cursor = Model.aggregate(..).cursor({ batchSize: 1000 }).exec();
|
|
793
|
-
* cursor.
|
|
805
|
+
* cursor.eachAsync(function(error, doc) {
|
|
794
806
|
* // use doc
|
|
795
807
|
* });
|
|
796
808
|
*
|
package/lib/connection.js
CHANGED
|
@@ -361,7 +361,13 @@ Connection.prototype.dropCollection = _wrapConnHelper(function dropCollection(co
|
|
|
361
361
|
*/
|
|
362
362
|
|
|
363
363
|
Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) {
|
|
364
|
-
this
|
|
364
|
+
// If `dropDatabase()` is called, this model's collection will not be
|
|
365
|
+
// init-ed. It is sufficiently common to call `dropDatabase()` after
|
|
366
|
+
// `mongoose.connect()` but before creating models that we want to
|
|
367
|
+
// support this. See gh-6967
|
|
368
|
+
for (const name of Object.keys(this.models)) {
|
|
369
|
+
delete this.models[name].$init;
|
|
370
|
+
}
|
|
365
371
|
this.db.dropDatabase(cb);
|
|
366
372
|
});
|
|
367
373
|
|
package/lib/document.js
CHANGED
|
@@ -100,13 +100,15 @@ function Document(obj, fields, skipId, options) {
|
|
|
100
100
|
$__hasIncludedChildren(fields) :
|
|
101
101
|
{};
|
|
102
102
|
|
|
103
|
-
this
|
|
103
|
+
if (this._doc == null) {
|
|
104
|
+
this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren, false);
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
// By default, defaults get applied **before** setting initial values
|
|
107
|
+
// Re: gh-6155
|
|
108
|
+
$__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, true, {
|
|
109
|
+
isNew: this.isNew
|
|
110
|
+
});
|
|
111
|
+
}
|
|
110
112
|
|
|
111
113
|
if (obj) {
|
|
112
114
|
if (obj instanceof Document) {
|
|
@@ -788,6 +790,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
788
790
|
|
|
789
791
|
return this;
|
|
790
792
|
}
|
|
793
|
+
} else {
|
|
794
|
+
this.$__.$setCalled.add(path);
|
|
791
795
|
}
|
|
792
796
|
|
|
793
797
|
function _handleIndex(i) {
|
|
@@ -806,7 +810,8 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
806
810
|
delete this._doc[key];
|
|
807
811
|
}
|
|
808
812
|
|
|
809
|
-
if (
|
|
813
|
+
if (typeof path[key] === 'object' &&
|
|
814
|
+
path[key] != null &&
|
|
810
815
|
pathtype !== 'virtual' &&
|
|
811
816
|
pathtype !== 'real' &&
|
|
812
817
|
!(this.$__path(pathName) instanceof MixedSchema) &&
|
|
@@ -849,7 +854,10 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
849
854
|
}
|
|
850
855
|
}
|
|
851
856
|
|
|
852
|
-
|
|
857
|
+
let pathType = this.schema.pathType(path);
|
|
858
|
+
if (pathType === 'adhocOrUndefined') {
|
|
859
|
+
pathType = getEmbeddedDiscriminatorPath(this, path, { typeOnly: true });
|
|
860
|
+
}
|
|
853
861
|
|
|
854
862
|
// Assume this is a Mongoose document that was copied into a POJO using
|
|
855
863
|
// `Object.assign()` or `{...doc}`
|
|
@@ -858,7 +866,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
858
866
|
}
|
|
859
867
|
|
|
860
868
|
if (pathType === 'nested' && val) {
|
|
861
|
-
if (
|
|
869
|
+
if (typeof val === 'object' && val != null) {
|
|
862
870
|
if (!merge) {
|
|
863
871
|
this.setValue(path, null);
|
|
864
872
|
cleanModifiedSubpaths(this, path);
|
|
@@ -1056,25 +1064,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1056
1064
|
// a single nested doc. That's to make sure we get the correct context.
|
|
1057
1065
|
// Otherwise we would double-call the setter, see gh-7196.
|
|
1058
1066
|
if (this.schema.singleNestedPaths[path] == null) {
|
|
1059
|
-
// Init the new subdoc to the previous values of the doc, so
|
|
1060
|
-
// getters/setters see the correct current state. We pass the new subdoc
|
|
1061
|
-
// instead of the old subdoc because otherwise side effects in setters
|
|
1062
|
-
// wouldn't work, see gh-7585
|
|
1063
|
-
if (constructing && this.$__.$options.priorDoc) {
|
|
1064
|
-
const priorVal = Object.assign({}, this.$__.$options.priorDoc._doc);
|
|
1065
|
-
delete priorVal[this.schema.options.discriminatorKey];
|
|
1066
|
-
init(this, priorVal, this._doc);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
1067
|
val = schema.applySetters(val, this, false, priorVal);
|
|
1070
|
-
|
|
1071
|
-
if (constructing && this.$__.$options.priorDoc) {
|
|
1072
|
-
// Clear init-ed paths afterwards, because those should be paths that
|
|
1073
|
-
// were in the previous doc and should not be in the new one.
|
|
1074
|
-
for (const path of Object.keys(this.$__.activePaths.states.init)) {
|
|
1075
|
-
delete this._doc[path];
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
1068
|
}
|
|
1079
1069
|
|
|
1080
1070
|
if (!didPopulate && this.$__.populated) {
|
|
@@ -1495,6 +1485,8 @@ Document.prototype.directModifiedPaths = function() {
|
|
|
1495
1485
|
* doc.$isEmpty('nested'); // false
|
|
1496
1486
|
* doc.nested.$isEmpty(); // false
|
|
1497
1487
|
*
|
|
1488
|
+
* @memberOf Document
|
|
1489
|
+
* @instance
|
|
1498
1490
|
* @api public
|
|
1499
1491
|
* @method $isEmpty
|
|
1500
1492
|
* @return {Boolean}
|
|
@@ -2555,22 +2547,22 @@ Document.prototype.$__getAllSubdocs = function() {
|
|
|
2555
2547
|
Embedded = Embedded || require('./types/embedded');
|
|
2556
2548
|
|
|
2557
2549
|
function docReducer(doc, seed, path) {
|
|
2558
|
-
|
|
2550
|
+
let val = doc;
|
|
2551
|
+
if (path) {
|
|
2552
|
+
val = doc instanceof Document ? doc._doc[path] : doc[path];
|
|
2553
|
+
}
|
|
2559
2554
|
if (val instanceof Embedded) {
|
|
2560
2555
|
seed.push(val);
|
|
2561
|
-
}
|
|
2562
|
-
else if (val instanceof Map) {
|
|
2556
|
+
} else if (val instanceof Map) {
|
|
2563
2557
|
seed = Array.from(val.keys()).reduce(function(seed, path) {
|
|
2564
2558
|
return docReducer(val.get(path), seed, null);
|
|
2565
2559
|
}, seed);
|
|
2566
|
-
}
|
|
2567
|
-
else if (val && val.$isSingleNested) {
|
|
2560
|
+
} else if (val && val.$isSingleNested) {
|
|
2568
2561
|
seed = Object.keys(val._doc).reduce(function(seed, path) {
|
|
2569
2562
|
return docReducer(val._doc, seed, path);
|
|
2570
2563
|
}, seed);
|
|
2571
2564
|
seed.push(val);
|
|
2572
|
-
}
|
|
2573
|
-
else if (val && val.isMongooseDocumentArray) {
|
|
2565
|
+
} else if (val && val.isMongooseDocumentArray) {
|
|
2574
2566
|
val.forEach(function _docReduce(doc) {
|
|
2575
2567
|
if (!doc || !doc._doc) {
|
|
2576
2568
|
return;
|
|
@@ -3262,7 +3254,6 @@ Document.prototype.populated = function(path, val, options) {
|
|
|
3262
3254
|
}
|
|
3263
3255
|
|
|
3264
3256
|
// internal
|
|
3265
|
-
|
|
3266
3257
|
if (val === true) {
|
|
3267
3258
|
if (!this.$__.populated) {
|
|
3268
3259
|
return undefined;
|
|
@@ -3272,6 +3263,22 @@ Document.prototype.populated = function(path, val, options) {
|
|
|
3272
3263
|
|
|
3273
3264
|
this.$__.populated || (this.$__.populated = {});
|
|
3274
3265
|
this.$__.populated[path] = {value: val, options: options};
|
|
3266
|
+
|
|
3267
|
+
// If this was a nested populate, make sure each populated doc knows
|
|
3268
|
+
// about its populated children (gh-7685)
|
|
3269
|
+
const pieces = path.split('.');
|
|
3270
|
+
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
3271
|
+
const subpath = pieces.slice(0, i + 1).join('.');
|
|
3272
|
+
const subdoc = this.get(subpath);
|
|
3273
|
+
if (subdoc != null && subdoc.$__ != null && this.populated(subpath)) {
|
|
3274
|
+
const rest = pieces.slice(i + 1).join('.');
|
|
3275
|
+
subdoc.populated(rest, val, options);
|
|
3276
|
+
// No need to continue because the above recursion should take care of
|
|
3277
|
+
// marking the rest of the docs as populated
|
|
3278
|
+
break;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3275
3282
|
return val;
|
|
3276
3283
|
};
|
|
3277
3284
|
|
|
@@ -3332,7 +3339,7 @@ Document.prototype.depopulate = function(path) {
|
|
|
3332
3339
|
if (virtualKeys.indexOf(path[i]) !== -1) {
|
|
3333
3340
|
delete this.$$populatedVirtuals[path[i]];
|
|
3334
3341
|
delete this._doc[path[i]];
|
|
3335
|
-
} else {
|
|
3342
|
+
} else if (populatedIds) {
|
|
3336
3343
|
this.$set(path[i], populatedIds);
|
|
3337
3344
|
}
|
|
3338
3345
|
}
|
|
@@ -20,8 +20,9 @@ const util = require('util');
|
|
|
20
20
|
* @api private
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
function NativeCollection() {
|
|
23
|
+
function NativeCollection(name, options) {
|
|
24
24
|
this.collection = null;
|
|
25
|
+
this.Promise = options.Promise || Promise;
|
|
25
26
|
MongooseCollection.apply(this, arguments);
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -115,9 +116,10 @@ const syncCollectionMethods = { watch: true };
|
|
|
115
116
|
function iter(i) {
|
|
116
117
|
NativeCollection.prototype[i] = function() {
|
|
117
118
|
const collection = this.collection;
|
|
118
|
-
const args = arguments;
|
|
119
|
+
const args = Array.from(arguments);
|
|
119
120
|
const _this = this;
|
|
120
121
|
const debug = get(_this, 'conn.base.options.debug');
|
|
122
|
+
const lastArg = arguments[arguments.length - 1];
|
|
121
123
|
|
|
122
124
|
// If user force closed, queueing will hang forever. See #5664
|
|
123
125
|
if (this.opts.$wasForceClosed) {
|
|
@@ -127,8 +129,18 @@ function iter(i) {
|
|
|
127
129
|
if (syncCollectionMethods[i]) {
|
|
128
130
|
throw new Error('Collection method ' + i + ' is synchronous');
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
if (typeof lastArg === 'function') {
|
|
133
|
+
this.addQueue(i, args);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
return new this.Promise((resolve, reject) => {
|
|
137
|
+
this.addQueue(i, [].concat(args).concat([(err, res) => {
|
|
138
|
+
if (err != null) {
|
|
139
|
+
return reject(err);
|
|
140
|
+
}
|
|
141
|
+
resolve(res);
|
|
142
|
+
}]));
|
|
143
|
+
});
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
if (debug) {
|
|
@@ -8,4 +8,4 @@ exports.Binary = require('./binary');
|
|
|
8
8
|
exports.Collection = require('./collection');
|
|
9
9
|
exports.Decimal128 = require('./decimal128');
|
|
10
10
|
exports.ObjectId = require('./objectid');
|
|
11
|
-
exports.ReadPreference = require('./ReadPreference');
|
|
11
|
+
exports.ReadPreference = require('./ReadPreference');
|
|
@@ -9,6 +9,9 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
|
|
|
9
9
|
const skipDocArrays = options.skipDocArrays;
|
|
10
10
|
|
|
11
11
|
let deleted = 0;
|
|
12
|
+
if (!doc) {
|
|
13
|
+
return deleted;
|
|
14
|
+
}
|
|
12
15
|
for (const modifiedPath of Object.keys(doc.$__.activePaths.states.modify)) {
|
|
13
16
|
if (skipDocArrays) {
|
|
14
17
|
const schemaType = doc.schema.path(modifiedPath);
|
|
@@ -18,8 +18,12 @@ module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
|
|
|
18
18
|
const subpath = parts.slice(0, i + 1).join('.');
|
|
19
19
|
schema = doc.schema.path(subpath);
|
|
20
20
|
if (schema == null) {
|
|
21
|
+
type = 'adhocOrUndefined';
|
|
21
22
|
continue;
|
|
22
23
|
}
|
|
24
|
+
if (schema.instance === 'Mixed') {
|
|
25
|
+
return typeOnly ? 'real' : schema;
|
|
26
|
+
}
|
|
23
27
|
type = doc.schema.pathType(subpath);
|
|
24
28
|
if ((schema.$isSingleNested || schema.$isMongooseDocumentArrayElement) &&
|
|
25
29
|
schema.schema.discriminators != null) {
|
|
@@ -30,11 +34,7 @@ module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
|
|
|
30
34
|
continue;
|
|
31
35
|
}
|
|
32
36
|
const rest = parts.slice(i + 1).join('.');
|
|
33
|
-
|
|
34
|
-
if (schema != null) {
|
|
35
|
-
type = discriminators[discriminatorKey].pathType(rest);
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
37
|
+
return getEmbeddedDiscriminatorPath(doc.get(subpath), rest, options);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -7,6 +7,9 @@ module.exports = function applyStaticHooks(model, hooks, statics) {
|
|
|
7
7
|
useErrorHandlers: true,
|
|
8
8
|
numCallbackParams: 1
|
|
9
9
|
};
|
|
10
|
+
|
|
11
|
+
hooks = hooks.filter(hook => hook.model !== false);
|
|
12
|
+
|
|
10
13
|
model.$__insertMany = hooks.createWrapper('insertMany',
|
|
11
14
|
model.$__insertMany, model, kareemOptions);
|
|
12
15
|
|
|
@@ -9,19 +9,19 @@ const hasParentPointers = Symbol('Mongoose.helpers.setParentPointers');
|
|
|
9
9
|
* This is a slow path function, should only run when model is compiled
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
module.exports = function setParentPointers(schema) {
|
|
12
|
+
module.exports = function setParentPointers(schema, parentSchemaType) {
|
|
13
13
|
if (schema[hasParentPointers]) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
schema[hasParentPointers] = true;
|
|
17
17
|
for (const path of Object.keys(schema.paths)) {
|
|
18
18
|
const schemaType = schema.paths[path];
|
|
19
|
-
if (
|
|
20
|
-
Object.defineProperty(schemaType
|
|
19
|
+
if (parentSchemaType != null) {
|
|
20
|
+
Object.defineProperty(schemaType, '$parentSchemaType', {
|
|
21
21
|
configurable: true,
|
|
22
22
|
writable: false,
|
|
23
23
|
enumerable: false,
|
|
24
|
-
value:
|
|
24
|
+
value: parentSchemaType
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
Object.defineProperty(schemaType, '$parentSchema', {
|
|
@@ -35,7 +35,7 @@ module.exports = function setParentPointers(schema) {
|
|
|
35
35
|
for (const path of Object.keys(schema.paths)) {
|
|
36
36
|
const type = schema.paths[path];
|
|
37
37
|
if (type.$isSingleNested || type.$isMongooseDocumentArray) {
|
|
38
|
-
setParentPointers(type.schema,
|
|
38
|
+
setParentPointers(type.schema, type);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
};
|
package/lib/helpers/symbols.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
exports.documentArrayParent = Symbol
|
|
6
|
-
|
|
7
|
-
exports.modelSymbol = Symbol
|
|
8
|
-
|
|
9
|
-
exports.
|
|
10
|
-
|
|
11
|
-
exports.objectIdSymbol = Symbol.for('mongoose#ObjectId');
|
|
12
|
-
|
|
13
|
-
exports.schemaTypeSymbol = Symbol.for('mongoose#schemaType');
|
|
3
|
+
exports.arrayParentSymbol = Symbol('mongoose#Array#_parent');
|
|
4
|
+
exports.arraySchemaSymbol = Symbol('mongoose#Array#_schema');
|
|
5
|
+
exports.documentArrayParent = Symbol('mongoose:documentArrayParent');
|
|
6
|
+
exports.getSymbol = Symbol('mongoose#Document#get');
|
|
7
|
+
exports.modelSymbol = Symbol('mongoose#Model');
|
|
8
|
+
exports.objectIdSymbol = Symbol('mongoose#ObjectId');
|
|
9
|
+
exports.schemaTypeSymbol = Symbol('mongoose#schemaType');
|
|
10
|
+
exports.validatorErrorSymbol = Symbol('mongoose:validatorError');
|
|
@@ -63,18 +63,24 @@ function applyTimestampsToChildren(now, update, schema) {
|
|
|
63
63
|
if (!path) {
|
|
64
64
|
continue;
|
|
65
65
|
}
|
|
66
|
+
|
|
67
|
+
let parentSchemaType = null;
|
|
68
|
+
const pieces = keyToSearch.split('.');
|
|
69
|
+
for (let i = pieces.length - 1; i > 0; --i) {
|
|
70
|
+
const s = schema.path(pieces.slice(0, i).join('.'));
|
|
71
|
+
if (s != null &&
|
|
72
|
+
(s.$isMongooseDocumentArray || s.$isSingleNested)) {
|
|
73
|
+
parentSchemaType = s;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
if (Array.isArray(update.$set[key]) && path.$isMongooseDocumentArray) {
|
|
67
79
|
applyTimestampsToDocumentArray(update.$set[key], path, now);
|
|
68
80
|
} else if (update.$set[key] && path.$isSingleNested) {
|
|
69
81
|
applyTimestampsToSingleNested(update.$set[key], path, now);
|
|
70
|
-
} else if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (parentPath == null) {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
timestamps = parentPath.schema.options.timestamps;
|
|
82
|
+
} else if (parentSchemaType != null) {
|
|
83
|
+
timestamps = parentSchemaType.schema.options.timestamps;
|
|
78
84
|
createdAt = handleTimestampOption(timestamps, 'createdAt');
|
|
79
85
|
updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
|
80
86
|
|
|
@@ -82,23 +88,23 @@ function applyTimestampsToChildren(now, update, schema) {
|
|
|
82
88
|
continue;
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
if (
|
|
91
|
+
if (parentSchemaType.$isSingleNested) {
|
|
86
92
|
// Single nested is easy
|
|
87
|
-
update.$set[
|
|
93
|
+
update.$set[parentSchemaType.path + '.' + updatedAt] = now;
|
|
88
94
|
continue;
|
|
89
95
|
}
|
|
90
96
|
|
|
91
|
-
let childPath = key.substr(
|
|
92
|
-
const firstDot = childPath.indexOf('.');
|
|
97
|
+
let childPath = key.substr(parentSchemaType.path.length + 1);
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
if (/^\d+$/.test(childPath)) {
|
|
100
|
+
update.$set[parentSchemaType.path + '.' + childPath][updatedAt] = now;
|
|
96
101
|
continue;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
|
-
|
|
104
|
+
const firstDot = childPath.indexOf('.');
|
|
105
|
+
childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath;
|
|
100
106
|
|
|
101
|
-
update.$set[
|
|
107
|
+
update.$set[parentSchemaType.path + '.' + childPath + '.' + updatedAt] = now;
|
|
102
108
|
} else if (path.schema != null && path.schema != schema && update.$set[key]) {
|
|
103
109
|
timestamps = path.schema.options.timestamps;
|
|
104
110
|
createdAt = handleTimestampOption(timestamps, 'createdAt');
|