mongoose 6.2.11 → 6.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.eslintrc.json +157 -157
  2. package/README.md +1 -1
  3. package/dist/browser.umd.js +2 -1704
  4. package/lib/aggregate.js +4 -3
  5. package/lib/collection.js +0 -7
  6. package/lib/connection.js +2 -1
  7. package/lib/cursor/ChangeStream.js +42 -2
  8. package/lib/cursor/QueryCursor.js +2 -0
  9. package/lib/document.js +22 -26
  10. package/lib/error/cast.js +8 -2
  11. package/lib/error/eachAsyncMultiError.js +41 -0
  12. package/lib/helpers/common.js +0 -8
  13. package/lib/helpers/cursor/eachAsync.js +44 -12
  14. package/lib/helpers/immediate.js +3 -1
  15. package/lib/helpers/isAsyncFunction.js +19 -7
  16. package/lib/helpers/model/discriminator.js +1 -3
  17. package/lib/helpers/populate/getModelsMapForPopulate.js +13 -10
  18. package/lib/helpers/query/applyGlobalOption.js +29 -0
  19. package/lib/helpers/schematype/handleImmutable.js +4 -1
  20. package/lib/helpers/timestamps/setupTimestamps.js +2 -2
  21. package/lib/index.js +11 -4
  22. package/lib/internal.js +0 -1
  23. package/lib/model.js +39 -27
  24. package/lib/query.js +23 -4
  25. package/lib/queryhelpers.js +11 -1
  26. package/lib/schema/SubdocumentPath.js +3 -15
  27. package/lib/schema/documentarray.js +7 -7
  28. package/lib/schema/number.js +1 -5
  29. package/lib/schema/objectid.js +2 -4
  30. package/lib/schema/string.js +1 -5
  31. package/lib/schema.js +115 -2
  32. package/lib/schematype.js +2 -2
  33. package/lib/types/DocumentArray/methods/index.js +9 -1
  34. package/lib/types/array/methods/index.js +8 -9
  35. package/lib/validoptions.js +1 -0
  36. package/package.json +25 -18
  37. package/tools/repl.js +2 -1
  38. package/tsconfig.json +2 -2
  39. package/types/aggregate.d.ts +1 -2
  40. package/types/connection.d.ts +4 -5
  41. package/types/cursor.d.ts +13 -6
  42. package/types/document.d.ts +17 -14
  43. package/types/error.d.ts +124 -124
  44. package/types/index.d.ts +350 -202
  45. package/types/mongooseoptions.d.ts +11 -6
  46. package/types/schemaoptions.d.ts +1 -2
  47. package/CHANGELOG.md +0 -7238
  48. package/History.md +0 -1
  49. package/lib/helpers/query/applyGlobalMaxTimeMS.js +0 -15
package/lib/model.js CHANGED
@@ -1067,13 +1067,33 @@ Model.prototype.$__deleteOne = Model.prototype.$__remove;
1067
1067
  * doc.model('User').findById(id, callback);
1068
1068
  *
1069
1069
  * @param {String} name model name
1070
+ * @method model
1070
1071
  * @api public
1072
+ * @return {Model}
1071
1073
  */
1072
1074
 
1073
1075
  Model.prototype.model = function model(name) {
1074
1076
  return this[modelDbSymbol].model(name);
1075
1077
  };
1076
1078
 
1079
+ /**
1080
+ * Returns another Model instance.
1081
+ *
1082
+ * #### Example:
1083
+ *
1084
+ * const doc = new Tank;
1085
+ * doc.model('User').findById(id, callback);
1086
+ *
1087
+ * @param {String} name model name
1088
+ * @method $model
1089
+ * @api public
1090
+ * @return {Model}
1091
+ */
1092
+
1093
+ Model.prototype.$model = function $model(name) {
1094
+ return this[modelDbSymbol].model(name);
1095
+ };
1096
+
1077
1097
  /**
1078
1098
  * Returns a document with `_id` only if at least one document exists in the database that matches
1079
1099
  * the given `filter`, and `null` otherwise.
@@ -1362,23 +1382,27 @@ Model.createCollection = function createCollection(options, callback) {
1362
1382
  }
1363
1383
 
1364
1384
  const schemaCollation = this &&
1365
- this.schema &&
1366
- this.schema.options &&
1367
- this.schema.options.collation || null;
1385
+ this.schema &&
1386
+ this.schema.options &&
1387
+ this.schema.options.collation;
1368
1388
  if (schemaCollation != null) {
1369
1389
  options = Object.assign({ collation: schemaCollation }, options);
1370
1390
  }
1371
1391
  const capped = this &&
1372
- this.schema &&
1373
- this.schema.options &&
1374
- this.schema.options.capped;
1375
- if (capped) {
1376
- options = Object.assign({ capped: true }, capped, options);
1392
+ this.schema &&
1393
+ this.schema.options &&
1394
+ this.schema.options.capped;
1395
+ if (capped != null) {
1396
+ if (typeof capped === 'number') {
1397
+ options = Object.assign({ capped: true, size: capped }, options);
1398
+ } else if (typeof capped === 'object') {
1399
+ options = Object.assign({ capped: true }, capped, options);
1400
+ }
1377
1401
  }
1378
1402
  const timeseries = this &&
1379
- this.schema &&
1380
- this.schema.options &&
1381
- this.schema.options.timeseries;
1403
+ this.schema &&
1404
+ this.schema.options &&
1405
+ this.schema.options.timeseries;
1382
1406
  if (timeseries != null) {
1383
1407
  options = Object.assign({ timeseries }, options);
1384
1408
  if (options.expireAfterSeconds != null) {
@@ -2148,14 +2172,7 @@ Model.find = function find(conditions, projection, options, callback) {
2148
2172
 
2149
2173
  const mq = new this.Query({}, {}, this, this.$__collection);
2150
2174
  mq.select(projection);
2151
-
2152
2175
  mq.setOptions(options);
2153
- if (this.schema.discriminatorMapping &&
2154
- this.schema.discriminatorMapping.isRoot &&
2155
- mq.selectedInclusively()) {
2156
- // Need to select discriminator key because original schema doesn't have it
2157
- mq.select(this.schema.options.discriminatorKey);
2158
- }
2159
2176
 
2160
2177
  callback = this.$handleCallbackError(callback);
2161
2178
 
@@ -2261,11 +2278,6 @@ Model.findOne = function findOne(conditions, projection, options, callback) {
2261
2278
  const mq = new this.Query({}, {}, this, this.$__collection);
2262
2279
  mq.select(projection);
2263
2280
  mq.setOptions(options);
2264
- if (this.schema.discriminatorMapping &&
2265
- this.schema.discriminatorMapping.isRoot &&
2266
- mq.selectedInclusively()) {
2267
- mq.select(this.schema.options.discriminatorKey);
2268
- }
2269
2281
 
2270
2282
  callback = this.$handleCallbackError(callback);
2271
2283
  return mq.findOne(conditions, callback);
@@ -3488,6 +3500,7 @@ function _setIsNew(doc, val) {
3488
3500
  const subdocs = doc.$getAllSubdocs();
3489
3501
  for (const subdoc of subdocs) {
3490
3502
  subdoc.$isNew = val;
3503
+ subdoc.$emit('isNew', val);
3491
3504
  }
3492
3505
  }
3493
3506
 
@@ -4212,7 +4225,6 @@ Model.aggregate = function aggregate(pipeline, options, callback) {
4212
4225
 
4213
4226
  const aggregate = new Aggregate(pipeline || []);
4214
4227
  aggregate.model(this);
4215
-
4216
4228
  if (options != null) {
4217
4229
  aggregate.option(options);
4218
4230
  }
@@ -4247,7 +4259,7 @@ Model.aggregate = function aggregate(pipeline, options, callback) {
4247
4259
  * }
4248
4260
  *
4249
4261
  * @param {Object} obj
4250
- * @param {Array} pathsToValidate
4262
+ * @param {Array|String} pathsToValidate
4251
4263
  * @param {Object} [context]
4252
4264
  * @param {Function} [callback]
4253
4265
  * @return {Promise|undefined}
@@ -4266,7 +4278,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
4266
4278
  let paths = Object.keys(schema.paths);
4267
4279
 
4268
4280
  if (pathsToValidate != null) {
4269
- const _pathsToValidate = new Set(pathsToValidate);
4281
+ const _pathsToValidate = typeof pathsToValidate === 'string' ? new Set(pathsToValidate.split(' ')) : new Set(pathsToValidate);
4270
4282
  paths = paths.filter(p => {
4271
4283
  const pieces = p.split('.');
4272
4284
  let cur = pieces[0];
@@ -4809,7 +4821,6 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4809
4821
  o[schema.options.versionKey] = Number;
4810
4822
  schema.add(o);
4811
4823
  }
4812
-
4813
4824
  let model;
4814
4825
  if (typeof name === 'function' && name.prototype instanceof Model) {
4815
4826
  model = name;
@@ -4853,6 +4864,7 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4853
4864
  model.model = function model(name) {
4854
4865
  return this.db.model(name);
4855
4866
  };
4867
+
4856
4868
  model.db = connection;
4857
4869
  model.prototype.db = connection;
4858
4870
  model.prototype[modelDbSymbol] = connection;
package/lib/query.js CHANGED
@@ -12,7 +12,7 @@ const ObjectParameterError = require('./error/objectParameter');
12
12
  const QueryCursor = require('./cursor/QueryCursor');
13
13
  const ReadPreference = require('./driver').get().ReadPreference;
14
14
  const ValidationError = require('./error/validation');
15
- const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
15
+ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption');
16
16
  const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
17
17
  const cast = require('./cast');
18
18
  const castArrayFilters = require('./helpers/update/castArrayFilters');
@@ -53,6 +53,7 @@ const queryOptionMethods = new Set([
53
53
  'maxScan',
54
54
  'maxTimeMS',
55
55
  'maxscan',
56
+ 'populate',
56
57
  'projection',
57
58
  'read',
58
59
  'select',
@@ -2192,6 +2193,7 @@ function _castArrayFilters(query) {
2192
2193
  * @api private
2193
2194
  */
2194
2195
  Query.prototype._find = wrapThunk(function(callback) {
2196
+
2195
2197
  this._castConditions();
2196
2198
 
2197
2199
  if (this.error() != null) {
@@ -2210,6 +2212,7 @@ Query.prototype._find = wrapThunk(function(callback) {
2210
2212
  const userProvidedFields = _this._userProvidedFields || {};
2211
2213
 
2212
2214
  applyGlobalMaxTimeMS(this.options, this.model);
2215
+ applyGlobalDiskUse(this.options, this.model);
2213
2216
 
2214
2217
  // Separate options to pass down to `completeMany()` in case we need to
2215
2218
  // set a session on the document
@@ -2228,8 +2231,15 @@ Query.prototype._find = wrapThunk(function(callback) {
2228
2231
  if (this.options.explain) {
2229
2232
  return callback(null, docs);
2230
2233
  }
2231
-
2232
2234
  if (!mongooseOptions.populate) {
2235
+ const versionKey = _this.schema.options.versionKey;
2236
+ if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
2237
+ docs.forEach((doc) => {
2238
+ if (versionKey in doc) {
2239
+ delete doc[versionKey];
2240
+ }
2241
+ });
2242
+ }
2233
2243
  return mongooseOptions.lean ?
2234
2244
  callback(null, docs) :
2235
2245
  completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
@@ -2411,6 +2421,12 @@ Query.prototype._completeOne = function(doc, res, callback) {
2411
2421
  }
2412
2422
 
2413
2423
  if (!mongooseOptions.populate) {
2424
+ const versionKey = this.schema.options.versionKey;
2425
+ if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
2426
+ if (versionKey in doc) {
2427
+ delete doc[versionKey];
2428
+ }
2429
+ }
2414
2430
  return mongooseOptions.lean ?
2415
2431
  _completeOneLean(doc, res, options, callback) :
2416
2432
  completeOne(model, doc, res, options, projection, userProvidedFields,
@@ -2453,8 +2469,8 @@ Query.prototype._findOne = wrapThunk(function(callback) {
2453
2469
 
2454
2470
  this._applyPaths();
2455
2471
  this._fields = this._castFields(this._fields);
2456
-
2457
2472
  applyGlobalMaxTimeMS(this.options, this.model);
2473
+ applyGlobalDiskUse(this.options, this.model);
2458
2474
 
2459
2475
  // don't pass in the conditions because we already merged them in
2460
2476
  Query.base.findOne.call(this, {}, (err, doc) => {
@@ -2567,6 +2583,7 @@ Query.prototype._count = wrapThunk(function(callback) {
2567
2583
  }
2568
2584
 
2569
2585
  applyGlobalMaxTimeMS(this.options, this.model);
2586
+ applyGlobalDiskUse(this.options, this.model);
2570
2587
 
2571
2588
  const conds = this._conditions;
2572
2589
  const options = this._optionsForExec();
@@ -2594,6 +2611,7 @@ Query.prototype._countDocuments = wrapThunk(function(callback) {
2594
2611
  }
2595
2612
 
2596
2613
  applyGlobalMaxTimeMS(this.options, this.model);
2614
+ applyGlobalDiskUse(this.options, this.model);
2597
2615
 
2598
2616
  const conds = this._conditions;
2599
2617
  const options = this._optionsForExec();
@@ -2814,6 +2832,7 @@ Query.prototype.__distinct = wrapThunk(function __distinct(callback) {
2814
2832
  }
2815
2833
 
2816
2834
  applyGlobalMaxTimeMS(this.options, this.model);
2835
+ applyGlobalDiskUse(this.options, this.model);
2817
2836
 
2818
2837
  const options = this._optionsForExec();
2819
2838
 
@@ -5739,4 +5758,4 @@ Query.prototype.model;
5739
5758
  * Export
5740
5759
  */
5741
5760
 
5742
- module.exports = Query;
5761
+ module.exports = Query;
@@ -261,7 +261,17 @@ exports.applyPaths = function applyPaths(fields, schema) {
261
261
  delete fields[plusPath];
262
262
  }
263
263
 
264
- if (typeof type.selected !== 'boolean') return;
264
+ if (typeof type.selected !== 'boolean') {
265
+ return;
266
+ }
267
+
268
+ // If set to 0, we're explicitly excluding the discriminator key. Can't do this for all fields,
269
+ // because we have tests that assert that using `-path` to exclude schema-level `select: true`
270
+ // fields counts as an exclusive projection. See gh-11546
271
+ if (exclude && type.selected && path === schema.options.discriminatorKey && fields[path] != null && !fields[path]) {
272
+ delete fields[path];
273
+ return;
274
+ }
265
275
 
266
276
  if (hasPlusPath) {
267
277
  // forced inclusion
@@ -60,25 +60,13 @@ function _createConstructor(schema, baseClass) {
60
60
  Subdocument || (Subdocument = require('../types/subdocument'));
61
61
 
62
62
  const _embedded = function SingleNested(value, path, parent) {
63
- const _this = this;
64
-
65
63
  this.$__parent = parent;
66
64
  Subdocument.apply(this, arguments);
67
65
 
68
- this.$session(this.ownerDocument().$session());
69
-
70
- if (parent) {
71
- parent.$on('save', function() {
72
- _this.emit('save', _this);
73
- _this.constructor.emit('save', _this);
74
- });
75
-
76
- parent.$on('isNew', function(val) {
77
- _this.isNew = val;
78
- _this.emit('isNew', val);
79
- _this.constructor.emit('isNew', val);
80
- });
66
+ if (parent == null) {
67
+ return;
81
68
  }
69
+ this.$session(parent.$session());
82
70
  };
83
71
 
84
72
  schema._preCompile();
@@ -13,7 +13,6 @@ const SchemaType = require('../schematype');
13
13
  const discriminator = require('../helpers/model/discriminator');
14
14
  const handleIdOption = require('../helpers/schema/handleIdOption');
15
15
  const handleSpreadDoc = require('../helpers/document/handleSpreadDoc');
16
- const util = require('util');
17
16
  const utils = require('../utils');
18
17
  const getConstructor = require('../helpers/discriminator/getConstructor');
19
18
 
@@ -114,10 +113,12 @@ function _createConstructor(schema, options, baseClass) {
114
113
  Subdocument || (Subdocument = require('../types/ArraySubdocument'));
115
114
 
116
115
  // compile an embedded document for this schema
117
- function EmbeddedDocument() {
116
+ function EmbeddedDocument(_value, parentArray) {
118
117
  Subdocument.apply(this, arguments);
119
-
120
- this.$session(this.ownerDocument().$session());
118
+ if (parentArray == null || parentArray.getArrayParent() == null) {
119
+ return;
120
+ }
121
+ this.$session(parentArray.getArrayParent().$session());
121
122
  }
122
123
 
123
124
  schema._preCompile();
@@ -394,7 +395,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
394
395
 
395
396
  if (!Array.isArray(value)) {
396
397
  if (!init && !DocumentArrayPath.options.castNonArrays) {
397
- throw new CastError('DocumentArray', util.inspect(value), this.path, null, this);
398
+ throw new CastError('DocumentArray', value, this.path, null, this);
398
399
  }
399
400
  // gh-2442 mark whole array as modified if we're initializing a doc from
400
401
  // the db and the path isn't an array in the document
@@ -486,8 +487,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
486
487
  // see gh-746
487
488
  rawArray[i] = subdoc;
488
489
  } catch (error) {
489
- const valueInErrorMessage = util.inspect(rawArray[i]);
490
- throw new CastError('embedded', valueInErrorMessage,
490
+ throw new CastError('embedded', rawArray[i],
491
491
  value[arrayPathSymbol], error, this);
492
492
  }
493
493
  }
@@ -353,11 +353,7 @@ SchemaNumber.prototype.enum = function(values, message) {
353
353
  */
354
354
 
355
355
  SchemaNumber.prototype.cast = function(value, doc, init) {
356
- if (SchemaType._isRef(this, value, doc, init)) {
357
- if (typeof value === 'number') {
358
- return value;
359
- }
360
-
356
+ if (typeof value !== 'number' && SchemaType._isRef(this, value, doc, init)) {
361
357
  if (value == null || utils.isNonBuiltinObject(value)) {
362
358
  return this._castRef(value, doc, init);
363
359
  }
@@ -221,11 +221,9 @@ ObjectId.prototype.checkRequired = function checkRequired(value, doc) {
221
221
  */
222
222
 
223
223
  ObjectId.prototype.cast = function(value, doc, init) {
224
- if (SchemaType._isRef(this, value, doc, init)) {
224
+ if (!(value instanceof oid) && SchemaType._isRef(this, value, doc, init)) {
225
225
  // wait! we may need to cast this to a document
226
- if (value instanceof oid) {
227
- return value;
228
- } else if ((getConstructorName(value) || '').toLowerCase() === 'objectid') {
226
+ if ((getConstructorName(value) || '').toLowerCase() === 'objectid') {
229
227
  return new oid(value.toHexString());
230
228
  }
231
229
 
@@ -580,11 +580,7 @@ SchemaString.prototype.checkRequired = function checkRequired(value, doc) {
580
580
  */
581
581
 
582
582
  SchemaString.prototype.cast = function(value, doc, init) {
583
- if (SchemaType._isRef(this, value, doc, init)) {
584
- if (typeof value === 'string') {
585
- return value;
586
- }
587
-
583
+ if (typeof value !== 'string' && SchemaType._isRef(this, value, doc, init)) {
588
584
  return this._castRef(value, doc, init);
589
585
  }
590
586
 
package/lib/schema.js CHANGED
@@ -22,6 +22,7 @@ const readPref = require('./driver').get().ReadPreference;
22
22
  const setupTimestamps = require('./helpers/timestamps/setupTimestamps');
23
23
  const utils = require('./utils');
24
24
  const validateRef = require('./helpers/populate/validateRef');
25
+ const util = require('util');
25
26
 
26
27
  let MongooseTypes;
27
28
 
@@ -53,7 +54,7 @@ let id = 0;
53
54
  * - [autoCreate](/docs/guide.html#autoCreate): bool - defaults to null (which means use the connection's autoCreate option)
54
55
  * - [bufferCommands](/docs/guide.html#bufferCommands): bool - defaults to true
55
56
  * - [bufferTimeoutMS](/docs/guide.html#bufferTimeoutMS): number - defaults to 10000 (10 seconds). If `bufferCommands` is enabled, the amount of time Mongoose will wait for connectivity to be restablished before erroring out.
56
- * - [capped](/docs/guide.html#capped): bool - defaults to false
57
+ * - [capped](/docs/guide.html#capped): bool | number | object - defaults to false
57
58
  * - [collection](/docs/guide.html#collection): string - no default
58
59
  * - [discriminatorKey](/docs/guide.html#discriminatorKey): string - defaults to `__t`
59
60
  * - [id](/docs/guide.html#id): bool - defaults to true
@@ -370,6 +371,9 @@ Schema.prototype._clone = function _clone(Constructor) {
370
371
  if (this.discriminators != null) {
371
372
  s.discriminators = Object.assign({}, this.discriminators);
372
373
  }
374
+ if (this._applyDiscriminators != null) {
375
+ s._applyDiscriminators = Object.assign({}, this._applyDiscriminators);
376
+ }
373
377
 
374
378
  s.aliases = Object.assign({}, this.aliases);
375
379
 
@@ -469,6 +473,11 @@ Schema.prototype.defaultOptions = function(options) {
469
473
  return options;
470
474
  };
471
475
 
476
+ Schema.prototype.discriminator = function(name, schema) {
477
+ this._applyDiscriminators = {};
478
+ this._applyDiscriminators[name] = schema;
479
+ };
480
+
472
481
  /**
473
482
  * Adds key path / schema type pairs to this schema.
474
483
  *
@@ -510,7 +519,6 @@ Schema.prototype.add = function add(obj, prefix) {
510
519
 
511
520
  const keys = Object.keys(obj);
512
521
  const typeKey = this.options.typeKey;
513
-
514
522
  for (const key of keys) {
515
523
  const fullPath = prefix + key;
516
524
  const val = obj[key];
@@ -540,6 +548,25 @@ Schema.prototype.add = function add(obj, prefix) {
540
548
  this.nested[prefix.substring(0, prefix.length - 1)] = true;
541
549
  }
542
550
  this.path(prefix + key, val);
551
+ if (val[0] != null && !(val[0].instanceOfSchema) && utils.isPOJO(val[0].discriminators)) {
552
+ const schemaType = this.path(prefix + key);
553
+ for (const key in val[0].discriminators) {
554
+ schemaType.discriminator(key, val[0].discriminators[key]);
555
+ }
556
+ } else if (val[0] != null && val[0].instanceOfSchema && utils.isPOJO(val[0]._applyDiscriminators)) {
557
+ const applyDiscriminators = val[0]._applyDiscriminators || [];
558
+ const schemaType = this.path(prefix + key);
559
+ for (const disc in applyDiscriminators) {
560
+ schemaType.discriminator(disc, applyDiscriminators[disc]);
561
+ }
562
+ }
563
+ else if (val != null && val.instanceOfSchema && utils.isPOJO(val._applyDiscriminators)) {
564
+ const applyDiscriminators = val._applyDiscriminators || [];
565
+ const schemaType = this.path(prefix + key);
566
+ for (const disc in applyDiscriminators) {
567
+ schemaType.discriminator(disc, applyDiscriminators[disc]);
568
+ }
569
+ }
543
570
  } else if (Object.keys(val).length < 1) {
544
571
  // Special-case: {} always interpreted as Mixed path so leaf at this node
545
572
  if (prefix) {
@@ -569,6 +596,12 @@ Schema.prototype.add = function add(obj, prefix) {
569
596
  this.nested[prefix.substring(0, prefix.length - 1)] = true;
570
597
  }
571
598
  this.path(prefix + key, val);
599
+ if (val != null && !(val.instanceOfSchema) && utils.isPOJO(val.discriminators)) {
600
+ const schemaType = this.path(prefix + key);
601
+ for (const key in val.discriminators) {
602
+ schemaType.discriminator(key, val.discriminators[key]);
603
+ }
604
+ }
572
605
  }
573
606
  }
574
607
  }
@@ -579,6 +612,86 @@ Schema.prototype.add = function add(obj, prefix) {
579
612
  return this;
580
613
  };
581
614
 
615
+ /**
616
+ * Remove an index by name or index specification.
617
+ *
618
+ * removeIndex only removes indexes from your schema object. Does **not** affect the indexes
619
+ * in MongoDB.
620
+ *
621
+ * ####Example:
622
+ *
623
+ * const ToySchema = new Schema({ name: String, color: String, price: Number });
624
+ *
625
+ * // Add a new index on { name, color }
626
+ * ToySchema.index({ name: 1, color: 1 });
627
+ *
628
+ * // Remove index on { name, color }
629
+ * // Keep in mind that order matters! `removeIndex({ color: 1, name: 1 })` won't remove the index
630
+ * ToySchema.removeIndex({ name: 1, color: 1 });
631
+ *
632
+ * // Add an index with a custom name
633
+ * ToySchema.index({ color: 1 }, { name: 'my custom index name' });
634
+ * // Remove index by name
635
+ * ToySchema.removeIndex('my custom index name');
636
+ *
637
+ * @param {Object|string} index name or index specification
638
+ * @return {Schema} the Schema instance
639
+ * @api public
640
+ */
641
+
642
+ Schema.prototype.removeIndex = function removeIndex(index) {
643
+ if (arguments.length > 1) {
644
+ throw new Error('removeIndex() takes only 1 argument');
645
+ }
646
+
647
+ if (typeof index !== 'object' && typeof index !== 'string') {
648
+ throw new Error('removeIndex() may only take either an object or a string as an argument');
649
+ }
650
+
651
+ if (typeof index === 'object') {
652
+ for (let i = this._indexes.length - 1; i >= 0; --i) {
653
+ if (util.isDeepStrictEqual(this._indexes[i][0], index)) {
654
+ this._indexes.splice(i, 1);
655
+ }
656
+ }
657
+ } else {
658
+ for (let i = this._indexes.length - 1; i >= 0; --i) {
659
+ if (this._indexes[i][1] != null && this._indexes[i][1].name === index) {
660
+ this._indexes.splice(i, 1);
661
+ }
662
+ }
663
+ }
664
+
665
+ return this;
666
+ };
667
+
668
+ /**
669
+ * Remove all indexes from this schema.
670
+ *
671
+ * clearIndexes only removes indexes from your schema object. Does **not** affect the indexes
672
+ * in MongoDB.
673
+ *
674
+ * ####Example:
675
+ *
676
+ * const ToySchema = new Schema({ name: String, color: String, price: Number });
677
+ * ToySchema.index({ name: 1 });
678
+ * ToySchema.index({ color: 1 });
679
+ *
680
+ * // Remove all indexes on this schema
681
+ * ToySchema.clearIndexes();
682
+ *
683
+ * ToySchema.indexes(); // []
684
+ *
685
+ * @return {Schema} the Schema instance
686
+ * @api public
687
+ */
688
+
689
+ Schema.prototype.clearIndexes = function clearIndexes() {
690
+ this._indexes.length = 0;
691
+
692
+ return this;
693
+ };
694
+
582
695
  /**
583
696
  * Reserved document keys.
584
697
  *
package/lib/schematype.js CHANGED
@@ -1148,7 +1148,7 @@ SchemaType.prototype.getDefault = function(scope, init) {
1148
1148
  * @api private
1149
1149
  */
1150
1150
 
1151
- SchemaType.prototype._applySetters = function(value, scope, init, priorVal) {
1151
+ SchemaType.prototype._applySetters = function(value, scope, init, priorVal, options) {
1152
1152
  let v = value;
1153
1153
  if (init) {
1154
1154
  return v;
@@ -1156,7 +1156,7 @@ SchemaType.prototype._applySetters = function(value, scope, init, priorVal) {
1156
1156
  const setters = this.setters;
1157
1157
 
1158
1158
  for (let i = setters.length - 1; i >= 0; i--) {
1159
- v = setters[i].call(scope, v, priorVal, this);
1159
+ v = setters[i].call(scope, v, priorVal, this, options);
1160
1160
  }
1161
1161
 
1162
1162
  return v;
@@ -22,6 +22,14 @@ const methods = {
22
22
  return this.toObject(internalToObjectOptions);
23
23
  },
24
24
 
25
+ /*!
26
+ * ignore
27
+ */
28
+
29
+ getArrayParent() {
30
+ return this[arrayParentSymbol];
31
+ },
32
+
25
33
  /**
26
34
  * Overrides MongooseArray#cast
27
35
  *
@@ -311,8 +319,8 @@ const methods = {
311
319
  return this;
312
320
  }
313
321
  const value = methods._cast.call(this, val, i);
314
- arr[i] = value;
315
322
  methods._markModified.call(this, i);
323
+ arr[i] = value;
316
324
  return this;
317
325
  },
318
326
 
@@ -414,9 +414,9 @@ const methods = {
414
414
  }
415
415
 
416
416
  if (!found) {
417
+ this._markModified();
417
418
  rawArray.push(v);
418
419
  this._registerAtomic('$addToSet', v);
419
- this._markModified();
420
420
  [].push.call(added, v);
421
421
  }
422
422
  }, this);
@@ -510,9 +510,9 @@ const methods = {
510
510
 
511
511
  nonAtomicPush() {
512
512
  const values = [].map.call(arguments, this._mapCast, this);
513
+ this._markModified();
513
514
  const ret = [].push.apply(this, values);
514
515
  this._registerAtomic('$set', this);
515
- this._markModified();
516
516
  return ret;
517
517
  },
518
518
 
@@ -530,9 +530,9 @@ const methods = {
530
530
  */
531
531
 
532
532
  pop() {
533
+ this._markModified();
533
534
  const ret = [].pop.call(this);
534
535
  this._registerAtomic('$set', this);
535
- this._markModified();
536
536
  return ret;
537
537
  },
538
538
 
@@ -572,6 +572,7 @@ const methods = {
572
572
  const cur = this[arrayParentSymbol].get(this[arrayPathSymbol]);
573
573
  let i = cur.length;
574
574
  let mem;
575
+ this._markModified();
575
576
 
576
577
  while (i--) {
577
578
  mem = cur[i];
@@ -595,7 +596,6 @@ const methods = {
595
596
  this._registerAtomic('$pullAll', values);
596
597
  }
597
598
 
598
- this._markModified();
599
599
 
600
600
  // Might have modified child paths and then pulled, like
601
601
  // `doc.children[1].name = 'test';` followed by
@@ -657,7 +657,7 @@ const methods = {
657
657
  undefined, { skipDocumentArrayCast: true });
658
658
  let ret;
659
659
  const atomics = this[arrayAtomicsSymbol];
660
-
660
+ this._markModified();
661
661
  if (isOverwrite) {
662
662
  atomic.$each = values;
663
663
 
@@ -684,7 +684,6 @@ const methods = {
684
684
  }
685
685
 
686
686
  this._registerAtomic('$push', atomic);
687
- this._markModified();
688
687
  return ret;
689
688
  },
690
689
 
@@ -737,8 +736,8 @@ const methods = {
737
736
  return this;
738
737
  }
739
738
  const value = methods._cast.call(this, val, i);
740
- arr[i] = value;
741
739
  methods._markModified.call(this, i);
740
+ arr[i] = value;
742
741
  return this;
743
742
  },
744
743
 
@@ -763,9 +762,9 @@ const methods = {
763
762
 
764
763
  shift() {
765
764
  const arr = utils.isMongooseArray(this) ? this.__array : this;
765
+ this._markModified();
766
766
  const ret = [].shift.call(arr);
767
767
  this._registerAtomic('$set', this);
768
- this._markModified();
769
768
  return ret;
770
769
  },
771
770
 
@@ -890,9 +889,9 @@ const methods = {
890
889
  }
891
890
 
892
891
  const arr = utils.isMongooseArray(this) ? this.__array : this;
892
+ this._markModified();
893
893
  [].unshift.apply(arr, values);
894
894
  this._registerAtomic('$set', this);
895
- this._markModified();
896
895
  return this.length;
897
896
  }
898
897
  };