mongoose 6.2.9 → 6.3.0

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 (64) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/browser.umd.js +2 -1693
  3. package/lib/aggregate.js +59 -67
  4. package/lib/browser.js +4 -4
  5. package/lib/connection.js +21 -21
  6. package/lib/cursor/AggregationCursor.js +2 -2
  7. package/lib/cursor/ChangeStream.js +42 -2
  8. package/lib/cursor/QueryCursor.js +5 -3
  9. package/lib/document.js +39 -46
  10. package/lib/error/eachAsyncMultiError.js +41 -0
  11. package/lib/error/index.js +2 -2
  12. package/lib/helpers/cursor/eachAsync.js +44 -12
  13. package/lib/helpers/indexes/applySchemaCollation.js +13 -0
  14. package/lib/helpers/indexes/isTextIndex.js +16 -0
  15. package/lib/helpers/model/discriminator.js +1 -3
  16. package/lib/helpers/populate/markArraySubdocsPopulated.js +1 -1
  17. package/lib/helpers/projection/hasIncludedChildren.js +1 -1
  18. package/lib/helpers/query/applyGlobalOption.js +29 -0
  19. package/lib/helpers/query/castUpdate.js +3 -1
  20. package/lib/helpers/update/applyTimestampsToChildren.js +2 -2
  21. package/lib/helpers/update/applyTimestampsToUpdate.js +0 -1
  22. package/lib/index.js +33 -26
  23. package/lib/model.js +88 -90
  24. package/lib/options/SchemaArrayOptions.js +2 -2
  25. package/lib/options/SchemaBufferOptions.js +1 -1
  26. package/lib/options/SchemaDateOptions.js +2 -2
  27. package/lib/options/SchemaDocumentArrayOptions.js +3 -3
  28. package/lib/options/SchemaMapOptions.js +2 -2
  29. package/lib/options/SchemaNumberOptions.js +3 -3
  30. package/lib/options/SchemaObjectIdOptions.js +2 -2
  31. package/lib/options/SchemaStringOptions.js +1 -1
  32. package/lib/options/SchemaSubdocumentOptions.js +2 -2
  33. package/lib/options/SchemaTypeOptions.js +3 -3
  34. package/lib/query.js +273 -249
  35. package/lib/schema/SubdocumentPath.js +4 -3
  36. package/lib/schema/array.js +2 -2
  37. package/lib/schema/boolean.js +4 -4
  38. package/lib/schema/buffer.js +3 -3
  39. package/lib/schema/date.js +7 -7
  40. package/lib/schema/decimal128.js +2 -2
  41. package/lib/schema/documentarray.js +3 -3
  42. package/lib/schema/mixed.js +2 -2
  43. package/lib/schema/number.js +6 -6
  44. package/lib/schema/objectid.js +4 -7
  45. package/lib/schema/string.js +38 -16
  46. package/lib/schema.js +144 -30
  47. package/lib/schematype.js +75 -68
  48. package/lib/types/ArraySubdocument.js +1 -1
  49. package/lib/types/DocumentArray/methods/index.js +2 -2
  50. package/lib/types/array/index.js +1 -1
  51. package/lib/types/array/methods/index.js +13 -13
  52. package/lib/types/buffer.js +1 -1
  53. package/lib/types/decimal128.js +1 -1
  54. package/lib/types/objectid.js +1 -1
  55. package/lib/types/subdocument.js +31 -2
  56. package/lib/validoptions.js +1 -0
  57. package/lib/virtualtype.js +3 -3
  58. package/package.json +19 -13
  59. package/tools/repl.js +2 -1
  60. package/types/aggregate.d.ts +223 -0
  61. package/types/cursor.d.ts +10 -4
  62. package/types/index.d.ts +194 -209
  63. package/types/mongooseoptions.d.ts +10 -4
  64. package/lib/helpers/query/applyGlobalMaxTimeMS.js +0 -15
@@ -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
  }
@@ -112,7 +112,7 @@ QueryCursor.prototype._read = function() {
112
112
  * Registers a transform function which subsequently maps documents retrieved
113
113
  * via the streams interface or `.next()`
114
114
  *
115
- * ####Example
115
+ * #### Example
116
116
  *
117
117
  * // Map documents returned by `data` events
118
118
  * Thing.
@@ -211,7 +211,7 @@ QueryCursor.prototype.next = function(callback) {
211
211
  * will wait for the promise to resolve before iterating on to the next one.
212
212
  * Returns a promise that resolves when done.
213
213
  *
214
- * ####Example
214
+ * #### Example
215
215
  *
216
216
  * // Iterate over documents asynchronously
217
217
  * Thing.
@@ -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
@@ -298,7 +300,7 @@ QueryCursor.prototype._transformForAsyncIterator = function() {
298
300
  * You do not need to call this function explicitly, the JavaScript runtime
299
301
  * will call it for you.
300
302
  *
301
- * ####Example
303
+ * #### Example
302
304
  *
303
305
  * // Works without using `cursor()`
304
306
  * for await (const doc of Model.find([{ $sort: { name: 1 } }])) {
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);
@@ -268,7 +266,7 @@ Document.prototype.schema;
268
266
  * is handy for passing data to middleware without conflicting with Mongoose
269
267
  * internals.
270
268
  *
271
- * ####Example:
269
+ * #### Example:
272
270
  *
273
271
  * schema.pre('save', function() {
274
272
  * // Mongoose will set `isNew` to `false` if `save()` succeeds
@@ -326,7 +324,7 @@ Document.prototype.isNew;
326
324
  /**
327
325
  * Set this property to add additional query filters when Mongoose saves this document and `isNew` is false.
328
326
  *
329
- * ####Example:
327
+ * #### Example:
330
328
  *
331
329
  * // Make sure `save()` never updates a soft deleted document.
332
330
  * schema.pre('save', function() {
@@ -348,7 +346,7 @@ Object.defineProperty(Document.prototype, '$where', {
348
346
  /**
349
347
  * The string version of this documents _id.
350
348
  *
351
- * ####Note:
349
+ * #### Note:
352
350
  *
353
351
  * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](/docs/guide.html#id) of its `Schema` to false at construction time.
354
352
  *
@@ -389,7 +387,7 @@ Document.prototype.errors;
389
387
  * A string containing the current operation that Mongoose is executing
390
388
  * on this document. May be `null`, `'save'`, `'validate'`, or `'remove'`.
391
389
  *
392
- * ####Example:
390
+ * #### Example:
393
391
  *
394
392
  * const doc = new Model({ name: 'test' });
395
393
  * doc.$op; // null
@@ -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;
@@ -841,11 +837,11 @@ function init(self, obj, doc, opts, prefix) {
841
837
  /**
842
838
  * Sends an update command with this document `_id` as the query selector.
843
839
  *
844
- * ####Example:
840
+ * #### Example:
845
841
  *
846
842
  * weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
847
843
  *
848
- * ####Valid options:
844
+ * #### Valid options:
849
845
  *
850
846
  * - same as in [Model.update](#model_Model.update)
851
847
  *
@@ -876,11 +872,11 @@ Document.prototype.update = function update() {
876
872
  /**
877
873
  * Sends an updateOne command with this document `_id` as the query selector.
878
874
  *
879
- * ####Example:
875
+ * #### Example:
880
876
  *
881
877
  * weirdCar.updateOne({$inc: {wheels:1}}, { w: 1 }, callback);
882
878
  *
883
- * ####Valid options:
879
+ * #### Valid options:
884
880
  *
885
881
  * - same as in [Model.updateOne](#model_Model.updateOne)
886
882
  *
@@ -922,7 +918,7 @@ Document.prototype.updateOne = function updateOne(doc, options, callback) {
922
918
  /**
923
919
  * Sends a replaceOne command with this document `_id` as the query selector.
924
920
  *
925
- * ####Valid options:
921
+ * #### Valid options:
926
922
  *
927
923
  * - same as in [Model.replaceOne](https://mongoosejs.com/docs/api/model.html#model_Model.replaceOne)
928
924
  *
@@ -947,7 +943,7 @@ Document.prototype.replaceOne = function replaceOne() {
947
943
  * automatically set `session` if you `save()` a doc that you got from a
948
944
  * query with an associated session.
949
945
  *
950
- * ####Example:
946
+ * #### Example:
951
947
  *
952
948
  * const session = MyModel.startSession();
953
949
  * const doc = await MyModel.findOne().session(session);
@@ -1514,7 +1510,7 @@ function _isManuallyPopulatedArray(val, ref) {
1514
1510
  /**
1515
1511
  * Sets the value of a path, or many paths.
1516
1512
  *
1517
- * ####Example:
1513
+ * #### Example:
1518
1514
  *
1519
1515
  * // path, value
1520
1516
  * doc.set(path, value)
@@ -1718,7 +1714,7 @@ Document.prototype.$__setValue = function(path, val) {
1718
1714
  /**
1719
1715
  * Returns the value of a path.
1720
1716
  *
1721
- * ####Example
1717
+ * #### Example
1722
1718
  *
1723
1719
  * // path
1724
1720
  * doc.get('age') // 47
@@ -1824,7 +1820,7 @@ Document.prototype.$__path = function(path) {
1824
1820
  *
1825
1821
  * _Very helpful when using [Mixed](https://mongoosejs.com/docs/schematypes.html#mixed) types._
1826
1822
  *
1827
- * ####Example:
1823
+ * #### Example:
1828
1824
  *
1829
1825
  * doc.mixed.type = 'changed';
1830
1826
  * doc.markModified('mixed.type');
@@ -1847,7 +1843,7 @@ Document.prototype.markModified = function(path, scope) {
1847
1843
  /**
1848
1844
  * Clears the modified state on the specified path.
1849
1845
  *
1850
- * ####Example:
1846
+ * #### Example:
1851
1847
  *
1852
1848
  * doc.foo = 'bar';
1853
1849
  * doc.unmarkModified('foo');
@@ -1867,7 +1863,7 @@ Document.prototype.unmarkModified = function(path) {
1867
1863
  /**
1868
1864
  * Don't run validation on this path or persist changes to this path.
1869
1865
  *
1870
- * ####Example:
1866
+ * #### Example:
1871
1867
  *
1872
1868
  * doc.foo = null;
1873
1869
  * doc.$ignore('foo');
@@ -1892,7 +1888,7 @@ Document.prototype.$ignore = function(path) {
1892
1888
  * A path `a` may be in `modifiedPaths()` but not in `directModifiedPaths()`
1893
1889
  * because a child of `a` was directly modified.
1894
1890
  *
1895
- * ####Example
1891
+ * #### Example
1896
1892
  * const schema = new Schema({ foo: String, nested: { bar: String } });
1897
1893
  * const Model = mongoose.model('Test', schema);
1898
1894
  * await Model.create({ foo: 'original', nested: { bar: 'original' } });
@@ -1915,7 +1911,7 @@ Document.prototype.directModifiedPaths = function() {
1915
1911
  * Useful for determining whether this subdoc will get stripped out by the
1916
1912
  * [minimize option](/docs/guide.html#minimize).
1917
1913
  *
1918
- * ####Example:
1914
+ * #### Example:
1919
1915
  * const schema = new Schema({ nested: { foo: String } });
1920
1916
  * const Model = mongoose.model('Test', schema);
1921
1917
  * const doc = new Model({});
@@ -2048,7 +2044,7 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
2048
2044
  *
2049
2045
  * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified.
2050
2046
  *
2051
- * ####Example
2047
+ * #### Example
2052
2048
  *
2053
2049
  * doc.set('documents.0.title', 'changed');
2054
2050
  * doc.isModified() // true
@@ -2094,7 +2090,7 @@ Document.prototype[documentIsModified] = Document.prototype.isModified;
2094
2090
  /**
2095
2091
  * Checks if a path is set to its default.
2096
2092
  *
2097
- * ####Example
2093
+ * #### Example
2098
2094
  *
2099
2095
  * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} });
2100
2096
  * const m = new MyModel();
@@ -2128,7 +2124,7 @@ Document.prototype.$isDefault = function(path) {
2128
2124
  /**
2129
2125
  * Getter/setter, determines whether the document was removed or not.
2130
2126
  *
2131
- * ####Example:
2127
+ * #### Example:
2132
2128
  * const product = await product.remove();
2133
2129
  * product.$isDeleted(); // true
2134
2130
  * product.remove(); // no-op, doesn't send anything to the db
@@ -2158,7 +2154,7 @@ Document.prototype.$isDeleted = function(val) {
2158
2154
  /**
2159
2155
  * Returns true if `path` was directly set and modified, else false.
2160
2156
  *
2161
- * ####Example
2157
+ * #### Example
2162
2158
  *
2163
2159
  * doc.set('documents.0.title', 'changed');
2164
2160
  * doc.isDirectModified('documents.0.title') // true
@@ -2214,7 +2210,7 @@ Document.prototype.isInit = function(path) {
2214
2210
  /**
2215
2211
  * Checks if `path` was selected in the source query which initialized this document.
2216
2212
  *
2217
- * ####Example
2213
+ * #### Example
2218
2214
  *
2219
2215
  * const doc = await Thing.findOne().select('name');
2220
2216
  * doc.isSelected('name') // true
@@ -2295,7 +2291,7 @@ Document.prototype.$__isSelected = Document.prototype.isSelected;
2295
2291
  * Checks if `path` was explicitly selected. If no projection, always returns
2296
2292
  * true.
2297
2293
  *
2298
- * ####Example
2294
+ * #### Example
2299
2295
  *
2300
2296
  * Thing.findOne().select('nested.name').exec(function (err, doc) {
2301
2297
  * doc.isDirectSelected('nested.name') // true
@@ -2357,11 +2353,11 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
2357
2353
  /**
2358
2354
  * Executes registered validation rules for this document.
2359
2355
  *
2360
- * ####Note:
2356
+ * #### Note:
2361
2357
  *
2362
2358
  * This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned to your `callback`.
2363
2359
  *
2364
- * ####Example:
2360
+ * #### Example:
2365
2361
  *
2366
2362
  * doc.validate(function (err) {
2367
2363
  * if (err) handleError(err);
@@ -2485,10 +2481,7 @@ function _getPathsToValidate(doc) {
2485
2481
  if (subdoc.$basePath) {
2486
2482
  // Remove child paths for now, because we'll be validating the whole
2487
2483
  // subdoc
2488
- if (!subdoc.$__.fullPath) {
2489
- subdoc.ownerDocument();
2490
- }
2491
- const fullPathToSubdoc = subdoc.$__.fullPath;
2484
+ const fullPathToSubdoc = subdoc.$__fullPathWithIndexes();
2492
2485
 
2493
2486
  for (const p of paths) {
2494
2487
  if (p === null || p.startsWith(fullPathToSubdoc + '.')) {
@@ -2826,11 +2819,11 @@ function _handlePathsToSkip(paths, pathsToSkip) {
2826
2819
  /**
2827
2820
  * Executes registered validation rules (skipping asynchronous validators) for this document.
2828
2821
  *
2829
- * ####Note:
2822
+ * #### Note:
2830
2823
  *
2831
2824
  * This method is useful if you need synchronous validation.
2832
2825
  *
2833
- * ####Example:
2826
+ * #### Example:
2834
2827
  *
2835
2828
  * const err = doc.validateSync();
2836
2829
  * if (err) {
@@ -3065,7 +3058,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3065
3058
  * Saves this document by inserting a new document into the database if [document.isNew](/docs/api.html#document_Document-isNew) is `true`,
3066
3059
  * or sends an [updateOne](/docs/api.html#document_Document-updateOne) operation **only** with the modifications to the database, it does not replace the whole document in the latter case.
3067
3060
  *
3068
- * ####Example:
3061
+ * #### Example:
3069
3062
  *
3070
3063
  * product.sold = Date.now();
3071
3064
  * product = await product.save();
@@ -3073,7 +3066,7 @@ function _checkImmutableSubpaths(subdoc, schematype, priorVal) {
3073
3066
  * If save is successful, the returned promise will fulfill with the document
3074
3067
  * saved.
3075
3068
  *
3076
- * ####Example:
3069
+ * #### Example:
3077
3070
  *
3078
3071
  * const newProduct = await product.save();
3079
3072
  * newProduct === product; // true
@@ -3601,7 +3594,7 @@ Document.prototype.$toObject = function(options, json) {
3601
3594
  *
3602
3595
  * Buffers are converted to instances of [mongodb.Binary](https://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
3603
3596
  *
3604
- * ####Options:
3597
+ * #### Options:
3605
3598
  *
3606
3599
  * - `getters` apply all getters (path and virtual getters), defaults to false
3607
3600
  * - `aliases` apply all aliases if `virtuals=true`, defaults to true
@@ -3613,7 +3606,7 @@ Document.prototype.$toObject = function(options, json) {
3613
3606
  * - `flattenMaps` convert Maps to POJOs. Useful if you want to JSON.stringify() the result of toObject(), defaults to false
3614
3607
  * - `useProjection` set to `true` to omit fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema.
3615
3608
  *
3616
- * ####Getters/Virtuals
3609
+ * #### Getters/Virtuals
3617
3610
  *
3618
3611
  * Example of only applying path getters
3619
3612
  *
@@ -3631,7 +3624,7 @@ Document.prototype.$toObject = function(options, json) {
3631
3624
  *
3632
3625
  * schema.set('toObject', { virtuals: true })
3633
3626
  *
3634
- * ####Transform
3627
+ * #### Transform
3635
3628
  *
3636
3629
  * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function.
3637
3630
  *
@@ -3643,7 +3636,7 @@ Document.prototype.$toObject = function(options, json) {
3643
3636
  * - `ret` The plain object representation which has been converted
3644
3637
  * - `options` The options in use (either schema options or the options passed inline)
3645
3638
  *
3646
- * ####Example
3639
+ * #### Example
3647
3640
  *
3648
3641
  * // specify the transform schema option
3649
3642
  * if (!schema.options.toObject) schema.options.toObject = {};
@@ -4137,7 +4130,7 @@ Document.prototype.equals = function(doc) {
4137
4130
  /**
4138
4131
  * Populates paths on an existing document.
4139
4132
  *
4140
- * ####Example:
4133
+ * #### Example:
4141
4134
  *
4142
4135
  * await doc.populate([
4143
4136
  * 'stories',
@@ -4254,7 +4247,7 @@ Document.prototype.$getPopulatedDocs = function $getPopulatedDocs() {
4254
4247
  /**
4255
4248
  * Gets _id(s) used during population of the given `path`.
4256
4249
  *
4257
- * ####Example:
4250
+ * #### Example:
4258
4251
  *
4259
4252
  * Model.findOne().populate('author').exec(function (err, doc) {
4260
4253
  * console.log(doc.author.name) // Dr.Seuss
@@ -4316,7 +4309,7 @@ Document.prototype.$populated = Document.prototype.populated;
4316
4309
  /**
4317
4310
  * Takes a populated field and returns it to its unpopulated state.
4318
4311
  *
4319
- * ####Example:
4312
+ * #### Example:
4320
4313
  *
4321
4314
  * Model.findOne().populate('author').exec(function (err, doc) {
4322
4315
  * console.log(doc.author.name); // Dr.Seuss
@@ -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,7 +4,7 @@
4
4
  * MongooseError constructor. MongooseError is the base class for all
5
5
  * Mongoose-specific errors.
6
6
  *
7
- * ####Example:
7
+ * #### Example:
8
8
  * const Model = mongoose.model('Test', new Schema({ answer: Number }));
9
9
  * const doc = new Model({ answer: 'not a number' });
10
10
  * const err = doc.validateSync();
@@ -105,7 +105,7 @@ MongooseError.ValidationError = require('./validation');
105
105
  * A `ValidationError` has a hash of `errors` that contain individual
106
106
  * `ValidatorError` instances.
107
107
  *
108
- * ####Example:
108
+ * #### Example:
109
109
  *
110
110
  * const schema = Schema({ name: { type: String, required: true } });
111
111
  * const Model = mongoose.model('Test', schema);
@@ -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) {
@@ -6,7 +6,7 @@ const utils = require('../../utils');
6
6
  * If populating a path within a document array, make sure each
7
7
  * subdoc within the array knows its subpaths are populated.
8
8
  *
9
- * ####Example:
9
+ * #### Example:
10
10
  * const doc = await Article.findOne().populate('comments.author');
11
11
  * doc.comments[0].populated('author'); // Should be set
12
12
  */
@@ -4,7 +4,7 @@
4
4
  * Creates an object that precomputes whether a given path has child fields in
5
5
  * the projection.
6
6
  *
7
- * ####Example:
7
+ * #### Example:
8
8
  * const res = hasIncludedChildren({ 'a.b.c': 0 });
9
9
  * res.a; // 1
10
10
  * res['a.b']; // 1