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.
Files changed (37) hide show
  1. package/dist/browser.umd.js +301 -284
  2. package/index.d.ts +138 -108
  3. package/lib/aggregate.js +20 -25
  4. package/lib/cast.js +6 -2
  5. package/lib/connection.js +3 -5
  6. package/lib/document.js +62 -29
  7. package/lib/drivers/node-mongodb-native/collection.js +1 -1
  8. package/lib/error/objectExpected.js +1 -1
  9. package/lib/helpers/document/getEmbeddedDiscriminatorPath.js +15 -12
  10. package/lib/helpers/model/applyMethods.js +2 -1
  11. package/lib/helpers/populate/assignVals.js +3 -0
  12. package/lib/helpers/populate/createPopulateQueryFilter.js +3 -2
  13. package/lib/helpers/populate/lookupLocalFields.js +3 -0
  14. package/lib/helpers/printJestWarning.js +4 -2
  15. package/lib/{plugins → helpers/schema}/idGetter.js +0 -0
  16. package/lib/helpers/setDefaultsOnInsert.js +10 -29
  17. package/lib/helpers/timestamps/setupTimestamps.js +2 -2
  18. package/lib/helpers/update/castArrayFilters.js +17 -5
  19. package/lib/helpers/update/removeUnusedArrayFilters.js +21 -7
  20. package/lib/helpers/update/updatedPathsByArrayFilter.js +3 -0
  21. package/lib/index.js +0 -2
  22. package/lib/internal.js +21 -19
  23. package/lib/model.js +11 -3
  24. package/lib/schema/SubdocumentPath.js +2 -0
  25. package/lib/schema/documentarray.js +2 -0
  26. package/lib/schema/number.js +11 -4
  27. package/lib/schema/objectid.js +1 -2
  28. package/lib/schema.js +16 -2
  29. package/lib/schematype.js +15 -1
  30. package/lib/types/ArraySubdocument.js +13 -13
  31. package/lib/types/array/index.js +1 -1
  32. package/lib/types/map.js +1 -1
  33. package/lib/types/objectid.js +2 -1
  34. package/lib/types/subdocument.js +8 -2
  35. package/lib/utils.js +8 -0
  36. package/package.json +22 -20
  37. 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 && !schematype.caster) {
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.user = options.user;
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
- writable: true
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
- delete this.$__.pathsToScopes[path];
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
- // Sort dirty paths in a flat hierarchy.
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
- if (lastPath == null || item.path.indexOf(lastPath) !== 0) {
3182
- lastPath = item.path + '.';
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.value != null &&
3187
- top.value[arrayAtomicsSymbol] != null &&
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.value[arrayAtomicsSymbol] = {};
3194
- top.value[arrayAtomicsSymbol].$set = top.value;
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
- this.$set(key, populatedIds);
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
- this.$set(singlePath, populatedIds);
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 + '` and strict mode is set to throw.');
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 schema = null;
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
- schema = doc.schema.path(subpath);
20
- if (schema == null) {
22
+ schemaType = schema.path(subpath);
23
+ if (schemaType == null) {
21
24
  type = 'adhocOrUndefined';
22
25
  continue;
23
26
  }
24
- if (schema.instance === 'Mixed') {
25
- return typeOnly ? 'real' : schema;
27
+ if (schemaType.instance === 'Mixed') {
28
+ return typeOnly ? 'real' : schemaType;
26
29
  }
27
- type = doc.schema.pathType(subpath);
28
- if ((schema.$isSingleNested || schema.$isMongooseDocumentArrayElement) &&
29
- schema.schema.discriminators != null) {
30
- const discriminators = schema.schema.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(schema, 'schema.options.discriminatorKey'));
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 : schema;
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
- console.warn(`mongoose: the method name "${method}" is used by mongoose ` +
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 })\`.`);
@@ -46,6 +46,9 @@ module.exports = function assignVals(o) {
46
46
  if (val instanceof SkipPopulateValue) {
47
47
  return val.val;
48
48
  }
49
+ if (val === void 0) {
50
+ return val;
51
+ }
49
52
 
50
53
  const _allIds = o.allIds[i];
51
54
 
@@ -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
  }
@@ -13,6 +13,9 @@ module.exports = function lookupLocalFields(cur, path, val) {
13
13
  if (typeof cur !== 'object') {
14
14
  return void 0;
15
15
  }
16
+ if (val === void 0) {
17
+ return void 0;
18
+ }
16
19
  if (cur instanceof Map) {
17
20
  cur.set(path, val);
18
21
  } else {
@@ -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
- console.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
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
- console.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
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
- const isUnderneathMap = schemaType.path.endsWith('.$*') ||
79
- schemaType.path.indexOf('.$*.') !== -1;
80
- if (schemaType.$isSingleNested && !isUnderneathMap) {
81
- // Only handle nested schemas 1-level deep to avoid infinite
82
- // recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
83
- schemaType.schema.eachPath(function(_path, _schemaType) {
84
- if (_path === '_id' && _schemaType.auto) {
85
- // Ignore _id if auto id so we don't create subdocs
86
- return;
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.get(createdAt) && this.$__isSelected(createdAt)) {
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) continue;
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).map(key => Object.keys(update[key])).reduce((cur, arr) => cur.concat(arr), []);
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
- const firstKey = Object.keys(obj)[0];
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
- this.strictMode = undefined;
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;