mongoose 6.0.4 → 6.0.8
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/dist/browser.umd.js +301 -284
- package/index.d.ts +138 -108
- package/lib/aggregate.js +20 -25
- package/lib/cast.js +6 -2
- package/lib/connection.js +3 -5
- package/lib/document.js +62 -29
- package/lib/drivers/node-mongodb-native/collection.js +1 -1
- package/lib/error/objectExpected.js +1 -1
- package/lib/helpers/document/getEmbeddedDiscriminatorPath.js +15 -12
- package/lib/helpers/model/applyMethods.js +2 -1
- package/lib/helpers/populate/assignVals.js +3 -0
- package/lib/helpers/populate/createPopulateQueryFilter.js +3 -2
- package/lib/helpers/populate/lookupLocalFields.js +3 -0
- package/lib/helpers/printJestWarning.js +4 -2
- package/lib/{plugins → helpers/schema}/idGetter.js +0 -0
- package/lib/helpers/setDefaultsOnInsert.js +10 -29
- package/lib/helpers/timestamps/setupTimestamps.js +2 -2
- package/lib/helpers/update/castArrayFilters.js +17 -5
- package/lib/helpers/update/removeUnusedArrayFilters.js +21 -7
- package/lib/helpers/update/updatedPathsByArrayFilter.js +3 -0
- package/lib/index.js +0 -2
- package/lib/internal.js +21 -19
- package/lib/model.js +11 -3
- package/lib/schema/SubdocumentPath.js +2 -0
- package/lib/schema/documentarray.js +2 -0
- package/lib/schema/number.js +11 -4
- package/lib/schema/objectid.js +1 -2
- package/lib/schema.js +16 -2
- package/lib/schematype.js +15 -1
- package/lib/types/ArraySubdocument.js +13 -13
- package/lib/types/array/index.js +1 -1
- package/lib/types/map.js +1 -1
- package/lib/types/objectid.js +2 -1
- package/lib/types/subdocument.js +8 -2
- package/lib/utils.js +8 -0
- package/package.json +22 -20
- package/tools/repl.js +0 -1
package/lib/cast.js
CHANGED
|
@@ -85,6 +85,11 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
85
85
|
obj[path] = val.toString();
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
continue;
|
|
89
|
+
} else if (path === '$expr') {
|
|
90
|
+
if (typeof val !== 'object' || val == null) {
|
|
91
|
+
throw new Error('`$expr` must be an object');
|
|
92
|
+
}
|
|
88
93
|
continue;
|
|
89
94
|
} else if (path === '$elemMatch') {
|
|
90
95
|
val = cast(schema, val, options, context);
|
|
@@ -288,7 +293,7 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
288
293
|
nested = val[$cond];
|
|
289
294
|
|
|
290
295
|
if ($cond === '$not') {
|
|
291
|
-
if (nested && schematype
|
|
296
|
+
if (nested && schematype) {
|
|
292
297
|
_keys = Object.keys(nested);
|
|
293
298
|
if (_keys.length && isOperator(_keys[0])) {
|
|
294
299
|
for (const key in nested) {
|
|
@@ -307,7 +312,6 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
307
312
|
}
|
|
308
313
|
continue;
|
|
309
314
|
}
|
|
310
|
-
cast(schematype.caster ? schematype.caster.schema : schema, nested, options, context);
|
|
311
315
|
} else {
|
|
312
316
|
val[$cond] = schematype.castForQueryWrapper({
|
|
313
317
|
$conditional: $cond,
|
package/lib/connection.js
CHANGED
|
@@ -562,12 +562,10 @@ function _wrapConnHelper(fn) {
|
|
|
562
562
|
Array.prototype.slice.call(arguments);
|
|
563
563
|
const disconnectedError = new MongooseError('Connection ' + this.id +
|
|
564
564
|
' was disconnected when calling `' + fn.name + '`');
|
|
565
|
+
|
|
565
566
|
return promiseOrCallback(cb, cb => {
|
|
566
|
-
// Make it ok to call collection helpers before `mongoose.connect()`
|
|
567
|
-
// as long as `mongoose.connect()` is called on the same tick.
|
|
568
|
-
// Re: gh-8534
|
|
569
567
|
immediate(() => {
|
|
570
|
-
if (this.readyState === STATES.connecting && this._shouldBufferCommands()) {
|
|
568
|
+
if ((this.readyState === STATES.connecting || this.readyState === STATES.disconnected) && this._shouldBufferCommands()) {
|
|
571
569
|
this._queue.push({ fn: fn, ctx: this, args: argsWithoutCb.concat([cb]) });
|
|
572
570
|
} else if (this.readyState === STATES.disconnected && this.db == null) {
|
|
573
571
|
cb(disconnectedError);
|
|
@@ -744,7 +742,7 @@ Connection.prototype.openUri = function(uri, options, callback) {
|
|
|
744
742
|
// Backwards compat
|
|
745
743
|
if (options.user || options.pass) {
|
|
746
744
|
options.auth = options.auth || {};
|
|
747
|
-
options.auth.
|
|
745
|
+
options.auth.username = options.user;
|
|
748
746
|
options.auth.password = options.pass;
|
|
749
747
|
|
|
750
748
|
this.user = options.user;
|
package/lib/document.js
CHANGED
|
@@ -51,7 +51,7 @@ const getSymbol = require('./helpers/symbols').getSymbol;
|
|
|
51
51
|
const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
|
|
52
52
|
const scopeSymbol = require('./helpers/symbols').scopeSymbol;
|
|
53
53
|
const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
|
|
54
|
-
|
|
54
|
+
const parentPaths = require('./helpers/path/parentPaths');
|
|
55
55
|
let DocumentArray;
|
|
56
56
|
let MongooseArray;
|
|
57
57
|
let Embedded;
|
|
@@ -94,9 +94,7 @@ function Document(obj, fields, skipId, options) {
|
|
|
94
94
|
this.$__ = new InternalCache;
|
|
95
95
|
this.$__.emitter = new EventEmitter();
|
|
96
96
|
this.$isNew = 'isNew' in options ? options.isNew : true;
|
|
97
|
-
this.$errors = undefined;
|
|
98
97
|
this.$__.$options = options || {};
|
|
99
|
-
this.$locals = {};
|
|
100
98
|
this.$op = null;
|
|
101
99
|
if (obj != null && typeof obj !== 'object') {
|
|
102
100
|
throw new ObjectParameterError(obj, 'obj', 'Document');
|
|
@@ -271,7 +269,15 @@ Document.prototype.schema;
|
|
|
271
269
|
Object.defineProperty(Document.prototype, '$locals', {
|
|
272
270
|
configurable: false,
|
|
273
271
|
enumerable: false,
|
|
274
|
-
|
|
272
|
+
get: function() {
|
|
273
|
+
if (this.$__.locals == null) {
|
|
274
|
+
this.$__.locals = {};
|
|
275
|
+
}
|
|
276
|
+
return this.$__.locals;
|
|
277
|
+
},
|
|
278
|
+
set: function(v) {
|
|
279
|
+
this.$__.locals = v;
|
|
280
|
+
}
|
|
275
281
|
});
|
|
276
282
|
|
|
277
283
|
|
|
@@ -1800,6 +1806,7 @@ Document.prototype.$__path = function(path) {
|
|
|
1800
1806
|
Document.prototype.markModified = function(path, scope) {
|
|
1801
1807
|
this.$__.activePaths.modify(path);
|
|
1802
1808
|
if (scope != null && !this.ownerDocument) {
|
|
1809
|
+
this.$__.pathsToScopes = this.$__pathsToScopes || {};
|
|
1803
1810
|
this.$__.pathsToScopes[path] = scope;
|
|
1804
1811
|
}
|
|
1805
1812
|
};
|
|
@@ -1819,7 +1826,9 @@ Document.prototype.markModified = function(path, scope) {
|
|
|
1819
1826
|
|
|
1820
1827
|
Document.prototype.unmarkModified = function(path) {
|
|
1821
1828
|
this.$__.activePaths.init(path);
|
|
1822
|
-
|
|
1829
|
+
if (this.$__.pathsToScopes != null) {
|
|
1830
|
+
delete this.$__.pathsToScopes[path];
|
|
1831
|
+
}
|
|
1823
1832
|
};
|
|
1824
1833
|
|
|
1825
1834
|
/**
|
|
@@ -2382,6 +2391,7 @@ function _evaluateRequiredFunctions(doc) {
|
|
|
2382
2391
|
const p = doc.$__schema.path(path);
|
|
2383
2392
|
|
|
2384
2393
|
if (p != null && typeof p.originalRequiredValue === 'function') {
|
|
2394
|
+
doc.$__.cachedRequired = doc.$__.cachedRequired || {};
|
|
2385
2395
|
doc.$__.cachedRequired[path] = p.originalRequiredValue.call(doc, doc);
|
|
2386
2396
|
}
|
|
2387
2397
|
});
|
|
@@ -2400,7 +2410,7 @@ function _getPathsToValidate(doc) {
|
|
|
2400
2410
|
if (!doc.$__isSelected(path) && !doc.$isModified(path)) {
|
|
2401
2411
|
return false;
|
|
2402
2412
|
}
|
|
2403
|
-
if (path in doc.$__.cachedRequired) {
|
|
2413
|
+
if (doc.$__.cachedRequired != null && path in doc.$__.cachedRequired) {
|
|
2404
2414
|
return doc.$__.cachedRequired[path];
|
|
2405
2415
|
}
|
|
2406
2416
|
return true;
|
|
@@ -2639,7 +2649,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
|
|
|
2639
2649
|
// so in that case pull out the document's id
|
|
2640
2650
|
val = val._id;
|
|
2641
2651
|
}
|
|
2642
|
-
const scope = path in _this.$__.pathsToScopes ?
|
|
2652
|
+
const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ?
|
|
2643
2653
|
_this.$__.pathsToScopes[path] :
|
|
2644
2654
|
_this;
|
|
2645
2655
|
|
|
@@ -3164,37 +3174,37 @@ Document.prototype.$__dirty = function() {
|
|
|
3164
3174
|
};
|
|
3165
3175
|
}));
|
|
3166
3176
|
|
|
3167
|
-
|
|
3168
|
-
all.sort(function(a, b) {
|
|
3169
|
-
return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
|
|
3170
|
-
});
|
|
3171
|
-
|
|
3177
|
+
const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value]));
|
|
3172
3178
|
// Ignore "foo.a" if "foo" is dirty already.
|
|
3173
3179
|
const minimal = [];
|
|
3174
|
-
let lastPath;
|
|
3175
|
-
let top;
|
|
3176
3180
|
|
|
3177
3181
|
all.forEach(function(item) {
|
|
3178
3182
|
if (!item) {
|
|
3179
3183
|
return;
|
|
3180
3184
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3185
|
+
|
|
3186
|
+
let top = null;
|
|
3187
|
+
|
|
3188
|
+
const array = parentPaths(item.path);
|
|
3189
|
+
for (let i = 0; i < array.length - 1; i++) {
|
|
3190
|
+
if (allPaths.has(array[i])) {
|
|
3191
|
+
top = allPaths.get(array[i]);
|
|
3192
|
+
break;
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
if (top == null) {
|
|
3183
3196
|
minimal.push(item);
|
|
3184
|
-
top = item;
|
|
3185
3197
|
} else if (top != null &&
|
|
3186
|
-
top
|
|
3187
|
-
top.
|
|
3188
|
-
top.value.hasAtomics()) {
|
|
3198
|
+
top[arrayAtomicsSymbol] != null &&
|
|
3199
|
+
top.hasAtomics()) {
|
|
3189
3200
|
// special case for top level MongooseArrays
|
|
3190
3201
|
// the `top` array itself and a sub path of `top` are being set.
|
|
3191
3202
|
// the only way to honor all of both modifications is through a $set
|
|
3192
3203
|
// of entire array.
|
|
3193
|
-
top
|
|
3194
|
-
top
|
|
3204
|
+
top[arrayAtomicsSymbol] = {};
|
|
3205
|
+
top[arrayAtomicsSymbol].$set = top;
|
|
3195
3206
|
}
|
|
3196
3207
|
});
|
|
3197
|
-
top = lastPath = null;
|
|
3198
3208
|
return minimal;
|
|
3199
3209
|
};
|
|
3200
3210
|
|
|
@@ -4022,13 +4032,33 @@ Document.prototype.equals = function(doc) {
|
|
|
4022
4032
|
* doc.stories[0].title; // 'Casino Royale'
|
|
4023
4033
|
* doc.populated('fans'); // Array of ObjectIds
|
|
4024
4034
|
*
|
|
4035
|
+
* await doc.populate('fans', '-email');
|
|
4036
|
+
* doc.fans[0].email // not populated
|
|
4037
|
+
*
|
|
4038
|
+
* await doc.populate('author fans', '-email');
|
|
4039
|
+
* doc.author.email // not populated
|
|
4040
|
+
* doc.fans[0].email // not populated
|
|
4041
|
+
*
|
|
4042
|
+
* @param {String|Object|Array} path either the path to populate or an object specifying all parameters, or either an array of those
|
|
4043
|
+
* @param {Object|String} [select] Field selection for the population query
|
|
4044
|
+
* @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
|
|
4045
|
+
* @param {Object} [match] Conditions for the population query
|
|
4046
|
+
* @param {Object} [options] Options for the population query (sort, etc)
|
|
4047
|
+
* @param {String} [options.path=null] The path to populate.
|
|
4048
|
+
* @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
|
|
4049
|
+
* @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
|
|
4050
|
+
* @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
|
|
4051
|
+
* @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://docs.mongodb.com/manual/tutorial/query-documents/), or a function that returns a filter object.
|
|
4052
|
+
* @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
|
|
4053
|
+
* @param {Object} [options.options=null] Additional options like `limit` and `lean`.
|
|
4054
|
+
* @param {Function} [callback] Callback
|
|
4055
|
+
* @see population ./populate.html
|
|
4056
|
+
* @see Query#select #query_Query-select
|
|
4025
4057
|
* @see Model.populate #model_Model.populate
|
|
4026
|
-
* @param {String|Object|Array} [path] The path to populate or an options object or array of paths and/or options objects.
|
|
4027
|
-
* @param {Function} [callback] When passed, population is invoked
|
|
4028
|
-
* @api public
|
|
4029
|
-
* @return {Promise|null} this
|
|
4030
4058
|
* @memberOf Document
|
|
4031
4059
|
* @instance
|
|
4060
|
+
* @return {Promise|null}
|
|
4061
|
+
* @api public
|
|
4032
4062
|
*/
|
|
4033
4063
|
|
|
4034
4064
|
Document.prototype.populate = function populate() {
|
|
@@ -4130,6 +4160,9 @@ Document.prototype.populated = function(path, val, options) {
|
|
|
4130
4160
|
if (!this.$__.populated) {
|
|
4131
4161
|
return undefined;
|
|
4132
4162
|
}
|
|
4163
|
+
if (typeof path !== 'string') {
|
|
4164
|
+
return undefined;
|
|
4165
|
+
}
|
|
4133
4166
|
|
|
4134
4167
|
// Map paths can be populated with either `path.$*` or just `path`
|
|
4135
4168
|
const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path;
|
|
@@ -4210,7 +4243,7 @@ Document.prototype.depopulate = function(path) {
|
|
|
4210
4243
|
continue;
|
|
4211
4244
|
}
|
|
4212
4245
|
delete populated[key];
|
|
4213
|
-
|
|
4246
|
+
utils.setValue(key, populatedIds, this._doc);
|
|
4214
4247
|
}
|
|
4215
4248
|
return this;
|
|
4216
4249
|
}
|
|
@@ -4223,7 +4256,7 @@ Document.prototype.depopulate = function(path) {
|
|
|
4223
4256
|
delete this.$$populatedVirtuals[singlePath];
|
|
4224
4257
|
delete this._doc[singlePath];
|
|
4225
4258
|
} else if (populatedIds) {
|
|
4226
|
-
|
|
4259
|
+
utils.setValue(singlePath, populatedIds, this._doc);
|
|
4227
4260
|
}
|
|
4228
4261
|
}
|
|
4229
4262
|
return this;
|
|
@@ -66,7 +66,7 @@ NativeCollection.prototype.onClose = function(force) {
|
|
|
66
66
|
* ignore
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
|
-
const syncCollectionMethods = { watch: true, find: true };
|
|
69
|
+
const syncCollectionMethods = { watch: true, find: true, aggregate: true };
|
|
70
70
|
|
|
71
71
|
/*!
|
|
72
72
|
* Copy the collection methods and make them subject to queues
|
|
@@ -18,7 +18,7 @@ class ObjectExpectedError extends MongooseError {
|
|
|
18
18
|
constructor(path, val) {
|
|
19
19
|
const typeDescription = Array.isArray(val) ? 'array' : 'primitive value';
|
|
20
20
|
super('Tried to set nested object field `' + path +
|
|
21
|
-
`\` to ${typeDescription} \`` + val + '`
|
|
21
|
+
`\` to ${typeDescription} \`` + val + '`');
|
|
22
22
|
this.path = path;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const get = require('../get');
|
|
4
|
+
const getSchemaDiscriminatorByValue = require('../discriminator/getSchemaDiscriminatorByValue');
|
|
4
5
|
|
|
5
6
|
/*!
|
|
6
7
|
* Like `schema.path()`, except with a document, because impossible to
|
|
@@ -10,26 +11,28 @@ const get = require('../get');
|
|
|
10
11
|
module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
|
|
11
12
|
options = options || {};
|
|
12
13
|
const typeOnly = options.typeOnly;
|
|
13
|
-
const parts = path.split('.');
|
|
14
|
-
let
|
|
14
|
+
const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
15
|
+
let schemaType = null;
|
|
15
16
|
let type = 'adhocOrUndefined';
|
|
16
17
|
|
|
18
|
+
const schema = getSchemaDiscriminatorByValue(doc.schema, doc.get(doc.schema.options.discriminatorKey)) || doc.schema;
|
|
19
|
+
|
|
17
20
|
for (let i = 0; i < parts.length; ++i) {
|
|
18
21
|
const subpath = parts.slice(0, i + 1).join('.');
|
|
19
|
-
|
|
20
|
-
if (
|
|
22
|
+
schemaType = schema.path(subpath);
|
|
23
|
+
if (schemaType == null) {
|
|
21
24
|
type = 'adhocOrUndefined';
|
|
22
25
|
continue;
|
|
23
26
|
}
|
|
24
|
-
if (
|
|
25
|
-
return typeOnly ? 'real' :
|
|
27
|
+
if (schemaType.instance === 'Mixed') {
|
|
28
|
+
return typeOnly ? 'real' : schemaType;
|
|
26
29
|
}
|
|
27
|
-
type =
|
|
28
|
-
if ((
|
|
29
|
-
|
|
30
|
-
const discriminators =
|
|
30
|
+
type = schema.pathType(subpath);
|
|
31
|
+
if ((schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) &&
|
|
32
|
+
schemaType.schema.discriminators != null) {
|
|
33
|
+
const discriminators = schemaType.schema.discriminators;
|
|
31
34
|
const discriminatorKey = doc.get(subpath + '.' +
|
|
32
|
-
get(
|
|
35
|
+
get(schemaType, 'schema.options.discriminatorKey'));
|
|
33
36
|
if (discriminatorKey == null || discriminators[discriminatorKey] == null) {
|
|
34
37
|
continue;
|
|
35
38
|
}
|
|
@@ -39,5 +42,5 @@ module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
// Are we getting the whole schema or just the type, 'real', 'nested', etc.
|
|
42
|
-
return typeOnly ? type :
|
|
45
|
+
return typeOnly ? type : schemaType;
|
|
43
46
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const get = require('../get');
|
|
4
|
+
const utils = require('../../utils');
|
|
4
5
|
|
|
5
6
|
/*!
|
|
6
7
|
* Register methods for this model
|
|
@@ -30,7 +31,7 @@ module.exports = function applyMethods(model, schema) {
|
|
|
30
31
|
}
|
|
31
32
|
if (schema.reserved[method] &&
|
|
32
33
|
!get(schema, `methodOptions.${method}.suppressWarning`, false)) {
|
|
33
|
-
|
|
34
|
+
utils.warn(`mongoose: the method name "${method}" is used by mongoose ` +
|
|
34
35
|
'internally, overwriting it may cause bugs. If you\'re sure you know ' +
|
|
35
36
|
'what you\'re doing, you can suppress this error by using ' +
|
|
36
37
|
`\`schema.method('${method}', fn, { suppressWarning: true })\`.`);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const SkipPopulateValue = require('./SkipPopulateValue');
|
|
4
4
|
const parentPaths = require('../path/parentPaths');
|
|
5
|
+
const { trusted } = require('../query/trusted');
|
|
5
6
|
|
|
6
7
|
module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) {
|
|
7
8
|
const match = _formatMatch(_match);
|
|
@@ -11,14 +12,14 @@ module.exports = function createPopulateQueryFilter(ids, _match, _foreignField,
|
|
|
11
12
|
const foreignSchemaType = model.schema.path(foreignField);
|
|
12
13
|
if (foreignField !== '_id' || !match['_id']) {
|
|
13
14
|
ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
|
|
14
|
-
match[foreignField] = { $in: ids };
|
|
15
|
+
match[foreignField] = trusted({ $in: ids });
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const _parentPaths = parentPaths(foreignField);
|
|
18
19
|
for (let i = 0; i < _parentPaths.length - 1; ++i) {
|
|
19
20
|
const cur = _parentPaths[i];
|
|
20
21
|
if (match[cur] != null && match[cur].$elemMatch != null) {
|
|
21
|
-
match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = { $in: ids };
|
|
22
|
+
match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = trusted({ $in: ids });
|
|
22
23
|
delete match[foreignField];
|
|
23
24
|
break;
|
|
24
25
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const utils = require('../utils');
|
|
4
|
+
|
|
3
5
|
if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
|
|
4
|
-
|
|
6
|
+
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
|
5
7
|
'with Jest\'s default jsdom test environment. Please make sure you read ' +
|
|
6
8
|
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
|
7
9
|
'http://mongoosejs.com/docs/jest.html');
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
if (typeof jest !== 'undefined' && process.nextTick.toString().indexOf('nextTick') === -1) {
|
|
11
|
-
|
|
13
|
+
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
|
12
14
|
'with Jest\'s mock timers enabled. Please make sure you read ' +
|
|
13
15
|
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
|
14
16
|
'http://mongoosejs.com/docs/jest.html');
|
|
File without changes
|
|
@@ -75,36 +75,17 @@ module.exports = function(filter, schema, castedDoc, options) {
|
|
|
75
75
|
|
|
76
76
|
schema.eachPath(function(path, schemaType) {
|
|
77
77
|
// Skip single nested paths if underneath a map
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const def = _schemaType.getDefault(null, true);
|
|
90
|
-
if (!isModified(modified, path + '.' + _path) &&
|
|
91
|
-
typeof def !== 'undefined') {
|
|
92
|
-
castedDoc = castedDoc || {};
|
|
93
|
-
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
|
94
|
-
castedDoc.$setOnInsert[path + '.' + _path] = def;
|
|
95
|
-
updatedValues[path + '.' + _path] = def;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
} else {
|
|
99
|
-
const def = schemaType.getDefault(null, true);
|
|
100
|
-
if (!isModified(modified, path) && typeof def !== 'undefined') {
|
|
101
|
-
castedDoc = castedDoc || {};
|
|
102
|
-
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
|
103
|
-
if (get(castedDoc, path) == null) {
|
|
104
|
-
castedDoc.$setOnInsert[path] = def;
|
|
105
|
-
}
|
|
106
|
-
updatedValues[path] = def;
|
|
78
|
+
if (schemaType.path === '_id' && schemaType.options.auto) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const def = schemaType.getDefault(null, true);
|
|
82
|
+
if (!isModified(modified, path) && typeof def !== 'undefined') {
|
|
83
|
+
castedDoc = castedDoc || {};
|
|
84
|
+
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
|
85
|
+
if (get(castedDoc, path) == null) {
|
|
86
|
+
castedDoc.$setOnInsert[path] = def;
|
|
107
87
|
}
|
|
88
|
+
updatedValues[path] = def;
|
|
108
89
|
}
|
|
109
90
|
});
|
|
110
91
|
|
|
@@ -31,7 +31,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
if (createdAt && !schema.paths[createdAt]) {
|
|
34
|
-
schemaAdditions[createdAt] = { type: Date, immutable: true };
|
|
34
|
+
schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable: true };
|
|
35
35
|
}
|
|
36
36
|
schema.add(schemaAdditions);
|
|
37
37
|
|
|
@@ -49,7 +49,7 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
49
49
|
(this.ownerDocument ? this.ownerDocument() : this).constructor.base.now();
|
|
50
50
|
const auto_id = this._id && this._id.auto;
|
|
51
51
|
|
|
52
|
-
if (!skipCreatedAt && createdAt && !this
|
|
52
|
+
if (!skipCreatedAt && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
|
|
53
53
|
this.$set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp);
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -7,25 +7,37 @@ const updatedPathsByArrayFilter = require('./updatedPathsByArrayFilter');
|
|
|
7
7
|
|
|
8
8
|
module.exports = function castArrayFilters(query) {
|
|
9
9
|
const arrayFilters = query.options.arrayFilters;
|
|
10
|
-
if (!Array.isArray(arrayFilters)) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
10
|
const update = query.getUpdate();
|
|
14
11
|
const schema = query.schema;
|
|
15
12
|
const strict = schema.options.strict;
|
|
16
13
|
const updatedPathsByFilter = updatedPathsByArrayFilter(update);
|
|
14
|
+
|
|
15
|
+
_castArrayFilters(arrayFilters, schema, strict, updatedPathsByFilter, query);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function _castArrayFilters(arrayFilters, schema, strict, updatedPathsByFilter, query) {
|
|
19
|
+
if (!Array.isArray(arrayFilters)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
for (const filter of arrayFilters) {
|
|
18
24
|
if (filter == null) {
|
|
19
25
|
throw new Error(`Got null array filter in ${arrayFilters}`);
|
|
20
26
|
}
|
|
21
27
|
for (const key of Object.keys(filter)) {
|
|
28
|
+
if (key === '$and' || key === '$or') {
|
|
29
|
+
_castArrayFilters(filter[key], schema, strict, updatedPathsByFilter, query);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
22
32
|
if (filter[key] == null) {
|
|
23
33
|
continue;
|
|
24
34
|
}
|
|
25
35
|
if (updatedPathsByFilter[key] === null) {
|
|
26
36
|
continue;
|
|
27
37
|
}
|
|
28
|
-
if (Object.keys(updatedPathsByFilter).length === 0)
|
|
38
|
+
if (Object.keys(updatedPathsByFilter).length === 0) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
29
41
|
const dot = key.indexOf('.');
|
|
30
42
|
let filterPath = dot === -1 ?
|
|
31
43
|
updatedPathsByFilter[key] + '.0' :
|
|
@@ -55,4 +67,4 @@ module.exports = function castArrayFilters(query) {
|
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
|
-
}
|
|
70
|
+
}
|
|
@@ -7,12 +7,26 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
module.exports = function removeUnusedArrayFilters(update, arrayFilters) {
|
|
10
|
-
const updateKeys = Object.keys(update).
|
|
10
|
+
const updateKeys = Object.keys(update).
|
|
11
|
+
map(key => Object.keys(update[key])).
|
|
12
|
+
reduce((cur, arr) => cur.concat(arr), []);
|
|
11
13
|
return arrayFilters.filter(obj => {
|
|
12
|
-
|
|
13
|
-
const firstDot = firstKey.indexOf('.');
|
|
14
|
-
const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot);
|
|
15
|
-
|
|
16
|
-
return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null;
|
|
14
|
+
return _checkSingleFilterKey(obj, updateKeys);
|
|
17
15
|
});
|
|
18
|
-
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function _checkSingleFilterKey(arrayFilter, updateKeys) {
|
|
19
|
+
const firstKey = Object.keys(arrayFilter)[0];
|
|
20
|
+
|
|
21
|
+
if (firstKey === '$and' || firstKey === '$or') {
|
|
22
|
+
if (!Array.isArray(arrayFilter[firstKey])) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return arrayFilter[firstKey].find(filter => _checkSingleFilterKey(filter, updateKeys)) != null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const firstDot = firstKey.indexOf('.');
|
|
29
|
+
const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot);
|
|
30
|
+
|
|
31
|
+
return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null;
|
|
32
|
+
}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const modifiedPaths = require('./modifiedPaths');
|
|
4
4
|
|
|
5
5
|
module.exports = function updatedPathsByArrayFilter(update) {
|
|
6
|
+
if (update == null) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
6
9
|
const updatedPaths = modifiedPaths(update);
|
|
7
10
|
|
|
8
11
|
return Object.keys(updatedPaths).reduce((cur, path) => {
|
package/lib/index.js
CHANGED
|
@@ -25,7 +25,6 @@ const legacyPluralize = require('./helpers/pluralize');
|
|
|
25
25
|
const utils = require('./utils');
|
|
26
26
|
const pkg = require('../package.json');
|
|
27
27
|
const cast = require('./cast');
|
|
28
|
-
const idGetter = require('./plugins/idGetter');
|
|
29
28
|
const removeSubdocs = require('./plugins/removeSubdocs');
|
|
30
29
|
const saveSubdocs = require('./plugins/saveSubdocs');
|
|
31
30
|
const trackTransaction = require('./plugins/trackTransaction');
|
|
@@ -102,7 +101,6 @@ function Mongoose(options) {
|
|
|
102
101
|
enumerable: true,
|
|
103
102
|
writable: false,
|
|
104
103
|
value: [
|
|
105
|
-
[idGetter, { deduplicate: true }],
|
|
106
104
|
[saveSubdocs, { deduplicate: true }],
|
|
107
105
|
[validateBeforeSave, { deduplicate: true }],
|
|
108
106
|
[shardingPlugin, { deduplicate: true }],
|
package/lib/internal.js
CHANGED
|
@@ -10,28 +10,30 @@ const ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default', '
|
|
|
10
10
|
module.exports = exports = InternalCache;
|
|
11
11
|
|
|
12
12
|
function InternalCache() {
|
|
13
|
-
|
|
14
|
-
this.selected = undefined;
|
|
15
|
-
this.shardval = undefined;
|
|
16
|
-
this.saveError = undefined;
|
|
17
|
-
this.validationError = undefined;
|
|
18
|
-
this.adhocPaths = undefined;
|
|
19
|
-
this.removing = undefined;
|
|
20
|
-
this.inserting = undefined;
|
|
21
|
-
this.saving = undefined;
|
|
22
|
-
this.version = undefined;
|
|
23
|
-
this.getters = {};
|
|
24
|
-
this._id = undefined;
|
|
25
|
-
this.populate = undefined; // what we want to populate in this doc
|
|
26
|
-
this.populated = undefined;// the _ids that have been populated
|
|
27
|
-
this.wasPopulated = false; // if this doc was the result of a population
|
|
28
|
-
this.scope = undefined;
|
|
13
|
+
|
|
29
14
|
this.activePaths = new ActiveRoster;
|
|
30
|
-
this.pathsToScopes = {};
|
|
31
|
-
this.cachedRequired = {};
|
|
32
|
-
this.session = null;
|
|
33
15
|
|
|
34
16
|
// embedded docs
|
|
35
17
|
this.ownerDocument = undefined;
|
|
36
18
|
this.fullPath = undefined;
|
|
37
19
|
}
|
|
20
|
+
|
|
21
|
+
InternalCache.prototype.strictMode = undefined;
|
|
22
|
+
InternalCache.prototype.selected = undefined;
|
|
23
|
+
InternalCache.prototype.shardval = undefined;
|
|
24
|
+
InternalCache.prototype.saveError = undefined;
|
|
25
|
+
InternalCache.prototype.validationError = undefined;
|
|
26
|
+
InternalCache.prototype.adhocPaths = undefined;
|
|
27
|
+
InternalCache.prototype.removing = undefined;
|
|
28
|
+
InternalCache.prototype.inserting = undefined;
|
|
29
|
+
InternalCache.prototype.saving = undefined;
|
|
30
|
+
InternalCache.prototype.version = undefined;
|
|
31
|
+
InternalCache.prototype._id = undefined;
|
|
32
|
+
InternalCache.prototype.populate = undefined; // what we want to populate in this doc
|
|
33
|
+
InternalCache.prototype.populated = undefined;// the _ids that have been populated
|
|
34
|
+
InternalCache.prototype.wasPopulated = false; // if this doc was the result of a population
|
|
35
|
+
InternalCache.prototype.scope = undefined;
|
|
36
|
+
|
|
37
|
+
InternalCache.prototype.session = null;
|
|
38
|
+
InternalCache.prototype.pathsToScopes = null;
|
|
39
|
+
InternalCache.prototype.cachedRequired = null;
|