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
package/lib/aggregate.js CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  const AggregationCursor = require('./cursor/AggregationCursor');
8
8
  const Query = require('./query');
9
- const applyGlobalMaxTimeMS = require('./helpers/query/applyGlobalMaxTimeMS');
9
+ const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption');
10
10
  const getConstructorName = require('./helpers/getConstructorName');
11
11
  const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
12
12
  const promiseOrCallback = require('./helpers/promiseOrCallback');
@@ -15,6 +15,8 @@ const utils = require('./utils');
15
15
  const read = Query.prototype.read;
16
16
  const readConcern = Query.prototype.readConcern;
17
17
 
18
+ const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']);
19
+
18
20
  /**
19
21
  * Aggregate constructor used for building aggregation pipelines. Do not
20
22
  * instantiate this class directly, use [Model.aggregate()](/docs/api.html#model_Model.aggregate) instead.
@@ -63,19 +65,21 @@ function Aggregate(pipeline, model) {
63
65
  * Contains options passed down to the [aggregate command](https://docs.mongodb.com/manual/reference/command/aggregate/).
64
66
  * Supported options are:
65
67
  *
66
- * - `readPreference`
67
- * - [`cursor`](./api.html#aggregate_Aggregate-cursor)
68
- * - [`explain`](./api.html#aggregate_Aggregate-explain)
69
68
  * - [`allowDiskUse`](./api.html#aggregate_Aggregate-allowDiskUse)
70
- * - `maxTimeMS`
71
69
  * - `bypassDocumentValidation`
72
- * - `raw`
73
- * - `promoteLongs`
74
- * - `promoteValues`
75
- * - `promoteBuffers`
76
70
  * - [`collation`](./api.html#aggregate_Aggregate-collation)
77
71
  * - `comment`
72
+ * - [`cursor`](./api.html#aggregate_Aggregate-cursor)
73
+ * - [`explain`](./api.html#aggregate_Aggregate-explain)
74
+ * - `fieldsAsRaw`
75
+ * - hint
76
+ * - let
77
+ * - `maxTimeMS`
78
+ * - `raw`
79
+ * - `readConcern`
80
+ * - `readPreference`
78
81
  * - [`session`](./api.html#aggregate_Aggregate-session)
82
+ * - `writeConcern`
79
83
  *
80
84
  * @property options
81
85
  * @memberOf Aggregate
@@ -374,7 +378,7 @@ Aggregate.prototype.unwind = function() {
374
378
  res.push({ $unwind: arg });
375
379
  } else if (typeof arg === 'string') {
376
380
  res.push({
377
- $unwind: (arg && arg.startsWith('$')) ? arg : '$' + arg
381
+ $unwind: (arg[0] === '$') ? arg : '$' + arg
378
382
  });
379
383
  } else {
380
384
  throw new Error('Invalid arg "' + arg + '" to unwind(), ' +
@@ -399,7 +403,7 @@ Aggregate.prototype.unwind = function() {
399
403
  * aggregate.replaceRoot({ x: { $concat: ['$this', '$that'] } });
400
404
  *
401
405
  * @see $replaceRoot https://docs.mongodb.org/manual/reference/operator/aggregation/replaceRoot
402
- * @param {String|Object} the field or document which will become the new root document
406
+ * @param {String|Object} newRoot the field or document which will become the new root document
403
407
  * @return {Aggregate}
404
408
  * @api public
405
409
  */
@@ -428,13 +432,13 @@ Aggregate.prototype.replaceRoot = function(newRoot) {
428
432
  * aggregate.count("userCount");
429
433
  *
430
434
  * @see $count https://docs.mongodb.org/manual/reference/operator/aggregation/count
431
- * @param {String} the name of the count field
435
+ * @param {String} fieldName The name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character.
432
436
  * @return {Aggregate}
433
437
  * @api public
434
438
  */
435
439
 
436
- Aggregate.prototype.count = function(countName) {
437
- return this.append({ $count: countName });
440
+ Aggregate.prototype.count = function(fieldName) {
441
+ return this.append({ $count: fieldName });
438
442
  };
439
443
 
440
444
  /**
@@ -460,7 +464,7 @@ Aggregate.prototype.sortByCount = function(arg) {
460
464
  return this.append({ $sortByCount: arg });
461
465
  } else if (typeof arg === 'string') {
462
466
  return this.append({
463
- $sortByCount: (arg && arg.startsWith('$')) ? arg : '$' + arg
467
+ $sortByCount: (arg[0] === '$') ? arg : '$' + arg
464
468
  });
465
469
  } else {
466
470
  throw new TypeError('Invalid arg "' + arg + '" to sortByCount(), ' +
@@ -621,9 +625,6 @@ Aggregate.prototype.unionWith = function(options) {
621
625
  */
622
626
 
623
627
  Aggregate.prototype.read = function(pref, tags) {
624
- if (!this.options) {
625
- this.options = {};
626
- }
627
628
  read.call(this, pref, tags);
628
629
  return this;
629
630
  };
@@ -642,9 +643,6 @@ Aggregate.prototype.read = function(pref, tags) {
642
643
  */
643
644
 
644
645
  Aggregate.prototype.readConcern = function(level) {
645
- if (!this.options) {
646
- this.options = {};
647
- }
648
646
  readConcern.call(this, level);
649
647
  return this;
650
648
  };
@@ -678,9 +676,9 @@ Aggregate.prototype.readConcern = function(level) {
678
676
 
679
677
  Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) {
680
678
  if (arguments.length === 3) {
681
- if ((typeof thenExpr === 'string' && !thenExpr.startsWith('$$')) ||
682
- (typeof elseExpr === 'string' && !elseExpr.startsWith('$$'))) {
683
- throw new Error('If thenExpr or elseExpr is string, it must start with $$. e.g. $$DESCEND, $$PRUNE, $$KEEP');
679
+ if ((typeof thenExpr === 'string' && !validRedactStringValues.has(thenExpr)) ||
680
+ (typeof elseExpr === 'string' && !validRedactStringValues.has(elseExpr))) {
681
+ throw new Error('If thenExpr or elseExpr is string, it must be either $$DESCEND, $$PRUNE or $$KEEP');
684
682
  }
685
683
 
686
684
  expression = {
@@ -773,7 +771,6 @@ Aggregate.prototype.explain = function(verbosity, callback) {
773
771
  * await Model.aggregate([{ $match: { foo: 'bar' } }]).allowDiskUse(true);
774
772
  *
775
773
  * @param {Boolean} value Should tell server it can use hard drive to store data during aggregation.
776
- * @param {Array} [tags] optional tags for this query
777
774
  * @see mongodb https://docs.mongodb.org/manual/reference/command/aggregate/
778
775
  */
779
776
 
@@ -865,9 +862,6 @@ Aggregate.prototype.option = function(value) {
865
862
  */
866
863
 
867
864
  Aggregate.prototype.cursor = function(options) {
868
- if (!this.options) {
869
- this.options = {};
870
- }
871
865
  this.options.cursor = options || {};
872
866
  return new AggregationCursor(this); // return this;
873
867
  };
@@ -886,9 +880,6 @@ Aggregate.prototype.cursor = function(options) {
886
880
  */
887
881
 
888
882
  Aggregate.prototype.collation = function(collation) {
889
- if (!this.options) {
890
- this.options = {};
891
- }
892
883
  this.options.collation = collation;
893
884
  return this;
894
885
  };
@@ -982,6 +973,7 @@ Aggregate.prototype.exec = function(callback) {
982
973
  const collection = this._model.collection;
983
974
 
984
975
  applyGlobalMaxTimeMS(this.options, model);
976
+ applyGlobalDiskUse(this.options, model);
985
977
 
986
978
  if (this.options && this.options.cursor) {
987
979
  return new AggregationCursor(this);
package/lib/collection.js CHANGED
@@ -23,13 +23,6 @@ function Collection(name, conn, opts) {
23
23
  if (opts === void 0) {
24
24
  opts = {};
25
25
  }
26
- if (opts.capped === void 0) {
27
- opts.capped = {};
28
- }
29
-
30
- if (typeof opts.capped === 'number') {
31
- opts.capped = { size: opts.capped };
32
- }
33
26
 
34
27
  this.opts = opts;
35
28
  this.name = name;
@@ -16,6 +16,7 @@ class ChangeStream extends EventEmitter {
16
16
 
17
17
  this.driverChangeStream = null;
18
18
  this.closed = false;
19
+ this.bindedEvents = false;
19
20
  this.pipeline = pipeline;
20
21
  this.options = options;
21
22
 
@@ -27,21 +28,60 @@ class ChangeStream extends EventEmitter {
27
28
  }
28
29
 
29
30
  this.driverChangeStream = driverChangeStream;
30
- this._bindEvents();
31
31
  this.emit('ready');
32
32
  });
33
33
  }
34
34
 
35
35
  _bindEvents() {
36
+ if (this.bindedEvents) {
37
+ return;
38
+ }
39
+
40
+ this.bindedEvents = true;
41
+
42
+ if (this.driverChangeStream == null) {
43
+ this.once('ready', () => {
44
+ this.driverChangeStream.on('close', () => {
45
+ this.closed = true;
46
+ });
47
+
48
+ ['close', 'change', 'end', 'error'].forEach(ev => {
49
+ this.driverChangeStream.on(ev, data => this.emit(ev, data));
50
+ });
51
+ });
52
+
53
+ return;
54
+ }
55
+
36
56
  this.driverChangeStream.on('close', () => {
37
57
  this.closed = true;
38
58
  });
39
59
 
40
60
  ['close', 'change', 'end', 'error'].forEach(ev => {
41
- this.driverChangeStream.on(ev, data => this.emit(ev, data));
61
+ this.driverChangeStream.on(ev, data => {
62
+ this.emit(ev, data);
63
+ });
42
64
  });
43
65
  }
44
66
 
67
+ hasNext(cb) {
68
+ return this.driverChangeStream.hasNext(cb);
69
+ }
70
+
71
+ next(cb) {
72
+ return this.driverChangeStream.next(cb);
73
+ }
74
+
75
+ on(event, handler) {
76
+ this._bindEvents();
77
+ return super.on(event, handler);
78
+ }
79
+
80
+ once(event, handler) {
81
+ this._bindEvents();
82
+ return super.once(event, handler);
83
+ }
84
+
45
85
  _queue(cb) {
46
86
  this.once('ready', () => cb());
47
87
  }
@@ -225,6 +225,8 @@ QueryCursor.prototype.next = function(callback) {
225
225
  * @param {Function} fn
226
226
  * @param {Object} [options]
227
227
  * @param {Number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
228
+ * @param {Number} [options.batchSize] if set, will call `fn()` with arrays of documents with length at most `batchSize`
229
+ * @param {Boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error.
228
230
  * @param {Function} [callback] executed when all docs have been processed
229
231
  * @return {Promise}
230
232
  * @api public
package/lib/document.js CHANGED
@@ -94,7 +94,7 @@ function Document(obj, fields, skipId, options) {
94
94
  this.$__ = new InternalCache();
95
95
  this.$isNew = 'isNew' in options ? options.isNew : true;
96
96
 
97
- if ('priorDoc' in options) {
97
+ if (options.priorDoc != null) {
98
98
  this.$__.priorDoc = options.priorDoc;
99
99
  }
100
100
 
@@ -119,7 +119,7 @@ function Document(obj, fields, skipId, options) {
119
119
  fields = undefined;
120
120
  } else {
121
121
  this.$__.strictMode = schema.options.strict;
122
- if (fields !== undefined) {
122
+ if (fields != null) {
123
123
  this.$__.selected = fields;
124
124
  }
125
125
  }
@@ -177,8 +177,6 @@ function Document(obj, fields, skipId, options) {
177
177
  $__applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults);
178
178
  }
179
179
 
180
- this.$__._id = this._id;
181
-
182
180
  if (!this.$__.strictMode && obj) {
183
181
  const _this = this;
184
182
  const keys = Object.keys(this._doc);
@@ -743,8 +741,6 @@ Document.prototype.$__init = function(doc, opts) {
743
741
  this.$emit('init', this);
744
742
  this.constructor.emit('init', this);
745
743
 
746
- this.$__._id = this._id;
747
-
748
744
  const hasIncludedChildren = this.$__.exclude === false && this.$__.fields ?
749
745
  $__hasIncludedChildren(this.$__.fields) :
750
746
  null;
@@ -1052,7 +1048,6 @@ Document.prototype.$set = function $set(path, val, type, options) {
1052
1048
  const merge = options.merge;
1053
1049
  const adhoc = type && type !== true;
1054
1050
  const constructing = type === true;
1055
- const typeKey = this.$__schema.options.typeKey;
1056
1051
  let adhocs;
1057
1052
  let keys;
1058
1053
  let i = 0;
@@ -1154,14 +1149,15 @@ Document.prototype.$set = function $set(path, val, type, options) {
1154
1149
  }
1155
1150
  }
1156
1151
 
1157
- // Ensure all properties are in correct order by deleting and recreating every property.
1158
- for (const key of Object.keys(this.$__schema.tree)) {
1159
- if (this._doc.hasOwnProperty(key)) {
1160
- const val = this._doc[key];
1161
- delete this._doc[key];
1162
- this._doc[key] = val;
1163
- }
1152
+ // Ensure all properties are in correct order
1153
+ const orderedDoc = {};
1154
+ const orderedKeys = Object.keys(this.$__schema.tree);
1155
+ for (let i = 0, len = orderedKeys.length; i < len; ++i) {
1156
+ (key = orderedKeys[i]) &&
1157
+ (this._doc.hasOwnProperty(key)) &&
1158
+ (orderedDoc[key] = undefined);
1164
1159
  }
1160
+ this._doc = Object.assign(orderedDoc, this._doc);
1165
1161
 
1166
1162
  return this;
1167
1163
  }
@@ -1385,6 +1381,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1385
1381
  }
1386
1382
 
1387
1383
  let popOpts;
1384
+ const typeKey = this.$__schema.options.typeKey;
1388
1385
  if (schema.options &&
1389
1386
  Array.isArray(schema.options[typeKey]) &&
1390
1387
  schema.options[typeKey].length &&
@@ -1404,7 +1401,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1404
1401
  // later in `$__set()` because we don't take `_doc` when we iterate through
1405
1402
  // a single nested doc. That's to make sure we get the correct context.
1406
1403
  // Otherwise we would double-call the setter, see gh-7196.
1407
- val = schema.applySetters(val, this, false, priorVal);
1404
+ val = schema.applySetters(val, this, false, priorVal, options);
1408
1405
  }
1409
1406
 
1410
1407
  if (Array.isArray(val) &&
@@ -2485,10 +2482,7 @@ function _getPathsToValidate(doc) {
2485
2482
  if (subdoc.$basePath) {
2486
2483
  // Remove child paths for now, because we'll be validating the whole
2487
2484
  // subdoc
2488
- if (!subdoc.$__.fullPath) {
2489
- subdoc.ownerDocument();
2490
- }
2491
- const fullPathToSubdoc = subdoc.$__.fullPath;
2485
+ const fullPathToSubdoc = subdoc.$__fullPathWithIndexes();
2492
2486
 
2493
2487
  for (const p of paths) {
2494
2488
  if (p === null || p.startsWith(fullPathToSubdoc + '.')) {
package/lib/error/cast.js CHANGED
@@ -23,7 +23,7 @@ class CastError extends MongooseError {
23
23
  const stringValue = getStringValue(value);
24
24
  const valueType = getValueType(value);
25
25
  const messageFormat = getMessageFormat(schemaType);
26
- const msg = formatMessage(null, type, stringValue, path, messageFormat, valueType);
26
+ const msg = formatMessage(null, type, stringValue, path, messageFormat, valueType, reason);
27
27
  super(msg);
28
28
  this.init(type, value, path, reason, schemaType);
29
29
  } else {
@@ -122,7 +122,7 @@ function getMessageFormat(schemaType) {
122
122
  * ignore
123
123
  */
124
124
 
125
- function formatMessage(model, kind, stringValue, path, messageFormat, valueType) {
125
+ function formatMessage(model, kind, stringValue, path, messageFormat, valueType, reason) {
126
126
  if (messageFormat != null) {
127
127
  let ret = messageFormat.
128
128
  replace('{KIND}', kind).
@@ -140,6 +140,11 @@ function formatMessage(model, kind, stringValue, path, messageFormat, valueType)
140
140
  if (model != null) {
141
141
  ret += ' for model "' + model.modelName + '"';
142
142
  }
143
+ if (reason != null &&
144
+ typeof reason.constructor === 'function' &&
145
+ reason.constructor.name !== 'AssertionError') {
146
+ ret += ' because of "' + reason.constructor.name + '"';
147
+ }
143
148
  return ret;
144
149
  }
145
150
  }
@@ -0,0 +1,41 @@
1
+ /*!
2
+ * Module dependencies.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const MongooseError = require('./');
8
+
9
+
10
+ /**
11
+ * If `eachAsync()` is called with `continueOnError: true`, there can be
12
+ * multiple errors. This error class contains an `errors` property, which
13
+ * contains an array of all errors that occurred in `eachAsync()`.
14
+ *
15
+ * @api private
16
+ */
17
+
18
+ class EachAsyncMultiError extends MongooseError {
19
+ /**
20
+ * @param {String} connectionString
21
+ */
22
+ constructor(errors) {
23
+ let preview = errors.map(e => e.message).join(', ');
24
+ if (preview.length > 50) {
25
+ preview = preview.slice(0, 50) + '...';
26
+ }
27
+ super(`eachAsync() finished with ${errors.length} errors: ${preview}`);
28
+
29
+ this.errors = errors;
30
+ }
31
+ }
32
+
33
+ Object.defineProperty(EachAsyncMultiError.prototype, 'name', {
34
+ value: 'EachAsyncMultiError'
35
+ });
36
+
37
+ /*!
38
+ * exports
39
+ */
40
+
41
+ module.exports = EachAsyncMultiError;
@@ -4,6 +4,7 @@
4
4
  * Module dependencies.
5
5
  */
6
6
 
7
+ const EachAsyncMultiError = require('../../error/eachAsyncMultiError');
7
8
  const immediate = require('../immediate');
8
9
  const promiseOrCallback = require('../promiseOrCallback');
9
10
 
@@ -24,10 +25,11 @@ const promiseOrCallback = require('../promiseOrCallback');
24
25
  module.exports = function eachAsync(next, fn, options, callback) {
25
26
  const parallel = options.parallel || 1;
26
27
  const batchSize = options.batchSize;
28
+ const continueOnError = options.continueOnError;
29
+ const aggregatedErrors = [];
27
30
  const enqueue = asyncQueue();
28
31
 
29
32
  return promiseOrCallback(callback, cb => {
30
-
31
33
  if (batchSize != null) {
32
34
  if (typeof batchSize !== 'number') {
33
35
  throw new TypeError('batchSize must be a number');
@@ -62,14 +64,22 @@ module.exports = function eachAsync(next, fn, options, callback) {
62
64
  return done();
63
65
  }
64
66
  if (err != null) {
65
- error = err;
66
- finalCallback(err);
67
- return done();
67
+ if (continueOnError) {
68
+ aggregatedErrors.push(err);
69
+ } else {
70
+ error = err;
71
+ finalCallback(err);
72
+ return done();
73
+ }
68
74
  }
69
75
  if (doc == null) {
70
76
  drained = true;
71
77
  if (handleResultsInProgress <= 0) {
72
- finalCallback(null);
78
+ const finalErr = continueOnError ?
79
+ createEachAsyncMultiError(aggregatedErrors) :
80
+ error;
81
+
82
+ finalCallback(finalErr);
73
83
  } else if (batchSize && documentsBatch.length) {
74
84
  handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
75
85
  }
@@ -102,11 +112,18 @@ module.exports = function eachAsync(next, fn, options, callback) {
102
112
  --handleResultsInProgress;
103
113
  }
104
114
  if (err != null) {
105
- error = err;
106
- return finalCallback(err);
115
+ if (continueOnError) {
116
+ aggregatedErrors.push(err);
117
+ } else {
118
+ error = err;
119
+ return finalCallback(err);
120
+ }
107
121
  }
108
122
  if (drained && handleResultsInProgress <= 0) {
109
- return finalCallback(null);
123
+ const finalErr = continueOnError ?
124
+ createEachAsyncMultiError(aggregatedErrors) :
125
+ error;
126
+ return finalCallback(finalErr);
110
127
  }
111
128
 
112
129
  immediate(() => enqueue(fetch));
@@ -118,11 +135,18 @@ module.exports = function eachAsync(next, fn, options, callback) {
118
135
  }
119
136
 
120
137
  function handleNextResult(doc, i, callback) {
121
- const promise = fn(doc, i);
122
- if (promise && typeof promise.then === 'function') {
123
- promise.then(
138
+ let maybePromise;
139
+ try {
140
+ maybePromise = fn(doc, i);
141
+ } catch (err) {
142
+ return callback(err);
143
+ }
144
+ if (maybePromise && typeof maybePromise.then === 'function') {
145
+ maybePromise.then(
124
146
  function() { callback(null); },
125
- function(error) { callback(error || new Error('`eachAsync()` promise rejected without error')); });
147
+ function(error) {
148
+ callback(error || new Error('`eachAsync()` promise rejected without error'));
149
+ });
126
150
  } else {
127
151
  callback(null);
128
152
  }
@@ -158,3 +182,11 @@ function asyncQueue() {
158
182
  }
159
183
  }
160
184
  }
185
+
186
+ function createEachAsyncMultiError(aggregatedErrors) {
187
+ if (aggregatedErrors.length === 0) {
188
+ return null;
189
+ }
190
+
191
+ return new EachAsyncMultiError(aggregatedErrors);
192
+ }
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const isTextIndex = require('./isTextIndex');
4
+
5
+ module.exports = function applySchemaCollation(indexKeys, indexOptions, schemaOptions) {
6
+ if (isTextIndex(indexKeys)) {
7
+ return;
8
+ }
9
+
10
+ if (schemaOptions.hasOwnProperty('collation') && !indexOptions.hasOwnProperty('collation')) {
11
+ indexOptions.collation = schemaOptions.collation;
12
+ }
13
+ };
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Returns `true` if the given index options have a `text` option.
5
+ */
6
+
7
+ module.exports = function isTextIndex(indexKeys) {
8
+ let isTextIndex = false;
9
+ for (const key of Object.keys(indexKeys)) {
10
+ if (indexKeys[key] === 'text') {
11
+ isTextIndex = true;
12
+ }
13
+ }
14
+
15
+ return isTextIndex;
16
+ };
@@ -17,7 +17,6 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
17
17
  */
18
18
 
19
19
  module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
20
-
21
20
  if (!(schema && schema.instanceOfSchema)) {
22
21
  throw new Error('You must pass a valid discriminator Schema');
23
22
  }
@@ -109,7 +108,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
109
108
 
110
109
  utils.merge(schema, baseSchema, {
111
110
  isDiscriminatorSchemaMerge: true,
112
- omit: { discriminators: true, base: true },
111
+ omit: { discriminators: true, base: true, _applyDiscriminators: true },
113
112
  omitNested: conflictingPaths.reduce((cur, path) => {
114
113
  cur['tree.' + path] = true;
115
114
  return cur;
@@ -141,7 +140,6 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
141
140
  obj[key][schema.options.typeKey] = existingPath ? existingPath.options[schema.options.typeKey] : String;
142
141
  schema.add(obj);
143
142
 
144
-
145
143
  schema.discriminatorMapping = { key: key, value: value, isRoot: false };
146
144
 
147
145
  if (baseSchema.options.collection) {
@@ -76,17 +76,20 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
76
76
  let schemaOptions = null;
77
77
  let modelNamesInOrder = null;
78
78
 
79
- if (schema != null && schema.instance === 'Embedded' && schema.options.ref) {
80
- const data = {
81
- localField: options.path + '._id',
82
- foreignField: '_id',
83
- justOne: true
84
- };
85
- const res = _getModelNames(doc, schema, modelNameFromQuery, model);
79
+ if (schema != null && schema.instance === 'Embedded') {
80
+ if (schema.options.ref) {
81
+ const data = {
82
+ localField: options.path + '._id',
83
+ foreignField: '_id',
84
+ justOne: true
85
+ };
86
+ const res = _getModelNames(doc, schema, modelNameFromQuery, model);
86
87
 
87
- const unpopulatedValue = mpath.get(options.path, doc);
88
- const id = mpath.get('_id', unpopulatedValue);
89
- addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
88
+ const unpopulatedValue = mpath.get(options.path, doc);
89
+ const id = mpath.get('_id', unpopulatedValue);
90
+ addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
91
+ }
92
+ // No-op if no `ref` set. See gh-11538
90
93
  continue;
91
94
  }
92
95
 
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const utils = require('../../utils');
4
+
5
+ function applyGlobalMaxTimeMS(options, model) {
6
+ applyGlobalOption(options, model, 'maxTimeMS');
7
+ }
8
+
9
+ function applyGlobalDiskUse(options, model) {
10
+ applyGlobalOption(options, model, 'allowDiskUse');
11
+ }
12
+
13
+ module.exports = {
14
+ applyGlobalMaxTimeMS,
15
+ applyGlobalDiskUse
16
+ };
17
+
18
+
19
+ function applyGlobalOption(options, model, optionName) {
20
+ if (utils.hasUserDefinedProperty(options, optionName)) {
21
+ return;
22
+ }
23
+
24
+ if (utils.hasUserDefinedProperty(model.db.options, optionName)) {
25
+ options[optionName] = model.db.options[optionName];
26
+ } else if (utils.hasUserDefinedProperty(model.base.options, optionName)) {
27
+ options[optionName] = model.base.options[optionName];
28
+ }
29
+ }
@@ -47,7 +47,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
47
47
  } else if (!options.overwriteDiscriminatorKey) {
48
48
  delete obj[schema.options.discriminatorKey];
49
49
  }
50
- if (options.upsert) {
50
+ if (options.upsert && !options.overwrite) {
51
51
  moveImmutableProperties(schema, obj, context);
52
52
  }
53
53
 
@@ -217,6 +217,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
217
217
  }
218
218
 
219
219
  if (op !== '$setOnInsert' &&
220
+ !options.overwrite &&
220
221
  handleImmutable(schematype, strict, obj, key, prefix + key, context)) {
221
222
  continue;
222
223
  }
@@ -311,6 +312,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
311
312
 
312
313
  // You can use `$setOnInsert` with immutable keys
313
314
  if (op !== '$setOnInsert' &&
315
+ !options.overwrite &&
314
316
  handleImmutable(schematype, strict, obj, key, prefix + key, context)) {
315
317
  continue;
316
318
  }