mongoose 6.1.8 → 6.2.1

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 (55) hide show
  1. package/.eslintrc.json +150 -0
  2. package/CHANGELOG.md +50 -0
  3. package/dist/browser.umd.js +191 -187
  4. package/lib/aggregate.js +1 -1
  5. package/lib/cast/objectid.js +1 -2
  6. package/lib/cast.js +21 -8
  7. package/lib/connection.js +33 -3
  8. package/lib/document.js +84 -65
  9. package/lib/error/index.js +11 -0
  10. package/lib/error/syncIndexes.js +30 -0
  11. package/lib/helpers/clone.js +40 -27
  12. package/lib/helpers/common.js +2 -2
  13. package/lib/helpers/getFunctionName.js +6 -4
  14. package/lib/helpers/isMongooseObject.js +9 -8
  15. package/lib/helpers/isObject.js +4 -4
  16. package/lib/helpers/model/discriminator.js +2 -1
  17. package/lib/helpers/path/parentPaths.js +10 -5
  18. package/lib/helpers/populate/assignRawDocsToIdStructure.js +4 -2
  19. package/lib/helpers/populate/assignVals.js +8 -4
  20. package/lib/helpers/populate/getModelsMapForPopulate.js +6 -6
  21. package/lib/helpers/populate/markArraySubdocsPopulated.js +3 -1
  22. package/lib/helpers/populate/modelNamesFromRefPath.js +6 -5
  23. package/lib/helpers/query/cast$expr.js +284 -0
  24. package/lib/helpers/query/castUpdate.js +6 -2
  25. package/lib/helpers/schema/applyPlugins.js +11 -0
  26. package/lib/helpers/schema/getPath.js +4 -2
  27. package/lib/helpers/setDefaultsOnInsert.js +16 -7
  28. package/lib/helpers/timestamps/setupTimestamps.js +3 -8
  29. package/lib/helpers/update/applyTimestampsToChildren.js +2 -2
  30. package/lib/helpers/update/castArrayFilters.js +1 -1
  31. package/lib/helpers/update/updatedPathsByArrayFilter.js +1 -1
  32. package/lib/index.js +16 -40
  33. package/lib/internal.js +1 -1
  34. package/lib/model.js +37 -18
  35. package/lib/plugins/trackTransaction.js +4 -3
  36. package/lib/query.js +15 -13
  37. package/lib/queryhelpers.js +1 -1
  38. package/lib/schema/SubdocumentPath.js +1 -1
  39. package/lib/schema/array.js +18 -16
  40. package/lib/schema/documentarray.js +6 -9
  41. package/lib/schema/objectid.js +1 -1
  42. package/lib/schema.js +5 -4
  43. package/lib/schematype.js +74 -26
  44. package/lib/types/ArraySubdocument.js +2 -1
  45. package/lib/types/DocumentArray/index.js +9 -26
  46. package/lib/types/DocumentArray/isMongooseDocumentArray.js +5 -0
  47. package/lib/types/DocumentArray/methods/index.js +15 -3
  48. package/lib/types/array/index.js +21 -20
  49. package/lib/types/array/isMongooseArray.js +5 -0
  50. package/lib/types/array/methods/index.js +12 -12
  51. package/lib/utils.js +15 -8
  52. package/package.json +24 -156
  53. package/tools/repl.js +1 -1
  54. package/tsconfig.json +10 -0
  55. package/{index.d.ts → types/index.d.ts} +146 -86
@@ -153,7 +153,7 @@ function applyTimestampsToUpdateKey(schema, key, update, now) {
153
153
  // Single nested is easy
154
154
  update[parentPath + '.' + updatedAt] = now;
155
155
  } else if (parentSchemaType.$isMongooseDocumentArray) {
156
- let childPath = key.substr(parentPath.length + 1);
156
+ let childPath = key.substring(parentPath.length + 1);
157
157
 
158
158
  if (/^\d+$/.test(childPath)) {
159
159
  update[parentPath + '.' + childPath][updatedAt] = now;
@@ -161,7 +161,7 @@ function applyTimestampsToUpdateKey(schema, key, update, now) {
161
161
  }
162
162
 
163
163
  const firstDot = childPath.indexOf('.');
164
- childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath;
164
+ childPath = firstDot !== -1 ? childPath.substring(0, firstDot) : childPath;
165
165
 
166
166
  update[parentPath + '.' + childPath + '.' + updatedAt] = now;
167
167
  }
@@ -51,7 +51,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
51
51
  const dot = key.indexOf('.');
52
52
  let filterPath = dot === -1 ?
53
53
  updatedPathsByFilter[key] + '.0' :
54
- updatedPathsByFilter[key.substr(0, dot)] + '.0' + key.substr(dot);
54
+ updatedPathsByFilter[key.substring(0, dot)] + '.0' + key.substring(dot);
55
55
  if (filterPath == null) {
56
56
  throw new Error(`Filter path not found for ${key}`);
57
57
  }
@@ -19,7 +19,7 @@ module.exports = function updatedPathsByArrayFilter(update) {
19
19
  throw new Error(`Path '${path}' contains the same array filter multiple times`);
20
20
  }
21
21
  cur[match.substring(2, match.length - 1)] = path.
22
- substr(0, firstMatch - 1).
22
+ substring(0, firstMatch - 1).
23
23
  replace(/\$\[[^\]]+\]/g, '0');
24
24
  }
25
25
  return cur;
package/lib/index.js CHANGED
@@ -947,55 +947,31 @@ Mongoose.prototype.ObjectId = SchemaTypes.ObjectId;
947
947
  *
948
948
  * mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
949
949
  * mongoose.isValidObjectId('0123456789ab'); // true
950
- * mongoose.isValidObjectId(6); // false
950
+ * mongoose.isValidObjectId(6); // true
951
+ * mongoose.isValidObjectId({ test: 42 }); // false
951
952
  *
952
953
  * @method isValidObjectId
954
+ * @param {Any} value
955
+ * @returns {boolean} true if it is a valid ObjectId
953
956
  * @api public
954
957
  */
955
958
 
956
959
  Mongoose.prototype.isValidObjectId = function(v) {
957
- if (v == null) {
958
- return true;
959
- }
960
- const base = this || mongoose;
961
- const ObjectId = base.driver.get().ObjectId;
962
- if (v instanceof ObjectId) {
963
- return true;
964
- }
965
-
966
- if (v._id != null) {
967
- if (v._id instanceof ObjectId) {
968
- return true;
969
- }
970
- if (v._id.toString instanceof Function) {
971
- v = v._id.toString();
972
- if (typeof v === 'string' && v.length === 12) {
973
- return true;
974
- }
975
- if (typeof v === 'string' && /^[0-9A-Fa-f]{24}$/.test(v)) {
976
- return true;
977
- }
978
- return false;
979
- }
980
- }
981
-
982
- if (v.toString instanceof Function) {
983
- v = v.toString();
984
- }
985
-
986
- if (typeof v === 'string' && v.length === 12) {
987
- return true;
988
- }
989
- if (typeof v === 'string' && /^[0-9A-Fa-f]{24}$/.test(v)) {
990
- return true;
991
- }
992
-
993
- return false;
960
+ const _mongoose = this instanceof Mongoose ? this : mongoose;
961
+ return _mongoose.Types.ObjectId.isValid(v);
994
962
  };
995
963
 
996
- Mongoose.prototype.syncIndexes = function() {
964
+ /**
965
+ *
966
+ * Syncs all the indexes for the models registered with this connection.
967
+ *
968
+ * @param {Object} options
969
+ * @param {Boolean} options.continueOnError `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model.
970
+ * @returns
971
+ */
972
+ Mongoose.prototype.syncIndexes = function(options) {
997
973
  const _mongoose = this instanceof Mongoose ? this : mongoose;
998
- return _mongoose.connection.syncIndexes();
974
+ return _mongoose.connection.syncIndexes(options);
999
975
  };
1000
976
 
1001
977
  /**
package/lib/internal.js CHANGED
@@ -10,7 +10,7 @@ const ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default', '
10
10
  module.exports = exports = InternalCache;
11
11
 
12
12
  function InternalCache() {
13
- this.activePaths = new ActiveRoster;
13
+ this.activePaths = new ActiveRoster();
14
14
  this.strictMode = undefined;
15
15
  }
16
16
 
package/lib/model.js CHANGED
@@ -48,6 +48,7 @@ const modifiedPaths = require('./helpers/update/modifiedPaths');
48
48
  const parallelLimit = require('./helpers/parallelLimit');
49
49
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
50
50
  const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
51
+ const setDottedPath = require('./helpers/path/setDottedPath');
51
52
  const util = require('util');
52
53
  const utils = require('./utils');
53
54
 
@@ -379,7 +380,12 @@ Model.prototype.$__save = function(options, callback) {
379
380
  });
380
381
  }
381
382
  let numAffected = 0;
382
- if (get(options, 'safe.w') !== 0 && get(options, 'w') !== 0) {
383
+ const writeConcern = options != null ?
384
+ options.writeConcern != null ?
385
+ options.writeConcern.w :
386
+ options.w :
387
+ 0;
388
+ if (writeConcern !== 0) {
383
389
  // Skip checking if write succeeded if writeConcern is set to
384
390
  // unacknowledged writes, because otherwise `numAffected` will always be 0
385
391
  if (result != null) {
@@ -391,8 +397,10 @@ Model.prototype.$__save = function(options, callback) {
391
397
  numAffected = result;
392
398
  }
393
399
  }
400
+
401
+ const versionBump = this.$__.version || this.$__schema.options.optimisticConcurrency;
394
402
  // was this an update that required a version bump?
395
- if (this.$__.version && !this.$__.inserting) {
403
+ if (versionBump && !this.$__.inserting) {
396
404
  const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
397
405
  this.$__.version = undefined;
398
406
  const key = this.$__schema.options.versionKey;
@@ -740,7 +748,7 @@ Model.prototype.$__delta = function() {
740
748
  operand(this, where, delta, data, 1, '$unset');
741
749
  } else if (value === null) {
742
750
  operand(this, where, delta, data, null);
743
- } else if (value.isMongooseArray && value.$path() && value[arrayAtomicsSymbol]) {
751
+ } else if (utils.isMongooseArray(value) && value.$path() && value[arrayAtomicsSymbol]) {
744
752
  // arrays and other custom types (support plugins etc)
745
753
  handleAtomics(this, where, delta, data, value);
746
754
  } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) {
@@ -792,7 +800,7 @@ function checkDivergentArray(doc, path, array) {
792
800
  }
793
801
  }
794
802
 
795
- if (!(pop && array && array.isMongooseArray)) return;
803
+ if (!(pop && utils.isMongooseArray(array))) return;
796
804
 
797
805
  // If the array was populated using options that prevented all
798
806
  // documents from being returned (match, skip, limit) or they
@@ -832,7 +840,8 @@ Model.prototype.$__version = function(where, delta) {
832
840
  if (where === true) {
833
841
  // this is an insert
834
842
  if (key) {
835
- this.$__setValue(key, delta[key] = 0);
843
+ setDottedPath(delta, key, 0);
844
+ this.$__setValue(key, 0);
836
845
  }
837
846
  return;
838
847
  }
@@ -1059,8 +1068,8 @@ Model.prototype.model = function model(name) {
1059
1068
  };
1060
1069
 
1061
1070
  /**
1062
- * Returns true if at least one document exists in the database that matches
1063
- * the given `filter`, and false otherwise.
1071
+ * Returns a document with `_id` only if at least one document exists in the database that matches
1072
+ * the given `filter`, and `null` otherwise.
1064
1073
  *
1065
1074
  * Under the hood, `MyModel.exists({ answer: 42 })` is equivalent to
1066
1075
  * `MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()`
@@ -1097,10 +1106,6 @@ Model.exists = function exists(filter, options, callback) {
1097
1106
  if (typeof callback === 'function') {
1098
1107
  return query.exec(callback);
1099
1108
  }
1100
- options = options || {};
1101
- if (!options.explain) {
1102
- return query.then(doc => !!doc);
1103
- }
1104
1109
 
1105
1110
  return query;
1106
1111
  };
@@ -1136,11 +1141,13 @@ Model.exists = function exists(filter, options, callback) {
1136
1141
  * @param {Object|String} [options] If string, same as `options.value`.
1137
1142
  * @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
1138
1143
  * @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
1144
+ * @param {Boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
1139
1145
  * @return {Model} The newly created discriminator model
1140
1146
  * @api public
1141
1147
  */
1142
1148
 
1143
1149
  Model.discriminator = function(name, schema, options) {
1150
+
1144
1151
  let model;
1145
1152
  if (typeof name === 'function') {
1146
1153
  model = name;
@@ -1164,7 +1171,7 @@ Model.discriminator = function(name, schema, options) {
1164
1171
  }
1165
1172
 
1166
1173
  schema = discriminator(this, name, schema, value, true);
1167
- if (this.db.models[name]) {
1174
+ if (this.db.models[name] && !schema.options.overwriteModels) {
1168
1175
  throw new OverwriteModelError(name);
1169
1176
  }
1170
1177
 
@@ -3374,7 +3381,7 @@ Model.$__insertMany = function(arr, options, callback) {
3374
3381
  return doc != null;
3375
3382
  });
3376
3383
  // Quickly escape while there aren't any valid docAttributes
3377
- if (docAttributes.length < 1) {
3384
+ if (docAttributes.length === 0) {
3378
3385
  if (rawResult) {
3379
3386
  const res = {
3380
3387
  mongoose: {
@@ -3609,16 +3616,21 @@ Model.bulkWrite = function(ops, options, callback) {
3609
3616
  * `bulkSave` uses `bulkWrite` under the hood, so it's mostly useful when dealing with many documents (10K+)
3610
3617
  *
3611
3618
  * @param {[Document]} documents
3619
+ * @param {Object} [options] options passed to the underlying `bulkWrite()`
3620
+ * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
3621
+ * @param {String|number} [options.w=1] The [write concern](https://docs.mongodb.com/manual/reference/write-concern/). See [`Query#w()`](/docs/api.html#query_Query-w) for more information.
3622
+ * @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
3623
+ * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
3612
3624
  *
3613
3625
  */
3614
- Model.bulkSave = function(documents) {
3626
+ Model.bulkSave = function(documents, options) {
3615
3627
  const preSavePromises = documents.map(buildPreSavePromise);
3616
3628
 
3617
3629
  const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true });
3618
3630
 
3619
3631
  let bulkWriteResultPromise;
3620
3632
  return Promise.all(preSavePromises)
3621
- .then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations))
3633
+ .then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations, options))
3622
3634
  .then(() => documents.map(buildSuccessfulWriteHandlerPromise))
3623
3635
  .then(() => bulkWriteResultPromise)
3624
3636
  .catch((err) => {
@@ -4451,9 +4463,16 @@ const excludeIdRegGlobal = /\s?-_id\s?/g;
4451
4463
 
4452
4464
  function populate(model, docs, options, callback) {
4453
4465
  const populateOptions = { ...options };
4454
- if (model.base.options.strictPopulate != null && options.strictPopulate == null) {
4455
- populateOptions.strictPopulate = model.base.options.strictPopulate;
4466
+ if (options.strictPopulate == null) {
4467
+ if (options._localModel != null && options._localModel.schema._userProvidedOptions.strictPopulate != null) {
4468
+ populateOptions.strictPopulate = options._localModel.schema._userProvidedOptions.strictPopulate;
4469
+ } else if (options._localModel != null && model.base.options.strictPopulate != null) {
4470
+ populateOptions.strictPopulate = model.base.options.strictPopulate;
4471
+ } else if (model.base.options.strictPopulate != null) {
4472
+ populateOptions.strictPopulate = model.base.options.strictPopulate;
4473
+ }
4456
4474
  }
4475
+
4457
4476
  // normalize single / multiple docs passed
4458
4477
  if (!Array.isArray(docs)) {
4459
4478
  docs = [docs];
@@ -4524,7 +4543,7 @@ function populate(model, docs, options, callback) {
4524
4543
  }
4525
4544
  if (!hasOne) {
4526
4545
  // If models but no docs, skip further deep populate.
4527
- if (modelsMap.length > 0) {
4546
+ if (modelsMap.length !== 0) {
4528
4547
  return callback();
4529
4548
  }
4530
4549
  // If no models to populate but we have a nested populate,
@@ -2,6 +2,7 @@
2
2
 
3
3
  const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol;
4
4
  const sessionNewDocuments = require('../helpers/symbols').sessionNewDocuments;
5
+ const utils = require('../utils');
5
6
 
6
7
  module.exports = function trackTransaction(schema) {
7
8
  schema.pre('save', function() {
@@ -47,10 +48,10 @@ function _getAtomics(doc, previous) {
47
48
  const val = doc.$__getValue(path);
48
49
  if (val != null &&
49
50
  val instanceof Array &&
50
- val.isMongooseDocumentArray &&
51
+ utils.isMongooseDocumentArray(val) &&
51
52
  val.length &&
52
53
  val[arrayAtomicsSymbol] != null &&
53
- Object.keys(val[arrayAtomicsSymbol]).length > 0) {
54
+ Object.keys(val[arrayAtomicsSymbol]).length !== 0) {
54
55
  const existing = previous.get(path) || {};
55
56
  pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
56
57
  }
@@ -61,7 +62,7 @@ function _getAtomics(doc, previous) {
61
62
  const path = dirt.path;
62
63
 
63
64
  const val = dirt.value;
64
- if (val != null && val[arrayAtomicsSymbol] != null && Object.keys(val[arrayAtomicsSymbol]).length > 0) {
65
+ if (val != null && val[arrayAtomicsSymbol] != null && Object.keys(val[arrayAtomicsSymbol]).length !== 0) {
65
66
  const existing = previous.get(path) || {};
66
67
  pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol]));
67
68
  }
package/lib/query.js CHANGED
@@ -120,7 +120,7 @@ function Query(conditions, options, model, collection) {
120
120
  * inherit mquery
121
121
  */
122
122
 
123
- Query.prototype = new mquery;
123
+ Query.prototype = new mquery();
124
124
  Query.prototype.constructor = Query;
125
125
  Query.base = mquery.prototype;
126
126
 
@@ -4848,7 +4848,8 @@ Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
4848
4848
  overwrite: overwrite,
4849
4849
  strict: this._mongooseOptions.strict,
4850
4850
  upsert: upsert,
4851
- arrayFilters: this.options.arrayFilters
4851
+ arrayFilters: this.options.arrayFilters,
4852
+ overwriteDiscriminatorKey: this._mongooseOptions.overwriteDiscriminatorKey
4852
4853
  }, this, this._conditions);
4853
4854
  };
4854
4855
 
@@ -5057,18 +5058,19 @@ Query.prototype.cast = function(model, obj) {
5057
5058
  model = getDiscriminatorByValue(model.discriminators, obj[discriminatorKey]) || model;
5058
5059
  }
5059
5060
 
5061
+ const opts = { upsert: this.options && this.options.upsert };
5062
+ if (this.options) {
5063
+ if ('strict' in this.options) {
5064
+ opts.strict = this.options.strict;
5065
+ opts.strictQuery = opts.strict;
5066
+ }
5067
+ if ('strictQuery' in this.options) {
5068
+ opts.strictQuery = this.options.strictQuery;
5069
+ }
5070
+ }
5071
+
5060
5072
  try {
5061
- return cast(model.schema, obj, {
5062
- upsert: this.options && this.options.upsert,
5063
- strict: (this.options && 'strict' in this.options) ?
5064
- this.options.strict :
5065
- get(model, 'schema.options.strict', null),
5066
- strictQuery: (this.options && 'strictQuery' in this.options) ?
5067
- this.options.strictQuery :
5068
- (this.options && 'strict' in this.options) ?
5069
- this.options.strict :
5070
- get(model, 'schema.options.strictQuery', null)
5071
- }, this);
5073
+ return cast(model.schema, obj, opts, this);
5072
5074
  } catch (err) {
5073
5075
  // CastError, assign model
5074
5076
  if (typeof err.setModel === 'function') {
@@ -212,7 +212,7 @@ exports.applyPaths = function applyPaths(fields, schema) {
212
212
 
213
213
  let addedPath = analyzePath(path, type);
214
214
  // arrays
215
- if (addedPath == null && type.$isMongooseArray && !type.$isMongooseDocumentArray) {
215
+ if (addedPath == null && !Array.isArray(type) && type.$isMongooseArray && !type.$isMongooseDocumentArray) {
216
216
  addedPath = analyzePath(path, type.caster);
217
217
  }
218
218
  if (addedPath != null) {
@@ -168,7 +168,7 @@ SubdocumentPath.prototype.cast = function(val, doc, init, priorVal, options) {
168
168
  const path = this.path;
169
169
  const selected = Object.keys(parentSelected).reduce((obj, key) => {
170
170
  if (key.startsWith(path + '.')) {
171
- obj[key.substr(path.length + 1)] = parentSelected[key];
171
+ obj[key.substring(path.length + 1)] = parentSelected[key];
172
172
  }
173
173
  return obj;
174
174
  }, {});
@@ -112,14 +112,12 @@ function SchemaArray(key, cast, options, schemaOptions) {
112
112
 
113
113
  if (!('defaultValue' in this) || this.defaultValue !== void 0) {
114
114
  const defaultFn = function() {
115
- let arr = [];
116
- if (fn) {
117
- arr = defaultArr.call(this);
118
- } else if (defaultArr != null) {
119
- arr = arr.concat(defaultArr);
120
- }
121
115
  // Leave it up to `cast()` to convert the array
122
- return arr;
116
+ return fn
117
+ ? defaultArr.call(this)
118
+ : defaultArr != null
119
+ ? [].concat(defaultArr)
120
+ : [];
123
121
  };
124
122
  defaultFn.$runBeforeSetters = !fn;
125
123
  this.default(defaultFn);
@@ -275,7 +273,7 @@ SchemaArray.prototype.applyGetters = function(value, scope) {
275
273
 
276
274
  const ret = SchemaType.prototype.applyGetters.call(this, value, scope);
277
275
  if (Array.isArray(ret)) {
278
- const rawValue = ret.isMongooseArrayProxy ? ret.__array : ret;
276
+ const rawValue = utils.isMongooseArray(ret) ? ret.__array : ret;
279
277
  const len = rawValue.length;
280
278
  for (let i = 0; i < len; ++i) {
281
279
  rawValue[i] = this.caster.applyGetters(rawValue[i], scope);
@@ -299,7 +297,7 @@ SchemaArray.prototype._applySetters = function(value, scope, init, priorVal) {
299
297
  }
300
298
 
301
299
  // No need to wrap empty arrays
302
- if (value != null && value.length > 0) {
300
+ if (value != null && value.length !== 0) {
303
301
  const valueDepth = arrayDepth(value);
304
302
  if (valueDepth.min === valueDepth.max && valueDepth.max < depth && valueDepth.containsNonArrayItem) {
305
303
  for (let i = valueDepth.max; i < depth; ++i) {
@@ -344,7 +342,7 @@ SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
344
342
  // Special case: if this index is on the parent of what looks like
345
343
  // GeoJSON, skip setting the default to empty array re: #1668, #3233
346
344
  const arrayGeojsonPath = this.path.endsWith('.coordinates') ?
347
- this.path.substr(0, this.path.lastIndexOf('.')) : null;
345
+ this.path.substring(0, this.path.lastIndexOf('.')) : null;
348
346
  if (arrayGeojsonPath != null) {
349
347
  for (i = 0, l = indexes.length; i < l; ++i) {
350
348
  const pathIndex = indexes[i][0][arrayGeojsonPath];
@@ -357,7 +355,7 @@ SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
357
355
 
358
356
  options = options || emptyOpts;
359
357
 
360
- let rawValue = value.isMongooseArrayProxy ? value.__array : value;
358
+ let rawValue = utils.isMongooseArray(value) ? value.__array : value;
361
359
  value = MongooseArray(rawValue, options.path || this._arrayPath || this.path, doc, this);
362
360
  rawValue = value.__array;
363
361
 
@@ -557,12 +555,16 @@ function cast$all(val) {
557
555
  }
558
556
 
559
557
  val = val.map(function(v) {
560
- if (utils.isObject(v)) {
561
- const o = {};
562
- o[this.path] = v;
563
- return cast(this.casterConstructor.schema, o)[this.path];
558
+ if (!utils.isObject(v)) {
559
+ return v;
560
+ }
561
+ if (v.$elemMatch != null) {
562
+ return { $elemMatch: cast(this.casterConstructor.schema, v.$elemMatch) };
564
563
  }
565
- return v;
564
+
565
+ const o = {};
566
+ o[this.path] = v;
567
+ return cast(this.casterConstructor.schema, o)[this.path];
566
568
  }, this);
567
569
 
568
570
  return this.castForQuery(val);
@@ -228,7 +228,7 @@ DocumentArrayPath.prototype.doValidate = function(array, fn, scope, options) {
228
228
  if (options && options.updateValidator) {
229
229
  return fn();
230
230
  }
231
- if (!array.isMongooseDocumentArray) {
231
+ if (!utils.isMongooseDocumentArray(array)) {
232
232
  array = new MongooseDocumentArray(array, _this.path, scope);
233
233
  }
234
234
 
@@ -398,12 +398,9 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
398
398
  return this.cast([value], doc, init, prev, options);
399
399
  }
400
400
 
401
- if (!(value && value.isMongooseDocumentArray) &&
402
- !options.skipDocumentArrayCast) {
403
- value = new MongooseDocumentArray(value, this.path, doc);
404
- } else if (value && value.isMongooseDocumentArray) {
405
- // We need to create a new array, otherwise change tracking will
406
- // update the old doc (gh-4449)
401
+ // We need to create a new array, otherwise change tracking will
402
+ // update the old doc (gh-4449)
403
+ if (!options.skipDocumentArrayCast || utils.isMongooseDocumentArray(value)) {
407
404
  value = new MongooseDocumentArray(value, this.path, doc);
408
405
  }
409
406
 
@@ -415,7 +412,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
415
412
  value[arrayPathSymbol] = this.path + '.' + options.arrayPathIndex;
416
413
  }
417
414
 
418
- const rawArray = value.isMongooseDocumentArrayProxy ? value.__array : value;
415
+ const rawArray = utils.isMongooseDocumentArray(value) ? value.__array : value;
419
416
 
420
417
  const len = rawArray.length;
421
418
  const initDocumentOptions = { skipId: true, willInit: true };
@@ -546,7 +543,7 @@ function scopePaths(array, fields, init) {
546
543
  continue;
547
544
  }
548
545
  if (sub.startsWith('$.')) {
549
- sub = sub.substr(2);
546
+ sub = sub.substring(2);
550
547
  }
551
548
  hasKeys || (hasKeys = true);
552
549
  selected[sub] = fields[key];
@@ -281,7 +281,7 @@ function resetId(v) {
281
281
 
282
282
  if (this instanceof Document) {
283
283
  if (v === void 0) {
284
- const _v = new oid;
284
+ const _v = new oid();
285
285
  this.$__._id = _v;
286
286
  return _v;
287
287
  }
package/lib/schema.js CHANGED
@@ -75,6 +75,7 @@ let id = 0;
75
75
  * - [selectPopulatedPaths](/docs/guide.html#selectPopulatedPaths): boolean - defaults to `true`
76
76
  * - [skipVersioning](/docs/guide.html#skipVersioning): object - paths to exclude from versioning
77
77
  * - [timestamps](/docs/guide.html#timestamps): object or boolean - defaults to `false`. If true, Mongoose adds `createdAt` and `updatedAt` properties to your schema and manages those properties for you.
78
+ * - [pluginTags](/docs/guide.html#pluginTags): array of strings - defaults to `undefined`. If set and plugin called with `tags` option, will only apply that plugin to schemas with a matching tag.
78
79
  *
79
80
  * ####Options for Nested Schemas:
80
81
  * - `excludeIndexes`: bool - defaults to `false`. If `true`, skip building indexes on this schema's paths.
@@ -532,13 +533,13 @@ Schema.prototype.add = function add(obj, prefix) {
532
533
  // Special-case: Non-options definitely a path so leaf at this node
533
534
  // Examples: Schema instances, SchemaType instances
534
535
  if (prefix) {
535
- this.nested[prefix.substr(0, prefix.length - 1)] = true;
536
+ this.nested[prefix.substring(0, prefix.length - 1)] = true;
536
537
  }
537
538
  this.path(prefix + key, val);
538
539
  } else if (Object.keys(val).length < 1) {
539
540
  // Special-case: {} always interpreted as Mixed path so leaf at this node
540
541
  if (prefix) {
541
- this.nested[prefix.substr(0, prefix.length - 1)] = true;
542
+ this.nested[prefix.substring(0, prefix.length - 1)] = true;
542
543
  }
543
544
  this.path(fullPath, val); // mixed type
544
545
  } else if (!val[typeKey] || (typeKey === 'type' && isPOJO(val.type) && val.type.type)) {
@@ -553,7 +554,7 @@ Schema.prototype.add = function add(obj, prefix) {
553
554
  if (isPOJO(_typeDef) && Object.keys(_typeDef).length > 0) {
554
555
  // If a POJO is the value of a type key, make it a subdocument
555
556
  if (prefix) {
556
- this.nested[prefix.substr(0, prefix.length - 1)] = true;
557
+ this.nested[prefix.substring(0, prefix.length - 1)] = true;
557
558
  }
558
559
  const _schema = new Schema(_typeDef);
559
560
  const schemaWrappedPath = Object.assign({}, val, { type: _schema });
@@ -561,7 +562,7 @@ Schema.prototype.add = function add(obj, prefix) {
561
562
  } else {
562
563
  // Either the type is non-POJO or we interpret it as Mixed anyway
563
564
  if (prefix) {
564
- this.nested[prefix.substr(0, prefix.length - 1)] = true;
565
+ this.nested[prefix.substring(0, prefix.length - 1)] = true;
565
566
  }
566
567
  this.path(prefix + key, val);
567
568
  }