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.
- package/.eslintrc.json +157 -157
- package/dist/browser.umd.js +2 -1693
- package/lib/aggregate.js +23 -31
- package/lib/collection.js +0 -7
- package/lib/cursor/ChangeStream.js +42 -2
- package/lib/cursor/QueryCursor.js +2 -0
- package/lib/document.js +13 -19
- package/lib/error/cast.js +7 -2
- package/lib/error/eachAsyncMultiError.js +41 -0
- package/lib/helpers/cursor/eachAsync.js +44 -12
- package/lib/helpers/indexes/applySchemaCollation.js +13 -0
- package/lib/helpers/indexes/isTextIndex.js +16 -0
- package/lib/helpers/model/discriminator.js +1 -3
- package/lib/helpers/populate/getModelsMapForPopulate.js +13 -10
- package/lib/helpers/query/applyGlobalOption.js +29 -0
- package/lib/helpers/query/castUpdate.js +3 -1
- package/lib/helpers/schematype/handleImmutable.js +4 -1
- package/lib/helpers/timestamps/setupTimestamps.js +2 -2
- package/lib/helpers/update/applyTimestampsToChildren.js +2 -2
- package/lib/helpers/update/applyTimestampsToUpdate.js +0 -1
- package/lib/index.js +11 -4
- package/lib/model.js +36 -36
- package/lib/query.js +59 -26
- package/lib/queryhelpers.js +11 -1
- package/lib/schema/SubdocumentPath.js +2 -1
- package/lib/schema/documentarray.js +2 -4
- package/lib/schema/objectid.js +0 -3
- package/lib/schema/string.js +24 -2
- package/lib/schema.js +117 -3
- package/lib/schematype.js +2 -3
- package/lib/types/DocumentArray/methods/index.js +1 -1
- package/lib/types/array/methods/index.js +9 -10
- package/lib/types/subdocument.js +29 -0
- package/lib/validoptions.js +1 -0
- package/package.json +13 -7
- package/tools/repl.js +2 -1
- package/tsconfig.json +2 -1
- package/types/aggregate.d.ts +223 -0
- package/types/connection.d.ts +2 -2
- package/types/cursor.d.ts +10 -4
- package/types/document.d.ts +3 -3
- package/types/index.d.ts +200 -215
- package/types/mongooseoptions.d.ts +10 -4
- package/CHANGELOG.md +0 -7227
- package/History.md +0 -1
- 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/
|
|
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
|
|
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}
|
|
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(
|
|
437
|
-
return this.append({ $count:
|
|
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
|
|
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' && !
|
|
682
|
-
(typeof elseExpr === 'string' && !
|
|
683
|
-
throw new Error('If thenExpr or elseExpr is string, it must
|
|
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 =>
|
|
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 (
|
|
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
|
|
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
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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) {
|
|
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'
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
}
|