mongoose 5.7.4 → 5.7.8
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/History.md +40 -0
- package/lib/aggregate.js +3 -3
- package/lib/cast.js +8 -0
- package/lib/connection.js +29 -17
- package/lib/cursor/AggregationCursor.js +10 -0
- package/lib/document.js +98 -13
- package/lib/error/validation.js +4 -2
- package/lib/helpers/cursor/eachAsync.js +54 -50
- package/lib/helpers/updateValidators.js +3 -0
- package/lib/index.js +9 -0
- package/lib/model.js +9 -7
- package/lib/options/SchemaArrayOptions.js +13 -0
- package/lib/options/SchemaBufferOptions.js +13 -0
- package/lib/options/SchemaDateOptions.js +13 -0
- package/lib/options/SchemaNumberOptions.js +13 -0
- package/lib/options/SchemaObjectIdOptions.js +13 -0
- package/lib/options/SchemaStringOptions.js +39 -0
- package/lib/options/SchemaTypeOptions.js +22 -5
- package/lib/query.js +1 -1
- package/lib/queryhelpers.js +1 -1
- package/lib/schema/SingleNestedPath.js +5 -3
- package/lib/schema/array.js +2 -2
- package/lib/schema/documentarray.js +42 -17
- package/lib/schema/string.js +23 -12
- package/lib/schema.js +7 -13
- package/lib/schematype.js +3 -3
- package/lib/types/core_array.js +4 -1
- package/lib/types/documentarray.js +81 -0
- package/lib/types/subdocument.js +13 -0
- package/package.json +3 -6
package/History.md
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
5.7.8 / 2019-11-04
|
|
2
|
+
==================
|
|
3
|
+
* fix(document): allow manually populating path within document array #8273
|
|
4
|
+
* fix(populate): update top-level `populated()` when updating document array with populated subpaths #8265
|
|
5
|
+
* fix(cursor): throw error when using aggregation cursor as async iterator #8280
|
|
6
|
+
* fix(schema): retain `_id: false` in schema after nesting in another schema #8274
|
|
7
|
+
* fix(document): make Document class an event emitter to support defining documents without models in node #8272
|
|
8
|
+
* docs: document return types for `.discriminator()` #8287
|
|
9
|
+
* docs(connection): add note about exporting schemas, not models, in multi connection paradigm #8275
|
|
10
|
+
* docs: clarify that transforms defined in `toObject()` options are applied to subdocs #8260
|
|
11
|
+
|
|
12
|
+
5.7.7 / 2019-10-24
|
|
13
|
+
==================
|
|
14
|
+
* fix(populate): make populate virtual consistently an empty array if local field is only empty arrays #8230
|
|
15
|
+
* fix(query): allow findOne(objectid) and find(objectid) #8268
|
|
16
|
+
|
|
17
|
+
5.7.6 / 2019-10-21
|
|
18
|
+
==================
|
|
19
|
+
* fix: upgrade mongodb driver -> 3.3.3 to fix issue with failing to connect to a replica set if one member is down #8209
|
|
20
|
+
* fix(document): fix TypeError when setting a single nested subdoc with timestamps #8251
|
|
21
|
+
* fix(cursor): fix issue with long-running `eachAsync()` cursor #8249 #8235
|
|
22
|
+
* fix(connection): ensure repeated `close` events from useUnifiedTopology don't disconnect Mongoose from replica set #8224
|
|
23
|
+
* fix(document): support calling `Document` constructor directly in Node.js #8237
|
|
24
|
+
* fix(populate): add document array subpaths to parent doc `populated()` when calling `DocumentArray#push()` #8247
|
|
25
|
+
* fix(options): add missing minlength and maxlength to SchemaStringOptions #8256
|
|
26
|
+
* docs: add documentarraypath to API docs, including DocumentArrayPath#discriminator() #8164
|
|
27
|
+
* docs(schematypes): add a section about the `type` property #8227
|
|
28
|
+
* docs(api): fix Connection.close return param #8258 [gosuhiman](https://github.com/gosuhiman)
|
|
29
|
+
* docs: update link to broken image on home page #8253 [krosenk729](https://github.com/krosenk729)
|
|
30
|
+
|
|
31
|
+
5.7.5 / 2019-10-14
|
|
32
|
+
==================
|
|
33
|
+
* fix(query): delete top-level `_bsontype` property in queries to prevent silent empty queries #8222
|
|
34
|
+
* fix(update): handle subdocument pre('validate') errors in update validation #7187
|
|
35
|
+
* fix(subdocument): make subdocument#isModified use parent document's isModified #8223
|
|
36
|
+
* docs(index): add favicon to home page #8226
|
|
37
|
+
* docs: add schema options to API docs #8012
|
|
38
|
+
* docs(middleware): add note about accessing the document being updated in pre('findOneAndUpdate') #8218
|
|
39
|
+
* refactor: remove redundant code in ValidationError #8244 [AbdelrahmanHafez](https://github.com/AbdelrahmanHafez)
|
|
40
|
+
|
|
1
41
|
5.7.4 / 2019-10-09
|
|
2
42
|
==================
|
|
3
43
|
* fix(schema): handle `required: null` and `required: undefined` as `required: false` #8219
|
package/lib/aggregate.js
CHANGED
|
@@ -1011,20 +1011,20 @@ Aggregate.prototype.catch = function(reject) {
|
|
|
1011
1011
|
|
|
1012
1012
|
/**
|
|
1013
1013
|
* Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
|
|
1014
|
-
* This function *only* works for `find()` queries.
|
|
1015
1014
|
* You do not need to call this function explicitly, the JavaScript runtime
|
|
1016
1015
|
* will call it for you.
|
|
1017
1016
|
*
|
|
1018
1017
|
* ####Example
|
|
1019
1018
|
*
|
|
1020
|
-
*
|
|
1019
|
+
* const agg = Model.aggregate([{ $match: { age: { $gte: 25 } } }]);
|
|
1020
|
+
* for await (const doc of agg) {
|
|
1021
1021
|
* console.log(doc.name);
|
|
1022
1022
|
* }
|
|
1023
1023
|
*
|
|
1024
1024
|
* Node.js 10.x supports async iterators natively without any flags. You can
|
|
1025
1025
|
* enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
|
|
1026
1026
|
*
|
|
1027
|
-
* **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
|
|
1027
|
+
* **Note:** This function is not set if `Symbol.asyncIterator` is undefined. If
|
|
1028
1028
|
* `Symbol.asyncIterator` is undefined, that means your Node.js version does not
|
|
1029
1029
|
* support async iterators.
|
|
1030
1030
|
*
|
package/lib/cast.js
CHANGED
|
@@ -27,6 +27,14 @@ module.exports = function cast(schema, obj, options, context) {
|
|
|
27
27
|
throw new Error('Query filter must be an object, got an array ', util.inspect(obj));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// bson 1.x has the unfortunate tendency to remove filters that have a top-level
|
|
31
|
+
// `_bsontype` property. But we should still allow ObjectIds because
|
|
32
|
+
// `Collection#find()` has a special case to support `find(objectid)`.
|
|
33
|
+
// Should remove this when we upgrade to bson 4.x. See gh-8222, gh-8268
|
|
34
|
+
if (obj.hasOwnProperty('_bsontype') && obj._bsontype !== 'ObjectID') {
|
|
35
|
+
delete obj._bsontype;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
const paths = Object.keys(obj);
|
|
31
39
|
let i = paths.length;
|
|
32
40
|
let _keys;
|
package/lib/connection.js
CHANGED
|
@@ -639,18 +639,28 @@ Connection.prototype.openUri = function(uri, options, callback) {
|
|
|
639
639
|
_this.db = db;
|
|
640
640
|
|
|
641
641
|
// `useUnifiedTopology` events
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
642
|
+
const type = get(db, 's.topology.s.description.type', '');
|
|
643
|
+
if (options.useUnifiedTopology) {
|
|
644
|
+
if (type === 'Single') {
|
|
645
|
+
const server = Array.from(db.s.topology.s.servers.values())[0];
|
|
646
|
+
server.s.pool.on('reconnect', () => {
|
|
647
|
+
_handleReconnect();
|
|
648
|
+
});
|
|
649
|
+
server.s.pool.on('reconnectFailed', () => {
|
|
650
|
+
_this.emit('reconnectFailed');
|
|
651
|
+
});
|
|
652
|
+
server.s.pool.on('timeout', () => {
|
|
653
|
+
_this.emit('timeout');
|
|
654
|
+
});
|
|
655
|
+
} else if (type.startsWith('ReplicaSet')) {
|
|
656
|
+
db.on('close', function() {
|
|
657
|
+
const type = get(db, 's.topology.s.description.type', '');
|
|
658
|
+
if (type !== 'ReplicaSetWithPrimary') {
|
|
659
|
+
// Implicitly emits 'disconnected'
|
|
660
|
+
_this.readyState = STATES.disconnected;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
654
664
|
}
|
|
655
665
|
|
|
656
666
|
// Backwards compat for mongoose 4.x
|
|
@@ -674,10 +684,12 @@ Connection.prototype.openUri = function(uri, options, callback) {
|
|
|
674
684
|
_this.emit('attemptReconnect');
|
|
675
685
|
});
|
|
676
686
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
687
|
+
if (!options.useUnifiedTopology || !type.startsWith('ReplicaSet')) {
|
|
688
|
+
db.on('close', function() {
|
|
689
|
+
// Implicitly emits 'disconnected'
|
|
690
|
+
_this.readyState = STATES.disconnected;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
681
693
|
client.on('left', function() {
|
|
682
694
|
if (_this.readyState === STATES.connected &&
|
|
683
695
|
get(db, 's.topology.s.coreTopology.s.replicaSetState.topologyType') === 'ReplicaSetNoPrimary') {
|
|
@@ -745,7 +757,7 @@ const handleUseMongoClient = function handleUseMongoClient(options) {
|
|
|
745
757
|
*
|
|
746
758
|
* @param {Boolean} [force] optional
|
|
747
759
|
* @param {Function} [callback] optional
|
|
748
|
-
* @return {
|
|
760
|
+
* @return {Promise}
|
|
749
761
|
* @api public
|
|
750
762
|
*/
|
|
751
763
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
const MongooseError = require('../error/mongooseError');
|
|
7
8
|
const Readable = require('stream').Readable;
|
|
8
9
|
const eachAsync = require('../helpers/cursor/eachAsync');
|
|
9
10
|
const util = require('util');
|
|
@@ -94,6 +95,15 @@ AggregationCursor.prototype._read = function() {
|
|
|
94
95
|
});
|
|
95
96
|
};
|
|
96
97
|
|
|
98
|
+
if (Symbol.asyncIterator != null) {
|
|
99
|
+
const msg = 'Mongoose does not support using async iterators with an ' +
|
|
100
|
+
'existing aggregation cursor. See http://bit.ly/mongoose-async-iterate-aggregation';
|
|
101
|
+
|
|
102
|
+
AggregationCursor.prototype[Symbol.asyncIterator] = function() {
|
|
103
|
+
throw new MongooseError(msg);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
/**
|
|
98
108
|
* Registers a transform function which subsequently maps documents retrieved
|
|
99
109
|
* via the streams interface or `.next()`
|
package/lib/document.js
CHANGED
|
@@ -10,6 +10,7 @@ const MongooseError = require('./error/index');
|
|
|
10
10
|
const MixedSchema = require('./schema/mixed');
|
|
11
11
|
const ObjectExpectedError = require('./error/objectExpected');
|
|
12
12
|
const ObjectParameterError = require('./error/objectParameter');
|
|
13
|
+
const Schema = require('./schema');
|
|
13
14
|
const StrictModeError = require('./error/strict');
|
|
14
15
|
const ValidatorError = require('./schematype').ValidatorError;
|
|
15
16
|
const VirtualType = require('./virtualtype');
|
|
@@ -64,6 +65,17 @@ function Document(obj, fields, skipId, options) {
|
|
|
64
65
|
}
|
|
65
66
|
options = options || {};
|
|
66
67
|
|
|
68
|
+
// Support `browserDocument.js` syntax
|
|
69
|
+
if (this.schema == null) {
|
|
70
|
+
const _schema = utils.isObject(fields) && !fields.instanceOfSchema ?
|
|
71
|
+
new Schema(fields) :
|
|
72
|
+
fields;
|
|
73
|
+
this.$__setSchema(_schema);
|
|
74
|
+
fields = skipId;
|
|
75
|
+
skipId = options;
|
|
76
|
+
options = arguments[4] || {};
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
this.$__ = new InternalCache;
|
|
68
80
|
this.$__.emitter = new EventEmitter();
|
|
69
81
|
this.isNew = 'isNew' in options ? options.isNew : true;
|
|
@@ -172,6 +184,10 @@ utils.each(
|
|
|
172
184
|
|
|
173
185
|
Document.prototype.constructor = Document;
|
|
174
186
|
|
|
187
|
+
for (const i in EventEmitter.prototype) {
|
|
188
|
+
Document[i] = EventEmitter.prototype[i];
|
|
189
|
+
}
|
|
190
|
+
|
|
175
191
|
/**
|
|
176
192
|
* The documents schema.
|
|
177
193
|
*
|
|
@@ -486,8 +502,7 @@ Document.prototype.$__init = function(doc, opts) {
|
|
|
486
502
|
|
|
487
503
|
// handle docs with populated paths
|
|
488
504
|
// If doc._id is not null or undefined
|
|
489
|
-
if (doc._id
|
|
490
|
-
opts.populated && opts.populated.length) {
|
|
505
|
+
if (doc._id != null && opts.populated && opts.populated.length) {
|
|
491
506
|
const id = String(doc._id);
|
|
492
507
|
for (let i = 0; i < opts.populated.length; ++i) {
|
|
493
508
|
const item = opts.populated[i];
|
|
@@ -501,6 +516,8 @@ Document.prototype.$__init = function(doc, opts) {
|
|
|
501
516
|
|
|
502
517
|
init(this, doc, this._doc, opts);
|
|
503
518
|
|
|
519
|
+
markArraySubdocsPopulated(this, opts.populated);
|
|
520
|
+
|
|
504
521
|
this.emit('init', this);
|
|
505
522
|
this.constructor.emit('init', this);
|
|
506
523
|
|
|
@@ -509,6 +526,44 @@ Document.prototype.$__init = function(doc, opts) {
|
|
|
509
526
|
return this;
|
|
510
527
|
};
|
|
511
528
|
|
|
529
|
+
/*!
|
|
530
|
+
* If populating a path within a document array, make sure each
|
|
531
|
+
* subdoc within the array knows its subpaths are populated.
|
|
532
|
+
*
|
|
533
|
+
* ####Example:
|
|
534
|
+
* const doc = await Article.findOne().populate('comments.author');
|
|
535
|
+
* doc.comments[0].populated('author'); // Should be set
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
function markArraySubdocsPopulated(doc, populated) {
|
|
539
|
+
if (doc._id == null || populated == null || populated.length === 0) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const id = String(doc._id);
|
|
544
|
+
for (const item of populated) {
|
|
545
|
+
if (item.isVirtual) {
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const path = item.path;
|
|
549
|
+
const pieces = path.split('.');
|
|
550
|
+
for (let i = 0; i < pieces.length - 1; ++i) {
|
|
551
|
+
const subpath = pieces.slice(0, i + 1).join('.');
|
|
552
|
+
const rest = pieces.slice(i + 1).join('.');
|
|
553
|
+
const val = doc.get(subpath);
|
|
554
|
+
if (val == null) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
if (val.isMongooseDocumentArray) {
|
|
558
|
+
for (let j = 0; j < val.length; ++j) {
|
|
559
|
+
val[j].populated(rest, item._docs[id][j], item);
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
512
567
|
/*!
|
|
513
568
|
* Init helper.
|
|
514
569
|
*
|
|
@@ -1079,12 +1134,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1079
1134
|
|
|
1080
1135
|
let didPopulate = false;
|
|
1081
1136
|
if (refMatches && val instanceof Document) {
|
|
1082
|
-
|
|
1083
|
-
this.ownerDocument().populated(this.$__fullPath(path),
|
|
1084
|
-
val._id, { [populateModelSymbol]: val.constructor });
|
|
1085
|
-
} else {
|
|
1086
|
-
this.populated(path, val._id, { [populateModelSymbol]: val.constructor });
|
|
1087
|
-
}
|
|
1137
|
+
this.populated(path, val._id, { [populateModelSymbol]: val.constructor });
|
|
1088
1138
|
didPopulate = true;
|
|
1089
1139
|
}
|
|
1090
1140
|
|
|
@@ -1117,6 +1167,21 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1117
1167
|
val = schema.applySetters(val, this, false, priorVal);
|
|
1118
1168
|
}
|
|
1119
1169
|
|
|
1170
|
+
if (schema.$isMongooseDocumentArray &&
|
|
1171
|
+
Array.isArray(val) &&
|
|
1172
|
+
val.length > 0 &&
|
|
1173
|
+
val[0] != null &&
|
|
1174
|
+
val[0].$__ != null &&
|
|
1175
|
+
val[0].$__.populated != null) {
|
|
1176
|
+
const populatedPaths = Object.keys(val[0].$__.populated);
|
|
1177
|
+
for (const populatedPath of populatedPaths) {
|
|
1178
|
+
this.populated(path + '.' + populatedPath,
|
|
1179
|
+
val.map(v => v.populated(populatedPath)),
|
|
1180
|
+
val[0].$__.populated[populatedPath].options);
|
|
1181
|
+
}
|
|
1182
|
+
didPopulate = true;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1120
1185
|
if (!didPopulate && this.$__.populated) {
|
|
1121
1186
|
delete this.$__.populated[path];
|
|
1122
1187
|
}
|
|
@@ -1932,9 +1997,11 @@ Document.prototype.validate = function(options, callback) {
|
|
|
1932
1997
|
options = null;
|
|
1933
1998
|
}
|
|
1934
1999
|
|
|
1935
|
-
return utils.promiseOrCallback(callback, cb =>
|
|
1936
|
-
|
|
1937
|
-
|
|
2000
|
+
return utils.promiseOrCallback(callback, cb => {
|
|
2001
|
+
this.$__validate(options, function(error) {
|
|
2002
|
+
cb(error);
|
|
2003
|
+
});
|
|
2004
|
+
}, this.constructor.events);
|
|
1938
2005
|
};
|
|
1939
2006
|
|
|
1940
2007
|
/*!
|
|
@@ -2922,7 +2989,6 @@ Document.prototype.$toObject = function(options, json) {
|
|
|
2922
2989
|
*
|
|
2923
2990
|
* If you want to skip transformations, use `transform: false`:
|
|
2924
2991
|
*
|
|
2925
|
-
* if (!schema.options.toObject) schema.options.toObject = {};
|
|
2926
2992
|
* schema.options.toObject.hide = '_id';
|
|
2927
2993
|
* schema.options.toObject.transform = function (doc, ret, options) {
|
|
2928
2994
|
* if (options.hide) {
|
|
@@ -2938,7 +3004,26 @@ Document.prototype.$toObject = function(options, json) {
|
|
|
2938
3004
|
* doc.toObject({ hide: 'secret _id', transform: false });// { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }
|
|
2939
3005
|
* doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' }
|
|
2940
3006
|
*
|
|
2941
|
-
*
|
|
3007
|
+
* If you pass a transform in `toObject()` options, Mongoose will apply the transform
|
|
3008
|
+
* to [subdocuments](/docs/subdocs.html) in addition to the top-level document.
|
|
3009
|
+
* Similarly, `transform: false` skips transforms for all subdocuments.
|
|
3010
|
+
* Note that this is behavior is different for transforms defined in the schema:
|
|
3011
|
+
* if you define a transform in `schema.options.toObject.transform`, that transform
|
|
3012
|
+
* will **not** apply to subdocuments.
|
|
3013
|
+
*
|
|
3014
|
+
* const memberSchema = new Schema({ name: String, email: String });
|
|
3015
|
+
* const groupSchema = new Schema({ members: [memberSchema], name: String, email });
|
|
3016
|
+
* const Group = mongoose.model('Group', groupSchema);
|
|
3017
|
+
*
|
|
3018
|
+
* const doc = new Group({
|
|
3019
|
+
* name: 'Engineering',
|
|
3020
|
+
* email: 'dev@mongoosejs.io',
|
|
3021
|
+
* members: [{ name: 'Val', email: 'val@mongoosejs.io' }]
|
|
3022
|
+
* });
|
|
3023
|
+
*
|
|
3024
|
+
* // Removes `email` from both top-level document **and** array elements
|
|
3025
|
+
* // { name: 'Engineering', members: [{ name: 'Val' }] }
|
|
3026
|
+
* doc.toObject({ transform: (doc, ret) => { delete ret.email; return ret; } });
|
|
2942
3027
|
*
|
|
2943
3028
|
* Transforms, like all of these options, are also available for `toJSON`. See [this guide to `JSON.stringify()`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html) to learn why `toJSON()` and `toObject()` are separate functions.
|
|
2944
3029
|
*
|
package/lib/error/validation.js
CHANGED
|
@@ -18,19 +18,21 @@ const util = require('util');
|
|
|
18
18
|
function ValidationError(instance) {
|
|
19
19
|
this.errors = {};
|
|
20
20
|
this._message = '';
|
|
21
|
+
|
|
22
|
+
MongooseError.call(this, this._message);
|
|
21
23
|
if (instance && instance.constructor.name === 'model') {
|
|
22
24
|
this._message = instance.constructor.modelName + ' validation failed';
|
|
23
|
-
MongooseError.call(this, this._message);
|
|
24
25
|
} else {
|
|
25
26
|
this._message = 'Validation failed';
|
|
26
|
-
MongooseError.call(this, this._message);
|
|
27
27
|
}
|
|
28
28
|
this.name = 'ValidationError';
|
|
29
|
+
|
|
29
30
|
if (Error.captureStackTrace) {
|
|
30
31
|
Error.captureStackTrace(this);
|
|
31
32
|
} else {
|
|
32
33
|
this.stack = new Error().stack;
|
|
33
34
|
}
|
|
35
|
+
|
|
34
36
|
if (instance) {
|
|
35
37
|
instance.errors = this.errors;
|
|
36
38
|
}
|
|
@@ -22,6 +22,7 @@ const utils = require('../../utils');
|
|
|
22
22
|
|
|
23
23
|
module.exports = function eachAsync(next, fn, options, callback) {
|
|
24
24
|
const parallel = options.parallel || 1;
|
|
25
|
+
const enqueue = asyncQueue();
|
|
25
26
|
|
|
26
27
|
const handleNextResult = function(doc, callback) {
|
|
27
28
|
const promise = fn(doc);
|
|
@@ -37,71 +38,74 @@ module.exports = function eachAsync(next, fn, options, callback) {
|
|
|
37
38
|
const iterate = function(callback) {
|
|
38
39
|
let drained = false;
|
|
39
40
|
|
|
40
|
-
const getAndRun = function(cb) {
|
|
41
|
-
_next(function(err, doc) {
|
|
42
|
-
if (err) return cb(err);
|
|
43
|
-
if (drained) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
if (doc == null) {
|
|
47
|
-
drained = true;
|
|
48
|
-
return callback(null);
|
|
49
|
-
}
|
|
50
|
-
handleNextResult(doc, function(err) {
|
|
51
|
-
if (err) return cb(err);
|
|
52
|
-
// Make sure to clear the stack re: gh-4697
|
|
53
|
-
setTimeout(function() {
|
|
54
|
-
getAndRun(cb);
|
|
55
|
-
}, 0);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
};
|
|
59
|
-
|
|
60
41
|
let error = null;
|
|
61
42
|
for (let i = 0; i < parallel; ++i) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
43
|
+
enqueue(fetch);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fetch(done) {
|
|
47
|
+
if (drained || error) {
|
|
48
|
+
return done();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
next(function(err, doc) {
|
|
52
|
+
if (drained || error) {
|
|
53
|
+
return done();
|
|
65
54
|
}
|
|
66
55
|
if (err != null) {
|
|
67
56
|
error = err;
|
|
68
|
-
|
|
57
|
+
callback(err);
|
|
58
|
+
return done();
|
|
59
|
+
}
|
|
60
|
+
if (doc == null) {
|
|
61
|
+
drained = true;
|
|
62
|
+
callback(null);
|
|
63
|
+
return done();
|
|
69
64
|
}
|
|
65
|
+
|
|
66
|
+
done();
|
|
67
|
+
|
|
68
|
+
handleNextResult(doc, function(err) {
|
|
69
|
+
if (err != null) {
|
|
70
|
+
error = err;
|
|
71
|
+
return callback(err);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
setTimeout(() => enqueue(fetch), 0);
|
|
75
|
+
});
|
|
70
76
|
});
|
|
71
77
|
}
|
|
72
78
|
};
|
|
73
79
|
|
|
74
|
-
const _nextQueue = [];
|
|
75
80
|
return utils.promiseOrCallback(callback, cb => {
|
|
76
81
|
iterate(cb);
|
|
77
82
|
});
|
|
83
|
+
};
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
_nextQueue.push(cb);
|
|
87
|
-
}
|
|
85
|
+
// `next()` can only execute one at a time, so make sure we always execute
|
|
86
|
+
// `next()` in series, while still allowing multiple `fn()` instances to run
|
|
87
|
+
// in parallel.
|
|
88
|
+
function asyncQueue() {
|
|
89
|
+
const _queue = [];
|
|
90
|
+
let inProgress = null;
|
|
91
|
+
let id = 0;
|
|
88
92
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
return function enqueue(fn) {
|
|
94
|
+
if (_queue.length === 0 && inProgress == null) {
|
|
95
|
+
inProgress = id++;
|
|
96
|
+
return fn(_step);
|
|
97
|
+
}
|
|
98
|
+
_queue.push(fn);
|
|
99
|
+
};
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
function _step() {
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
inProgress = null;
|
|
104
|
+
if (_queue.length > 0) {
|
|
105
|
+
inProgress = id++;
|
|
106
|
+
const fn = _queue.shift();
|
|
107
|
+
fn(_step);
|
|
98
108
|
}
|
|
99
|
-
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
if (_nextQueue.length > 0) {
|
|
102
|
-
next(_step(_nextQueue.unshift()));
|
|
103
|
-
}
|
|
104
|
-
}, 0);
|
|
105
|
-
};
|
|
109
|
+
}, 0);
|
|
106
110
|
}
|
|
107
|
-
}
|
|
111
|
+
}
|
|
@@ -136,6 +136,9 @@ module.exports = function(query, schema, castedDoc, options, callback) {
|
|
|
136
136
|
_err.path = updates[i] + '.' + key;
|
|
137
137
|
validationErrors.push(_err);
|
|
138
138
|
}
|
|
139
|
+
} else {
|
|
140
|
+
err.path = updates[i];
|
|
141
|
+
validationErrors.push(err);
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
callback(null);
|
package/lib/index.js
CHANGED
|
@@ -1015,6 +1015,15 @@ Mongoose.prototype.now = function now() { return new Date(); };
|
|
|
1015
1015
|
|
|
1016
1016
|
Mongoose.prototype.CastError = require('./error/cast');
|
|
1017
1017
|
|
|
1018
|
+
/**
|
|
1019
|
+
* The constructor used for schematype options
|
|
1020
|
+
*
|
|
1021
|
+
* @method SchemaTypeOptions
|
|
1022
|
+
* @api public
|
|
1023
|
+
*/
|
|
1024
|
+
|
|
1025
|
+
Mongoose.prototype.SchemaTypeOptions = require('./options/SchemaTypeOptions');
|
|
1026
|
+
|
|
1018
1027
|
/**
|
|
1019
1028
|
* The [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver Mongoose uses.
|
|
1020
1029
|
*
|
package/lib/model.js
CHANGED
|
@@ -1066,7 +1066,8 @@ Model.exists = function exists(filter, options, callback) {
|
|
|
1066
1066
|
*
|
|
1067
1067
|
* @param {String} name discriminator model name
|
|
1068
1068
|
* @param {Schema} schema discriminator model schema
|
|
1069
|
-
* @param {String} value the string stored in the `discriminatorKey` property
|
|
1069
|
+
* @param {String} [value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
|
|
1070
|
+
* @return {Model} The newly created discriminator model
|
|
1070
1071
|
* @api public
|
|
1071
1072
|
*/
|
|
1072
1073
|
|
|
@@ -1149,7 +1150,7 @@ for (const i in EventEmitter.prototype) {
|
|
|
1149
1150
|
*
|
|
1150
1151
|
* Mongoose calls this function automatically when a model is created using
|
|
1151
1152
|
* [`mongoose.model()`](/docs/api.html#mongoose_Mongoose-model) or
|
|
1152
|
-
*
|
|
1153
|
+
* [`connection.model()`](/docs/api.html#connection_Connection-model), so you
|
|
1153
1154
|
* don't need to call it. This function is also idempotent, so you may call it
|
|
1154
1155
|
* to get back a promise that will resolve when your indexes are finished
|
|
1155
1156
|
* building as an alternative to [`MyModel.on('index')`](/docs/guide.html#indexes)
|
|
@@ -1963,7 +1964,7 @@ Model.deleteMany = function deleteMany(conditions, options, callback) {
|
|
|
1963
1964
|
* var promise = query.exec();
|
|
1964
1965
|
* promise.addBack(function (err, docs) {});
|
|
1965
1966
|
*
|
|
1966
|
-
* @param {Object} filter
|
|
1967
|
+
* @param {Object|ObjectId} filter
|
|
1967
1968
|
* @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](http://mongoosejs.com/docs/api.html#query_Query-select)
|
|
1968
1969
|
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
|
|
1969
1970
|
* @param {Function} [callback]
|
|
@@ -3411,7 +3412,7 @@ Model.bulkWrite = function(ops, options, callback) {
|
|
|
3411
3412
|
* var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
|
|
3412
3413
|
*
|
|
3413
3414
|
* @param {Object} obj
|
|
3414
|
-
* @return {
|
|
3415
|
+
* @return {Document} document instance
|
|
3415
3416
|
* @api public
|
|
3416
3417
|
*/
|
|
3417
3418
|
|
|
@@ -4120,10 +4121,11 @@ function populate(model, docs, options, callback) {
|
|
|
4120
4121
|
assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
|
|
4121
4122
|
|
|
4122
4123
|
if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
|
|
4123
|
-
// Ensure that we set populate virtuals
|
|
4124
|
-
// if we don't actually execute a query
|
|
4124
|
+
// Ensure that we set populate virtuals to 0 or empty array even
|
|
4125
|
+
// if we don't actually execute a query because they don't have
|
|
4126
|
+
// a value by default. See gh-7731, gh-8230
|
|
4125
4127
|
--_remaining;
|
|
4126
|
-
if (mod.count) {
|
|
4128
|
+
if (mod.count || mod.isVirtual) {
|
|
4127
4129
|
_assign(model, [], mod, assignmentOpts);
|
|
4128
4130
|
}
|
|
4129
4131
|
continue;
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const SchemaTypeOptions = require('./SchemaTypeOptions');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The options defined on an Array schematype.
|
|
7
|
+
*
|
|
8
|
+
* ####Example:
|
|
9
|
+
*
|
|
10
|
+
* const schema = new Schema({ tags: [String] });
|
|
11
|
+
* schema.path('tags').options; // SchemaArrayOptions instance
|
|
12
|
+
*
|
|
13
|
+
* @api public
|
|
14
|
+
* @inherits SchemaTypeOptions
|
|
15
|
+
* @constructor SchemaArrayOptions
|
|
16
|
+
*/
|
|
17
|
+
|
|
5
18
|
class SchemaArrayOptions extends SchemaTypeOptions {}
|
|
6
19
|
|
|
7
20
|
const opts = {
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const SchemaTypeOptions = require('./SchemaTypeOptions');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The options defined on a Buffer schematype.
|
|
7
|
+
*
|
|
8
|
+
* ####Example:
|
|
9
|
+
*
|
|
10
|
+
* const schema = new Schema({ bitmap: Buffer });
|
|
11
|
+
* schema.path('bitmap').options; // SchemaBufferOptions instance
|
|
12
|
+
*
|
|
13
|
+
* @api public
|
|
14
|
+
* @inherits SchemaTypeOptions
|
|
15
|
+
* @constructor SchemaBufferOptions
|
|
16
|
+
*/
|
|
17
|
+
|
|
5
18
|
class SchemaBufferOptions extends SchemaTypeOptions {}
|
|
6
19
|
|
|
7
20
|
const opts = {
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const SchemaTypeOptions = require('./SchemaTypeOptions');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The options defined on a Date schematype.
|
|
7
|
+
*
|
|
8
|
+
* ####Example:
|
|
9
|
+
*
|
|
10
|
+
* const schema = new Schema({ startedAt: Date });
|
|
11
|
+
* schema.path('startedAt').options; // SchemaDateOptions instance
|
|
12
|
+
*
|
|
13
|
+
* @api public
|
|
14
|
+
* @inherits SchemaTypeOptions
|
|
15
|
+
* @constructor SchemaDateOptions
|
|
16
|
+
*/
|
|
17
|
+
|
|
5
18
|
class SchemaDateOptions extends SchemaTypeOptions {}
|
|
6
19
|
|
|
7
20
|
const opts = {
|