mongoose 6.2.10 → 6.3.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 (46) hide show
  1. package/.eslintrc.json +157 -157
  2. package/dist/browser.umd.js +2 -1693
  3. package/lib/aggregate.js +23 -31
  4. package/lib/collection.js +0 -7
  5. package/lib/cursor/ChangeStream.js +42 -2
  6. package/lib/cursor/QueryCursor.js +2 -0
  7. package/lib/document.js +13 -19
  8. package/lib/error/cast.js +7 -2
  9. package/lib/error/eachAsyncMultiError.js +41 -0
  10. package/lib/helpers/cursor/eachAsync.js +44 -12
  11. package/lib/helpers/indexes/applySchemaCollation.js +13 -0
  12. package/lib/helpers/indexes/isTextIndex.js +16 -0
  13. package/lib/helpers/model/discriminator.js +1 -3
  14. package/lib/helpers/populate/getModelsMapForPopulate.js +13 -10
  15. package/lib/helpers/query/applyGlobalOption.js +29 -0
  16. package/lib/helpers/query/castUpdate.js +3 -1
  17. package/lib/helpers/schematype/handleImmutable.js +4 -1
  18. package/lib/helpers/timestamps/setupTimestamps.js +2 -2
  19. package/lib/helpers/update/applyTimestampsToChildren.js +2 -2
  20. package/lib/helpers/update/applyTimestampsToUpdate.js +0 -1
  21. package/lib/index.js +11 -4
  22. package/lib/model.js +36 -36
  23. package/lib/query.js +59 -26
  24. package/lib/queryhelpers.js +11 -1
  25. package/lib/schema/SubdocumentPath.js +2 -1
  26. package/lib/schema/documentarray.js +2 -4
  27. package/lib/schema/objectid.js +0 -3
  28. package/lib/schema/string.js +24 -2
  29. package/lib/schema.js +117 -3
  30. package/lib/schematype.js +2 -3
  31. package/lib/types/DocumentArray/methods/index.js +1 -1
  32. package/lib/types/array/methods/index.js +9 -10
  33. package/lib/types/subdocument.js +29 -0
  34. package/lib/validoptions.js +1 -0
  35. package/package.json +13 -7
  36. package/tools/repl.js +2 -1
  37. package/tsconfig.json +2 -1
  38. package/types/aggregate.d.ts +223 -0
  39. package/types/connection.d.ts +2 -2
  40. package/types/cursor.d.ts +10 -4
  41. package/types/document.d.ts +3 -3
  42. package/types/index.d.ts +200 -215
  43. package/types/mongooseoptions.d.ts +10 -4
  44. package/CHANGELOG.md +0 -7227
  45. package/History.md +0 -1
  46. package/lib/helpers/query/applyGlobalMaxTimeMS.js +0 -15
@@ -19,13 +19,16 @@ module.exports = function(schematype) {
19
19
  };
20
20
 
21
21
  function createImmutableSetter(path, immutable) {
22
- return function immutableSetter(v) {
22
+ return function immutableSetter(v, _priorVal, _doc, options) {
23
23
  if (this == null || this.$__ == null) {
24
24
  return v;
25
25
  }
26
26
  if (this.isNew) {
27
27
  return v;
28
28
  }
29
+ if (options && options.overwriteImmutable) {
30
+ return v;
31
+ }
29
32
 
30
33
  const _immutable = typeof immutable === 'function' ?
31
34
  immutable.call(this, this) :
@@ -48,8 +48,8 @@ module.exports = function setupTimestamps(schema, timestamps) {
48
48
  currentTime() :
49
49
  this.ownerDocument().constructor.base.now();
50
50
 
51
- if (!skipCreatedAt && this.isNew && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
52
- this.$set(createdAt, defaultTimestamp);
51
+ if (!skipCreatedAt && (this.isNew || this.$isSubdocument) && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
52
+ this.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true });
53
53
  }
54
54
 
55
55
  if (!skipUpdatedAt && updatedAt && (this.isNew || this.$isModified())) {
@@ -15,7 +15,7 @@ function applyTimestampsToChildren(now, update, schema) {
15
15
  }
16
16
 
17
17
  const keys = Object.keys(update);
18
- const hasDollarKey = keys.some(key => key.startsWith('$'));
18
+ const hasDollarKey = keys.some(key => key[0] === '$');
19
19
 
20
20
  if (hasDollarKey) {
21
21
  if (update.$push) {
@@ -38,7 +38,7 @@ function applyTimestampsToChildren(now, update, schema) {
38
38
  }
39
39
  }
40
40
 
41
- const updateKeys = Object.keys(update).filter(key => !key.startsWith('$'));
41
+ const updateKeys = Object.keys(update).filter(key => key[0] !== '$');
42
42
  for (const key of updateKeys) {
43
43
  applyTimestampsToUpdateKey(schema, key, update, now);
44
44
  }
@@ -80,7 +80,6 @@ function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, optio
80
80
  }
81
81
 
82
82
  if (!skipCreatedAt && createdAt) {
83
-
84
83
  if (currentUpdate[createdAt]) {
85
84
  delete currentUpdate[createdAt];
86
85
  }
package/lib/index.js CHANGED
@@ -179,7 +179,9 @@ Mongoose.prototype.driver = driver;
179
179
  Mongoose.prototype.set = function(key, value) {
180
180
  const _mongoose = this instanceof Mongoose ? this : mongoose;
181
181
 
182
- if (VALID_OPTIONS.indexOf(key) === -1) throw new Error(`\`${key}\` is an invalid option.`);
182
+ if (VALID_OPTIONS.indexOf(key) === -1) {
183
+ throw new Error(`\`${key}\` is an invalid option.`);
184
+ }
183
185
 
184
186
  if (arguments.length === 1) {
185
187
  return _mongoose.options[key];
@@ -265,7 +267,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
265
267
  * @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
266
268
  * @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
267
269
  * @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
268
- * @return {Connection} the created Connection object. Connections are thenable, so you can do `await mongoose.createConnection()`
270
+ * @return {Connection} the created Connection object. Connections are not thenable, so you can not `await mongoose.createConnection()`. To await use mongoose.createConnection(uri).asPromise() instead.
269
271
  * @api public
270
272
  */
271
273
 
@@ -467,6 +469,7 @@ Mongoose.prototype.pluralize = function(fn) {
467
469
  */
468
470
 
469
471
  Mongoose.prototype.model = function(name, schema, collection, options) {
472
+
470
473
  const _mongoose = this instanceof Mongoose ? this : mongoose;
471
474
 
472
475
  if (typeof schema === 'string') {
@@ -519,7 +522,6 @@ Mongoose.prototype.model = function(name, schema, collection, options) {
519
522
  }
520
523
 
521
524
  const model = _mongoose._model(name, schema, collection, options);
522
-
523
525
  _mongoose.connection.models[name] = model;
524
526
  _mongoose.models[name] = model;
525
527
 
@@ -561,12 +563,17 @@ Mongoose.prototype._model = function(name, schema, collection, options) {
561
563
 
562
564
  const connection = options.connection || _mongoose.connection;
563
565
  model = _mongoose.Model.compile(model || name, schema, collection, connection, _mongoose);
564
-
565
566
  // Errors handled internally, so safe to ignore error
566
567
  model.init(function $modelInitNoop() {});
567
568
 
568
569
  connection.emit('model', model);
569
570
 
571
+ if (schema._applyDiscriminators != null) {
572
+ for (const disc of Object.keys(schema._applyDiscriminators)) {
573
+ model.discriminator(disc, schema._applyDiscriminators[disc]);
574
+ }
575
+ }
576
+
570
577
  return model;
571
578
  };
572
579
 
package/lib/model.js CHANGED
@@ -26,6 +26,7 @@ const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
26
26
  const applyHooks = require('./helpers/model/applyHooks');
27
27
  const applyMethods = require('./helpers/model/applyMethods');
28
28
  const applyProjection = require('./helpers/projection/applyProjection');
29
+ const applySchemaCollation = require('./helpers/indexes/applySchemaCollation');
29
30
  const applyStaticHooks = require('./helpers/model/applyStaticHooks');
30
31
  const applyStatics = require('./helpers/model/applyStatics');
31
32
  const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
@@ -1073,6 +1074,22 @@ Model.prototype.model = function model(name) {
1073
1074
  return this[modelDbSymbol].model(name);
1074
1075
  };
1075
1076
 
1077
+ /**
1078
+ * Returns another Model instance.
1079
+ *
1080
+ * #### Example:
1081
+ *
1082
+ * const doc = new Tank;
1083
+ * doc.model('User').findById(id, callback);
1084
+ *
1085
+ * @param {String} name model name
1086
+ * @api public
1087
+ */
1088
+
1089
+ Model.prototype.$model = function $model(name) {
1090
+ return this[modelDbSymbol].model(name);
1091
+ };
1092
+
1076
1093
  /**
1077
1094
  * Returns a document with `_id` only if at least one document exists in the database that matches
1078
1095
  * the given `filter`, and `null` otherwise.
@@ -1361,23 +1378,27 @@ Model.createCollection = function createCollection(options, callback) {
1361
1378
  }
1362
1379
 
1363
1380
  const schemaCollation = this &&
1364
- this.schema &&
1365
- this.schema.options &&
1366
- this.schema.options.collation || null;
1381
+ this.schema &&
1382
+ this.schema.options &&
1383
+ this.schema.options.collation;
1367
1384
  if (schemaCollation != null) {
1368
1385
  options = Object.assign({ collation: schemaCollation }, options);
1369
1386
  }
1370
1387
  const capped = this &&
1371
- this.schema &&
1372
- this.schema.options &&
1373
- this.schema.options.capped;
1374
- if (capped) {
1375
- options = Object.assign({ capped: true }, capped, options);
1388
+ this.schema &&
1389
+ this.schema.options &&
1390
+ this.schema.options.capped;
1391
+ if (capped != null) {
1392
+ if (typeof capped === 'number') {
1393
+ options = Object.assign({ capped: true, size: capped }, options);
1394
+ } else if (typeof capped === 'object') {
1395
+ options = Object.assign({ capped: true }, capped, options);
1396
+ }
1376
1397
  }
1377
1398
  const timeseries = this &&
1378
- this.schema &&
1379
- this.schema.options &&
1380
- this.schema.options.timeseries;
1399
+ this.schema &&
1400
+ this.schema.options &&
1401
+ this.schema.options.timeseries;
1381
1402
  if (timeseries != null) {
1382
1403
  options = Object.assign({ timeseries }, options);
1383
1404
  if (options.expireAfterSeconds != null) {
@@ -1562,6 +1583,7 @@ Model.cleanIndexes = function cleanIndexes(callback) {
1562
1583
 
1563
1584
  for (const [schemaIndexKeysObject, schemaIndexOptions] of schemaIndexes) {
1564
1585
  const options = decorateDiscriminatorIndexOptions(this.schema, utils.clone(schemaIndexOptions));
1586
+ applySchemaCollation(schemaIndexKeysObject, options, this.schema.options);
1565
1587
 
1566
1588
  if (isIndexEqual(schemaIndexKeysObject, options, dbIndex)) {
1567
1589
  found = true;
@@ -1780,26 +1802,17 @@ function _ensureIndexes(model, options, callback) {
1780
1802
 
1781
1803
  const indexFields = utils.clone(index[0]);
1782
1804
  const indexOptions = utils.clone(index[1]);
1783
- let isTextIndex = false;
1784
- for (const key of Object.keys(indexFields)) {
1785
- if (indexFields[key] === 'text') {
1786
- isTextIndex = true;
1787
- }
1788
- }
1805
+
1789
1806
  delete indexOptions._autoIndex;
1790
1807
  decorateDiscriminatorIndexOptions(model.schema, indexOptions);
1791
1808
  applyWriteConcern(model.schema, indexOptions);
1809
+ applySchemaCollation(indexFields, indexOptions, model.schema.options);
1792
1810
 
1793
1811
  indexSingleStart(indexFields, options);
1794
1812
 
1795
1813
  if ('background' in options) {
1796
1814
  indexOptions.background = options.background;
1797
1815
  }
1798
- if (model.schema.options.hasOwnProperty('collation') &&
1799
- !indexOptions.hasOwnProperty('collation') &&
1800
- !isTextIndex) {
1801
- indexOptions.collation = model.schema.options.collation;
1802
- }
1803
1816
 
1804
1817
  model.collection.createIndex(indexFields, indexOptions, utils.tick(function(err, name) {
1805
1818
  indexSingleDone(err, indexFields, indexOptions, name);
@@ -2155,14 +2168,7 @@ Model.find = function find(conditions, projection, options, callback) {
2155
2168
 
2156
2169
  const mq = new this.Query({}, {}, this, this.$__collection);
2157
2170
  mq.select(projection);
2158
-
2159
2171
  mq.setOptions(options);
2160
- if (this.schema.discriminatorMapping &&
2161
- this.schema.discriminatorMapping.isRoot &&
2162
- mq.selectedInclusively()) {
2163
- // Need to select discriminator key because original schema doesn't have it
2164
- mq.select(this.schema.options.discriminatorKey);
2165
- }
2166
2172
 
2167
2173
  callback = this.$handleCallbackError(callback);
2168
2174
 
@@ -2268,11 +2274,6 @@ Model.findOne = function findOne(conditions, projection, options, callback) {
2268
2274
  const mq = new this.Query({}, {}, this, this.$__collection);
2269
2275
  mq.select(projection);
2270
2276
  mq.setOptions(options);
2271
- if (this.schema.discriminatorMapping &&
2272
- this.schema.discriminatorMapping.isRoot &&
2273
- mq.selectedInclusively()) {
2274
- mq.select(this.schema.options.discriminatorKey);
2275
- }
2276
2277
 
2277
2278
  callback = this.$handleCallbackError(callback);
2278
2279
  return mq.findOne(conditions, callback);
@@ -4219,7 +4220,6 @@ Model.aggregate = function aggregate(pipeline, options, callback) {
4219
4220
 
4220
4221
  const aggregate = new Aggregate(pipeline || []);
4221
4222
  aggregate.model(this);
4222
-
4223
4223
  if (options != null) {
4224
4224
  aggregate.option(options);
4225
4225
  }
@@ -4816,7 +4816,6 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4816
4816
  o[schema.options.versionKey] = Number;
4817
4817
  schema.add(o);
4818
4818
  }
4819
-
4820
4819
  let model;
4821
4820
  if (typeof name === 'function' && name.prototype instanceof Model) {
4822
4821
  model = name;
@@ -4860,6 +4859,7 @@ Model.compile = function compile(name, schema, collectionName, connection, base)
4860
4859
  model.model = function model(name) {
4861
4860
  return this.db.model(name);
4862
4861
  };
4862
+
4863
4863
  model.db = connection;
4864
4864
  model.prototype.db = connection;
4865
4865
  model.prototype[modelDbSymbol] = connection;
package/lib/query.js CHANGED
@@ -11,7 +11,8 @@ const MongooseError = require('./error/mongooseError');
11
11
  const ObjectParameterError = require('./error/objectParameter');
12
12
  const QueryCursor = require('./cursor/QueryCursor');
13
13
  const ReadPreference = require('./driver').get().ReadPreference;
14
- const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
14
+ const ValidationError = require('./error/validation');
15
+ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption');
15
16
  const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
16
17
  const cast = require('./cast');
17
18
  const castArrayFilters = require('./helpers/update/castArrayFilters');
@@ -2191,6 +2192,7 @@ function _castArrayFilters(query) {
2191
2192
  * @api private
2192
2193
  */
2193
2194
  Query.prototype._find = wrapThunk(function(callback) {
2195
+
2194
2196
  this._castConditions();
2195
2197
 
2196
2198
  if (this.error() != null) {
@@ -2209,6 +2211,7 @@ Query.prototype._find = wrapThunk(function(callback) {
2209
2211
  const userProvidedFields = _this._userProvidedFields || {};
2210
2212
 
2211
2213
  applyGlobalMaxTimeMS(this.options, this.model);
2214
+ applyGlobalDiskUse(this.options, this.model);
2212
2215
 
2213
2216
  // Separate options to pass down to `completeMany()` in case we need to
2214
2217
  // set a session on the document
@@ -2227,8 +2230,15 @@ Query.prototype._find = wrapThunk(function(callback) {
2227
2230
  if (this.options.explain) {
2228
2231
  return callback(null, docs);
2229
2232
  }
2230
-
2231
2233
  if (!mongooseOptions.populate) {
2234
+ const versionKey = _this.schema.options.versionKey;
2235
+ if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
2236
+ docs.forEach((doc) => {
2237
+ if (versionKey in doc) {
2238
+ delete doc[versionKey];
2239
+ }
2240
+ });
2241
+ }
2232
2242
  return mongooseOptions.lean ?
2233
2243
  callback(null, docs) :
2234
2244
  completeMany(_this.model, docs, fields, userProvidedFields, completeManyOptions, callback);
@@ -2410,6 +2420,12 @@ Query.prototype._completeOne = function(doc, res, callback) {
2410
2420
  }
2411
2421
 
2412
2422
  if (!mongooseOptions.populate) {
2423
+ const versionKey = this.schema.options.versionKey;
2424
+ if (mongooseOptions.lean && mongooseOptions.lean.versionKey === false && versionKey) {
2425
+ if (versionKey in doc) {
2426
+ delete doc[versionKey];
2427
+ }
2428
+ }
2413
2429
  return mongooseOptions.lean ?
2414
2430
  _completeOneLean(doc, res, options, callback) :
2415
2431
  completeOne(model, doc, res, options, projection, userProvidedFields,
@@ -2452,8 +2468,8 @@ Query.prototype._findOne = wrapThunk(function(callback) {
2452
2468
 
2453
2469
  this._applyPaths();
2454
2470
  this._fields = this._castFields(this._fields);
2455
-
2456
2471
  applyGlobalMaxTimeMS(this.options, this.model);
2472
+ applyGlobalDiskUse(this.options, this.model);
2457
2473
 
2458
2474
  // don't pass in the conditions because we already merged them in
2459
2475
  Query.base.findOne.call(this, {}, (err, doc) => {
@@ -2566,6 +2582,7 @@ Query.prototype._count = wrapThunk(function(callback) {
2566
2582
  }
2567
2583
 
2568
2584
  applyGlobalMaxTimeMS(this.options, this.model);
2585
+ applyGlobalDiskUse(this.options, this.model);
2569
2586
 
2570
2587
  const conds = this._conditions;
2571
2588
  const options = this._optionsForExec();
@@ -2593,6 +2610,7 @@ Query.prototype._countDocuments = wrapThunk(function(callback) {
2593
2610
  }
2594
2611
 
2595
2612
  applyGlobalMaxTimeMS(this.options, this.model);
2613
+ applyGlobalDiskUse(this.options, this.model);
2596
2614
 
2597
2615
  const conds = this._conditions;
2598
2616
  const options = this._optionsForExec();
@@ -2813,6 +2831,7 @@ Query.prototype.__distinct = wrapThunk(function __distinct(callback) {
2813
2831
  }
2814
2832
 
2815
2833
  applyGlobalMaxTimeMS(this.options, this.model);
2834
+ applyGlobalDiskUse(this.options, this.model);
2816
2835
 
2817
2836
  const options = this._optionsForExec();
2818
2837
 
@@ -3713,7 +3732,6 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options, callb
3713
3732
  */
3714
3733
  Query.prototype._findOneAndReplace = wrapThunk(function(callback) {
3715
3734
  this._castConditions();
3716
-
3717
3735
  if (this.error() != null) {
3718
3736
  callback(this.error());
3719
3737
  return null;
@@ -3724,9 +3742,6 @@ Query.prototype._findOneAndReplace = wrapThunk(function(callback) {
3724
3742
  convertNewToReturnDocument(options);
3725
3743
  let fields = null;
3726
3744
 
3727
- let castedDoc = new this.model(this._update, null, true);
3728
- this._update = castedDoc;
3729
-
3730
3745
  this._applyPaths();
3731
3746
  if (this._fields != null) {
3732
3747
  options.projection = this._castFields(utils.clone(this._fields));
@@ -3737,7 +3752,34 @@ Query.prototype._findOneAndReplace = wrapThunk(function(callback) {
3737
3752
  }
3738
3753
  }
3739
3754
 
3740
- castedDoc.$validate(err => {
3755
+ const runValidators = _getOption(this, 'runValidators', false);
3756
+ if (runValidators === false) {
3757
+ try {
3758
+ this._update = this._castUpdate(this._update, true);
3759
+ } catch (err) {
3760
+ const validationError = new ValidationError();
3761
+ validationError.errors[err.path] = err;
3762
+ callback(validationError);
3763
+ return null;
3764
+ }
3765
+
3766
+ this._collection.collection.findOneAndReplace(filter, this._update || {}, options, _wrapThunkCallback(this, (err, res) => {
3767
+ if (err) {
3768
+ return callback(err);
3769
+ }
3770
+
3771
+ const doc = res.value;
3772
+
3773
+ return this._completeOne(doc, res, callback);
3774
+ }));
3775
+
3776
+ return;
3777
+ }
3778
+
3779
+
3780
+ let castedDoc = new this.model(this._update, null, true);
3781
+ this._update = castedDoc;
3782
+ castedDoc.validate(err => {
3741
3783
  if (err != null) {
3742
3784
  return callback(err);
3743
3785
  }
@@ -3860,7 +3902,11 @@ Query.prototype._findAndModify = function(type, callback) {
3860
3902
  }
3861
3903
 
3862
3904
  if (!isOverwriting) {
3863
- this._update = castDoc(this, opts.overwrite);
3905
+ try {
3906
+ this._update = this._castUpdate(this._update, opts.overwrite);
3907
+ } catch (err) {
3908
+ return callback(err);
3909
+ }
3864
3910
  const _opts = Object.assign({}, opts, {
3865
3911
  setDefaultsOnInsert: this._mongooseOptions.setDefaultsOnInsert
3866
3912
  });
@@ -4055,10 +4101,10 @@ function _updateThunk(op, callback) {
4055
4101
  }
4056
4102
  this._update = new this.model(this._update, null, true);
4057
4103
  } else {
4058
- this._update = castDoc(this, options.overwrite);
4059
-
4060
- if (this._update instanceof Error) {
4061
- callback(this._update);
4104
+ try {
4105
+ this._update = this._castUpdate(this._update, options.overwrite);
4106
+ } catch (err) {
4107
+ callback(err);
4062
4108
  return null;
4063
4109
  }
4064
4110
 
@@ -4897,19 +4943,6 @@ function castQuery(query) {
4897
4943
  }
4898
4944
  }
4899
4945
 
4900
- /*!
4901
- * castDoc
4902
- * @api private
4903
- */
4904
-
4905
- function castDoc(query, overwrite) {
4906
- try {
4907
- return query._castUpdate(query._update, overwrite);
4908
- } catch (err) {
4909
- return err;
4910
- }
4911
- }
4912
-
4913
4946
  /**
4914
4947
  * Specifies paths which should be populated with other documents.
4915
4948
  *
@@ -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
@@ -167,10 +167,11 @@ SubdocumentPath.prototype.cast = function(val, doc, init, priorVal, options) {
167
167
  const path = this.path;
168
168
  const selected = Object.keys(parentSelected).reduce((obj, key) => {
169
169
  if (key.startsWith(path + '.')) {
170
+ obj = obj || {};
170
171
  obj[key.substring(path.length + 1)] = parentSelected[key];
171
172
  }
172
173
  return obj;
173
- }, {});
174
+ }, null);
174
175
  options = Object.assign({}, options, { priorDoc: priorVal });
175
176
  if (init) {
176
177
  subdoc = new Constructor(void 0, selected, doc);
@@ -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
 
@@ -394,7 +393,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
394
393
 
395
394
  if (!Array.isArray(value)) {
396
395
  if (!init && !DocumentArrayPath.options.castNonArrays) {
397
- throw new CastError('DocumentArray', util.inspect(value), this.path, null, this);
396
+ throw new CastError('DocumentArray', value, this.path, null, this);
398
397
  }
399
398
  // gh-2442 mark whole array as modified if we're initializing a doc from
400
399
  // the db and the path isn't an array in the document
@@ -486,8 +485,7 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
486
485
  // see gh-746
487
486
  rawArray[i] = subdoc;
488
487
  } catch (error) {
489
- const valueInErrorMessage = util.inspect(rawArray[i]);
490
- throw new CastError('embedded', valueInErrorMessage,
488
+ throw new CastError('embedded', rawArray[i],
491
489
  value[arrayPathSymbol], error, this);
492
490
  }
493
491
  }
@@ -282,11 +282,8 @@ function resetId(v) {
282
282
  if (this instanceof Document) {
283
283
  if (v === void 0) {
284
284
  const _v = new oid();
285
- this.$__._id = _v;
286
285
  return _v;
287
286
  }
288
-
289
- this.$__._id = v;
290
287
  }
291
288
 
292
289
  return v;
@@ -612,6 +612,10 @@ function handleSingle(val) {
612
612
  return this.castForQuery(val);
613
613
  }
614
614
 
615
+ /*!
616
+ * ignore
617
+ */
618
+
615
619
  function handleArray(val) {
616
620
  const _this = this;
617
621
  if (!Array.isArray(val)) {
@@ -622,14 +626,32 @@ function handleArray(val) {
622
626
  });
623
627
  }
624
628
 
629
+ /*!
630
+ * ignore
631
+ */
632
+
633
+ function handleSingleNoSetters(val) {
634
+ if (val == null) {
635
+ return this._castNullish(val);
636
+ }
637
+
638
+ return this.cast(val, this);
639
+ }
640
+
625
641
  const $conditionalHandlers = utils.options(SchemaType.prototype.$conditionalHandlers, {
626
642
  $all: handleArray,
627
643
  $gt: handleSingle,
628
644
  $gte: handleSingle,
629
645
  $lt: handleSingle,
630
646
  $lte: handleSingle,
631
- $options: String,
632
- $regex: handleSingle,
647
+ $options: handleSingleNoSetters,
648
+ $regex: function handle$regex(val) {
649
+ if (Object.prototype.toString.call(val) === '[object RegExp]') {
650
+ return val;
651
+ }
652
+
653
+ return handleSingleNoSetters.call(this, val);
654
+ },
633
655
  $not: handleSingle
634
656
  });
635
657