mongoose 5.2.6 → 5.2.7
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/.travis.yml +1 -1
- package/History.md +17 -0
- package/lib/cast/number.js +49 -0
- package/lib/connection.js +6 -0
- package/lib/helpers/populate/getSchemaTypes.js +80 -62
- package/lib/helpers/query/castUpdate.js +34 -12
- package/lib/model.js +21 -7
- package/lib/query.js +17 -4
- package/lib/queryhelpers.js +8 -2
- package/lib/schema/number.js +11 -31
- package/lib/schematype.js +30 -16
- package/package.json +4 -5
- package/tools/checkNodeVersion.js +7 -0
package/.travis.yml
CHANGED
|
@@ -16,6 +16,6 @@ before_script:
|
|
|
16
16
|
- ./mongodb-linux-x86_64-3.6.4/bin/mongod --fork --nopreallocj --dbpath ./data/db/27017 --syslog --port 27017
|
|
17
17
|
script:
|
|
18
18
|
- npm test
|
|
19
|
-
- npm run lint
|
|
19
|
+
- node tools/checkNodeVersion.js "4.x || 5.x" || npm run lint
|
|
20
20
|
notifications:
|
|
21
21
|
email: false
|
package/History.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
5.2.7 / 2018-08-06
|
|
2
|
+
==================
|
|
3
|
+
* fix(model): check `expireAfterSeconds` option when diffing indexes in syncIndexes() #6820 #6819 [christopherhex](https://github.com/christopherhex)
|
|
4
|
+
* chore: fix some common test flakes in travis #6816 [Fonger](https://github.com/Fonger)
|
|
5
|
+
* chore: bump eslint and webpack to avoid bad versions of eslint-scope #6814
|
|
6
|
+
* test(model): add delay to session tests to improve pass rate #6811 [Fonger](https://github.com/Fonger)
|
|
7
|
+
* fix(model): support options in `deleteMany` #6810 [Fonger](https://github.com/Fonger)
|
|
8
|
+
* fix(query): don't use $each when pushing an array into an array #6809 [lineus](https://github.com/lineus)
|
|
9
|
+
* chore: bump mquery so eslint isn't a prod dependency #6800
|
|
10
|
+
* fix(populate): correctly get schema type when calling `populate()` on already populated path #6798
|
|
11
|
+
* fix(populate): propagate readConcern options in populate from parent query #6792 #6785 [Fonger](https://github.com/Fonger)
|
|
12
|
+
* docs(connection): add description of useNewUrlParser option #6789
|
|
13
|
+
* fix(query): make select('+path') a no-op if no select prop in schema #6785
|
|
14
|
+
* docs(schematype+validation): document using function syntax for custom validator message #6772
|
|
15
|
+
* fix(update): throw CastError if updating with `$inc: null` #6770
|
|
16
|
+
* fix(connection): throw helpful error when calling `createConnection(undefined)` #6763
|
|
17
|
+
|
|
1
18
|
5.2.6 / 2018-07-30
|
|
2
19
|
==================
|
|
3
20
|
* fix(document): don't double-call deeply nested custom getters when using `get()` #6779 #6637
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CastError = require('../error/cast');
|
|
4
|
+
|
|
5
|
+
/*!
|
|
6
|
+
* Given a value, cast it to a number, or throw a `CastError` if the value
|
|
7
|
+
* cannot be casted. `null` and `undefined` are considered valid.
|
|
8
|
+
*
|
|
9
|
+
* @param {Any} value
|
|
10
|
+
* @param {String} [path] optional the path to set on the CastError
|
|
11
|
+
* @return {Boolean|null|undefined}
|
|
12
|
+
* @throws {CastError} if `value` is not one of the allowed values
|
|
13
|
+
* @api private
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
module.exports = function castNumber(val, path) {
|
|
17
|
+
if (isNaN(val)) {
|
|
18
|
+
throw new CastError('number', val, path);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (val == null) {
|
|
22
|
+
return val;
|
|
23
|
+
}
|
|
24
|
+
if (val === '') {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof val === 'string' || typeof val === 'boolean') {
|
|
29
|
+
val = Number(val);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isNaN(val)) {
|
|
33
|
+
throw new CastError('number', val, path);
|
|
34
|
+
}
|
|
35
|
+
if (val instanceof Number) {
|
|
36
|
+
return val;
|
|
37
|
+
}
|
|
38
|
+
if (typeof val === 'number') {
|
|
39
|
+
return val;
|
|
40
|
+
}
|
|
41
|
+
if (!Array.isArray(val) && typeof val.valueOf === 'function') {
|
|
42
|
+
return Number(val.valueOf());
|
|
43
|
+
}
|
|
44
|
+
if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
|
|
45
|
+
return new Number(val);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
throw new CastError('number', val, path);
|
|
49
|
+
};
|
package/lib/connection.js
CHANGED
|
@@ -420,6 +420,12 @@ Connection.prototype.openUri = function(uri, options, callback) {
|
|
|
420
420
|
'http://mongoosejs.com/docs/connections.html for supported connection syntax');
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
if (typeof uri !== 'string') {
|
|
424
|
+
throw new MongooseError('The `uri` parameter to `openUri()` must be a ' +
|
|
425
|
+
`string, got "${typeof uri}". Make sure the first parameter to ` +
|
|
426
|
+
'`mongoose.connect()` or `mongoose.createConnection()` is a string.');
|
|
427
|
+
}
|
|
428
|
+
|
|
423
429
|
const Promise = PromiseProvider.get();
|
|
424
430
|
const _this = this;
|
|
425
431
|
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* ignore
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const Mixed = require('../../schema/mixed');
|
|
8
|
+
const get = require('lodash.get');
|
|
9
|
+
const mpath = require('mpath');
|
|
9
10
|
|
|
10
11
|
/*!
|
|
11
12
|
* @param {Schema} schema
|
|
@@ -28,79 +29,96 @@ module.exports = function getSchemaTypes(schema, doc, path) {
|
|
|
28
29
|
while (p--) {
|
|
29
30
|
trypath = parts.slice(0, p).join('.');
|
|
30
31
|
foundschema = schema.path(trypath);
|
|
31
|
-
if (foundschema) {
|
|
32
|
-
if (foundschema.caster) {
|
|
33
|
-
// array of Mixed?
|
|
34
|
-
if (foundschema.caster instanceof Mixed) {
|
|
35
|
-
return foundschema.caster;
|
|
36
|
-
}
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const discriminatorKeyPath = trypath + '.' +
|
|
42
|
-
foundschema.schema.options.discriminatorKey;
|
|
43
|
-
const keys = mpath.get(discriminatorKeyPath, subdoc) || [];
|
|
44
|
-
schemas = Object.keys(discriminators).
|
|
45
|
-
reduce(function(cur, discriminator) {
|
|
46
|
-
if (keys.indexOf(discriminator) !== -1) {
|
|
47
|
-
cur.push(discriminators[discriminator]);
|
|
48
|
-
}
|
|
49
|
-
return cur;
|
|
50
|
-
}, []);
|
|
51
|
-
}
|
|
33
|
+
if (foundschema == null) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
52
36
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// a path like array.$
|
|
59
|
-
if (p !== parts.length && foundschema.schema) {
|
|
60
|
-
let ret;
|
|
61
|
-
if (parts[p] === '$') {
|
|
62
|
-
if (p + 1 === parts.length) {
|
|
63
|
-
// comments.$
|
|
64
|
-
return foundschema;
|
|
65
|
-
}
|
|
66
|
-
// comments.$.comments.$.title
|
|
67
|
-
ret = search(parts.slice(p + 1), schema, mpath.get(trypath, subdoc));
|
|
68
|
-
if (ret) {
|
|
69
|
-
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
|
70
|
-
!foundschema.schema.$isSingleNested;
|
|
71
|
-
}
|
|
72
|
-
return ret;
|
|
73
|
-
}
|
|
37
|
+
if (foundschema.caster) {
|
|
38
|
+
// array of Mixed?
|
|
39
|
+
if (foundschema.caster instanceof Mixed) {
|
|
40
|
+
return foundschema.caster;
|
|
41
|
+
}
|
|
74
42
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ret.push(_ret);
|
|
86
|
-
}
|
|
43
|
+
let schemas = null;
|
|
44
|
+
if (doc != null && foundschema.schema != null && foundschema.schema.discriminators != null) {
|
|
45
|
+
const discriminators = foundschema.schema.discriminators;
|
|
46
|
+
const discriminatorKeyPath = trypath + '.' +
|
|
47
|
+
foundschema.schema.options.discriminatorKey;
|
|
48
|
+
const keys = mpath.get(discriminatorKeyPath, subdoc) || [];
|
|
49
|
+
schemas = Object.keys(discriminators).
|
|
50
|
+
reduce(function(cur, discriminator) {
|
|
51
|
+
if (keys.indexOf(discriminator) !== -1) {
|
|
52
|
+
cur.push(discriminators[discriminator]);
|
|
87
53
|
}
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
54
|
+
return cur;
|
|
55
|
+
}, []);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Now that we found the array, we need to check if there
|
|
59
|
+
// are remaining document paths to look up for casting.
|
|
60
|
+
// Also we need to handle array.$.path since schema.path
|
|
61
|
+
// doesn't work for that.
|
|
62
|
+
// If there is no foundschema.schema we are dealing with
|
|
63
|
+
// a path like array.$
|
|
64
|
+
if (p !== parts.length && foundschema.schema) {
|
|
65
|
+
let ret;
|
|
66
|
+
if (parts[p] === '$') {
|
|
67
|
+
if (p + 1 === parts.length) {
|
|
68
|
+
// comments.$
|
|
69
|
+
return foundschema;
|
|
70
|
+
}
|
|
71
|
+
// comments.$.comments.$.title
|
|
72
|
+
ret = search(parts.slice(p + 1), schema, mpath.get(trypath, subdoc));
|
|
73
|
+
if (ret) {
|
|
74
|
+
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
|
75
|
+
!foundschema.schema.$isSingleNested;
|
|
76
|
+
}
|
|
77
|
+
return ret;
|
|
78
|
+
}
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
|
|
80
|
+
if (schemas != null && schemas.length > 0) {
|
|
81
|
+
ret = [];
|
|
82
|
+
for (var i = 0; i < schemas.length; ++i) {
|
|
83
|
+
let _ret = search(parts.slice(p), schemas[i], mpath.get(trypath, subdoc));
|
|
84
|
+
if (_ret != null) {
|
|
85
|
+
_ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
|
|
94
86
|
!foundschema.schema.$isSingleNested;
|
|
87
|
+
if (_ret.$isUnderneathDocArray) {
|
|
88
|
+
ret.$isUnderneathDocArray = true;
|
|
89
|
+
}
|
|
90
|
+
ret.push(_ret);
|
|
95
91
|
}
|
|
92
|
+
}
|
|
93
|
+
return ret;
|
|
94
|
+
} else {
|
|
95
|
+
ret = search(parts.slice(p), foundschema.schema, mpath.get(trypath, subdoc));
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
if (ret) {
|
|
98
|
+
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
|
99
|
+
!foundschema.schema.$isSingleNested;
|
|
98
100
|
}
|
|
101
|
+
|
|
102
|
+
return ret;
|
|
99
103
|
}
|
|
100
104
|
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (doc.$__ && doc.populated(trypath)) {
|
|
108
|
+
const schema = get(doc.$__.populated[trypath], 'options.model.schema');
|
|
109
|
+
if (schema != null) {
|
|
110
|
+
const ret = search(parts.slice(p), schema, mpath.get(trypath, subdoc));
|
|
111
|
+
|
|
112
|
+
if (ret) {
|
|
113
|
+
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
|
114
|
+
!schema.$isSingleNested;
|
|
115
|
+
}
|
|
101
116
|
|
|
102
|
-
|
|
117
|
+
return ret;
|
|
118
|
+
}
|
|
103
119
|
}
|
|
120
|
+
|
|
121
|
+
return foundschema;
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
124
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const CastError = require('../../error/cast');
|
|
3
4
|
const StrictModeError = require('../../error/strict');
|
|
4
5
|
const ValidationError = require('../../error/validation');
|
|
6
|
+
const castNumber = require('../../cast/number');
|
|
5
7
|
const getEmbeddedDiscriminatorPath = require('./getEmbeddedDiscriminatorPath');
|
|
6
8
|
const utils = require('../../utils');
|
|
7
9
|
|
|
@@ -136,7 +138,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
|
|
|
136
138
|
hasKeys = true;
|
|
137
139
|
try {
|
|
138
140
|
obj[key] = {
|
|
139
|
-
$each: castUpdateVal(schematype, val.$each, op, context)
|
|
141
|
+
$each: castUpdateVal(schematype, val.$each, op, context, prefix + key)
|
|
140
142
|
};
|
|
141
143
|
} catch (error) {
|
|
142
144
|
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
|
@@ -155,7 +157,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
|
|
|
155
157
|
}
|
|
156
158
|
} else {
|
|
157
159
|
try {
|
|
158
|
-
obj[key] = castUpdateVal(schematype, val, op, context);
|
|
160
|
+
obj[key] = castUpdateVal(schematype, val, op, context, prefix + key);
|
|
159
161
|
} catch (error) {
|
|
160
162
|
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
|
161
163
|
}
|
|
@@ -170,7 +172,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
|
|
|
170
172
|
} else if ((op === '$currentDate') || (op in castOps && schematype)) {
|
|
171
173
|
// $currentDate can take an object
|
|
172
174
|
try {
|
|
173
|
-
obj[key] = castUpdateVal(schematype, val, op, context);
|
|
175
|
+
obj[key] = castUpdateVal(schematype, val, op, context, prefix + key);
|
|
174
176
|
} catch (error) {
|
|
175
177
|
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
|
176
178
|
}
|
|
@@ -249,13 +251,15 @@ function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
|
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
try {
|
|
252
|
-
obj[key] = castUpdateVal(schematype, val, op, key, context);
|
|
254
|
+
obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
|
|
253
255
|
} catch (error) {
|
|
254
256
|
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
if (Array.isArray(obj[key]) && (op === '$addToSet' || op === '$push') && key !== '$each') {
|
|
258
|
-
|
|
260
|
+
if (schematype.caster && !schematype.caster.$isMongooseArray) {
|
|
261
|
+
obj[key] = { $each: obj[key] };
|
|
262
|
+
}
|
|
259
263
|
}
|
|
260
264
|
|
|
261
265
|
if (options.omitUndefined && obj[key] === void 0) {
|
|
@@ -295,10 +299,17 @@ function _handleCastError(error, query, key, aggregatedError) {
|
|
|
295
299
|
|
|
296
300
|
var numberOps = {
|
|
297
301
|
$pop: 1,
|
|
298
|
-
$unset: 1,
|
|
299
302
|
$inc: 1
|
|
300
303
|
};
|
|
301
304
|
|
|
305
|
+
/*!
|
|
306
|
+
* These ops require no casting because the RHS doesn't do anything.
|
|
307
|
+
*/
|
|
308
|
+
|
|
309
|
+
const noCastOps = {
|
|
310
|
+
$unset: 1
|
|
311
|
+
};
|
|
312
|
+
|
|
302
313
|
/*!
|
|
303
314
|
* These operators require casting docs
|
|
304
315
|
* to real Documents for Update operations.
|
|
@@ -331,12 +342,12 @@ var overwriteOps = {
|
|
|
331
342
|
* @api private
|
|
332
343
|
*/
|
|
333
344
|
|
|
334
|
-
function castUpdateVal(schema, val, op, $conditional, context) {
|
|
345
|
+
function castUpdateVal(schema, val, op, $conditional, context, path) {
|
|
335
346
|
if (!schema) {
|
|
336
347
|
// non-existing schema path
|
|
337
|
-
return op in numberOps
|
|
338
|
-
|
|
339
|
-
|
|
348
|
+
return op in numberOps ?
|
|
349
|
+
castNumber(val, path) :
|
|
350
|
+
val;
|
|
340
351
|
}
|
|
341
352
|
|
|
342
353
|
var cond = schema.caster && op in castOps &&
|
|
@@ -357,11 +368,22 @@ function castUpdateVal(schema, val, op, $conditional, context) {
|
|
|
357
368
|
return schema.cast(val);
|
|
358
369
|
}
|
|
359
370
|
|
|
371
|
+
if (op in noCastOps) {
|
|
372
|
+
return val;
|
|
373
|
+
}
|
|
360
374
|
if (op in numberOps) {
|
|
375
|
+
// Null and undefined not allowed for $pop, $inc
|
|
376
|
+
if (val == null) {
|
|
377
|
+
throw new CastError('number', val, schema.path);
|
|
378
|
+
}
|
|
361
379
|
if (op === '$inc') {
|
|
362
|
-
|
|
380
|
+
// Support `$inc` with long, int32, etc. (gh-4283)
|
|
381
|
+
return schema.castForQueryWrapper({
|
|
382
|
+
val: val,
|
|
383
|
+
context: context
|
|
384
|
+
});
|
|
363
385
|
}
|
|
364
|
-
return
|
|
386
|
+
return castNumber(val, schema.path);
|
|
365
387
|
}
|
|
366
388
|
if (op === '$currentDate') {
|
|
367
389
|
if (typeof val === 'object') {
|
package/lib/model.js
CHANGED
|
@@ -1044,7 +1044,7 @@ Model.syncIndexes = function syncIndexes(options, callback) {
|
|
|
1044
1044
|
utils.clone(schemaIndex[1]));
|
|
1045
1045
|
|
|
1046
1046
|
// If these options are different, need to rebuild the index
|
|
1047
|
-
const optionKeys = ['unique', 'partialFilterExpression', 'sparse'];
|
|
1047
|
+
const optionKeys = ['unique', 'partialFilterExpression', 'sparse', 'expireAfterSeconds'];
|
|
1048
1048
|
const indexCopy = Object.assign({}, index);
|
|
1049
1049
|
for (const key of optionKeys) {
|
|
1050
1050
|
if (!(key in options) && !(key in indexCopy)) {
|
|
@@ -1480,19 +1480,26 @@ Model.deleteOne = function deleteOne(conditions, callback) {
|
|
|
1480
1480
|
* Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
|
|
1481
1481
|
*
|
|
1482
1482
|
* @param {Object} conditions
|
|
1483
|
+
* @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
|
|
1483
1484
|
* @param {Function} [callback]
|
|
1484
1485
|
* @return {Query}
|
|
1485
1486
|
* @api public
|
|
1486
1487
|
*/
|
|
1487
1488
|
|
|
1488
|
-
Model.deleteMany = function deleteMany(conditions, callback) {
|
|
1489
|
+
Model.deleteMany = function deleteMany(conditions, options, callback) {
|
|
1489
1490
|
if (typeof conditions === 'function') {
|
|
1490
1491
|
callback = conditions;
|
|
1491
1492
|
conditions = {};
|
|
1493
|
+
options = null;
|
|
1494
|
+
}
|
|
1495
|
+
else if (typeof options === 'function') {
|
|
1496
|
+
callback = options;
|
|
1497
|
+
options = null;
|
|
1492
1498
|
}
|
|
1493
1499
|
|
|
1494
1500
|
// get the mongodb collection object
|
|
1495
1501
|
var mq = new this.Query(conditions, {}, this, this.collection);
|
|
1502
|
+
mq.setOptions(options);
|
|
1496
1503
|
|
|
1497
1504
|
if (callback) {
|
|
1498
1505
|
callback = this.$wrapCallback(callback);
|
|
@@ -2503,10 +2510,17 @@ Model.create = function create(doc, options, callback) {
|
|
|
2503
2510
|
* This function does **not** trigger any middleware. In particular, it
|
|
2504
2511
|
* does **not** trigger aggregate middleware.
|
|
2505
2512
|
*
|
|
2513
|
+
* The ChangeStream object is an event emitter that emits the following events:
|
|
2514
|
+
*
|
|
2515
|
+
* - 'change': A change occurred, see below example
|
|
2516
|
+
* - 'error': An unrecoverable error occurred. In particular, change streams currently error out if they lose connection to the replica set primary. Follow [this GitHub issue](https://github.com/Automattic/mongoose/issues/6799) for updates.
|
|
2517
|
+
* - 'end': Emitted if the underlying stream is closed
|
|
2518
|
+
* - 'close': Emitted if the underlying stream is closed
|
|
2519
|
+
*
|
|
2506
2520
|
* ####Example:
|
|
2507
2521
|
*
|
|
2508
2522
|
* const doc = await Person.create({ name: 'Ned Stark' });
|
|
2509
|
-
* Person.watch().on('change', change => console.log(change));
|
|
2523
|
+
* const changeStream = Person.watch().on('change', change => console.log(change));
|
|
2510
2524
|
* // Will print from the above `console.log()`:
|
|
2511
2525
|
* // { _id: { _data: ... },
|
|
2512
2526
|
* // operationType: 'delete',
|
|
@@ -3872,10 +3886,10 @@ function getModelsMapForPopulate(model, docs, options) {
|
|
|
3872
3886
|
let discriminatorKey;
|
|
3873
3887
|
let modelForFindSchema;
|
|
3874
3888
|
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3889
|
+
let originalModel = options.model;
|
|
3890
|
+
let isVirtual = false;
|
|
3891
|
+
let isRefPathArray = false;
|
|
3892
|
+
const modelSchema = model.schema;
|
|
3879
3893
|
|
|
3880
3894
|
for (i = 0; i < len; i++) {
|
|
3881
3895
|
doc = docs[i];
|
package/lib/query.js
CHANGED
|
@@ -844,13 +844,20 @@ Query.prototype.mod = function() {
|
|
|
844
844
|
* // exclude c and d, include other fields
|
|
845
845
|
* query.select('-c -d');
|
|
846
846
|
*
|
|
847
|
+
* // Use `+` to override schema-level `select: false` without making the
|
|
848
|
+
* // projection inclusive.
|
|
849
|
+
* const schema = new Schema({
|
|
850
|
+
* foo: { type: String, select: false },
|
|
851
|
+
* bar: String
|
|
852
|
+
* });
|
|
853
|
+
* // ...
|
|
854
|
+
* query.select('+foo'); // Override foo's `select: false` without excluding `bar`
|
|
855
|
+
*
|
|
847
856
|
* // or you may use object notation, useful when
|
|
848
857
|
* // you have keys already prefixed with a "-"
|
|
849
858
|
* query.select({ a: 1, b: 1 });
|
|
850
859
|
* query.select({ c: 0, d: 0 });
|
|
851
860
|
*
|
|
852
|
-
* // force inclusion of field excluded at schema level
|
|
853
|
-
* query.select('+path')
|
|
854
861
|
*
|
|
855
862
|
* @method select
|
|
856
863
|
* @memberOf Query
|
|
@@ -3736,11 +3743,17 @@ Query.prototype.populate = function() {
|
|
|
3736
3743
|
|
|
3737
3744
|
const res = utils.populate.apply(null, arguments);
|
|
3738
3745
|
|
|
3739
|
-
// Propagate readPreference and lean from parent query,
|
|
3740
|
-
// specified
|
|
3746
|
+
// Propagate readConcern and readPreference and lean from parent query,
|
|
3747
|
+
// unless one already specified
|
|
3741
3748
|
if (this.options != null) {
|
|
3749
|
+
const readConcern = this.options.readConcern;
|
|
3742
3750
|
const readPref = this.options.readPreference;
|
|
3751
|
+
|
|
3743
3752
|
for (let i = 0; i < res.length; ++i) {
|
|
3753
|
+
if (readConcern != null && get(res[i], 'options.readConcern') == null) {
|
|
3754
|
+
res[i].options = res[i].options || {};
|
|
3755
|
+
res[i].options.readConcern = readConcern;
|
|
3756
|
+
}
|
|
3744
3757
|
if (readPref != null && get(res[i], 'options.readPreference') == null) {
|
|
3745
3758
|
res[i].options = res[i].options || {};
|
|
3746
3759
|
res[i].options.readPreference = readPref;
|
package/lib/queryhelpers.js
CHANGED
|
@@ -188,10 +188,16 @@ exports.applyPaths = function applyPaths(fields, schema) {
|
|
|
188
188
|
let stack = [];
|
|
189
189
|
|
|
190
190
|
let analyzePath = function(path, type) {
|
|
191
|
+
var plusPath = '+' + path;
|
|
192
|
+
var hasPlusPath = fields && plusPath in fields;
|
|
193
|
+
if (hasPlusPath) {
|
|
194
|
+
// forced inclusion
|
|
195
|
+
delete fields[plusPath];
|
|
196
|
+
}
|
|
197
|
+
|
|
191
198
|
if (typeof type.selected !== 'boolean') return;
|
|
192
199
|
|
|
193
|
-
|
|
194
|
-
if (fields && plusPath in fields) {
|
|
200
|
+
if (hasPlusPath) {
|
|
195
201
|
// forced inclusion
|
|
196
202
|
delete fields[plusPath];
|
|
197
203
|
|
package/lib/schema/number.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/*!
|
|
2
4
|
* Module requirements.
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const MongooseError = require('../error');
|
|
8
|
+
const SchemaType = require('../schematype');
|
|
9
|
+
const castNumber = require('../cast/number');
|
|
10
|
+
const handleBitwiseOperator = require('./operators/bitwise');
|
|
11
|
+
const utils = require('../utils');
|
|
12
|
+
|
|
13
|
+
const CastError = SchemaType.CastError;
|
|
14
|
+
let Document;
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* Number SchemaType constructor.
|
|
@@ -207,31 +211,7 @@ SchemaNumber.prototype.cast = function(value, doc, init) {
|
|
|
207
211
|
value._id : // documents
|
|
208
212
|
value;
|
|
209
213
|
|
|
210
|
-
|
|
211
|
-
if (val === null) {
|
|
212
|
-
return val;
|
|
213
|
-
}
|
|
214
|
-
if (val === '') {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
if (typeof val === 'string' || typeof val === 'boolean') {
|
|
218
|
-
val = Number(val);
|
|
219
|
-
}
|
|
220
|
-
if (val instanceof Number) {
|
|
221
|
-
return val;
|
|
222
|
-
}
|
|
223
|
-
if (typeof val === 'number') {
|
|
224
|
-
return val;
|
|
225
|
-
}
|
|
226
|
-
if (!Array.isArray(val) && typeof val.valueOf === 'function') {
|
|
227
|
-
return Number(val.valueOf());
|
|
228
|
-
}
|
|
229
|
-
if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
|
|
230
|
-
return new Number(val);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
throw new CastError('number', value, this.path);
|
|
214
|
+
return castNumber(val, this.path);
|
|
235
215
|
};
|
|
236
216
|
|
|
237
217
|
/*!
|
package/lib/schematype.js
CHANGED
|
@@ -414,7 +414,9 @@ SchemaType.prototype.get = function(fn) {
|
|
|
414
414
|
/**
|
|
415
415
|
* Adds validator(s) for this document path.
|
|
416
416
|
*
|
|
417
|
-
* Validators always receive the value to validate as their first argument and
|
|
417
|
+
* Validators always receive the value to validate as their first argument and
|
|
418
|
+
* must return `Boolean`. Returning `false` or throwing an error means
|
|
419
|
+
* validation failed.
|
|
418
420
|
*
|
|
419
421
|
* The error message argument is optional. If not passed, the [default generic error message template](#error_messages_MongooseError-messages) will be used.
|
|
420
422
|
*
|
|
@@ -446,24 +448,35 @@ SchemaType.prototype.get = function(fn) {
|
|
|
446
448
|
*
|
|
447
449
|
* ####Error message templates:
|
|
448
450
|
*
|
|
449
|
-
* From the examples above, you may have noticed that error messages support
|
|
451
|
+
* From the examples above, you may have noticed that error messages support
|
|
452
|
+
* basic templating. There are a few other template keywords besides `{PATH}`
|
|
453
|
+
* and `{VALUE}` too. To find out more, details are available
|
|
454
|
+
* [here](#error_messages_MongooseError.messages).
|
|
450
455
|
*
|
|
451
|
-
*
|
|
456
|
+
* If Mongoose's built-in error message templating isn't enough, Mongoose
|
|
457
|
+
* supports setting the `message` property to a function.
|
|
452
458
|
*
|
|
453
|
-
*
|
|
459
|
+
* schema.path('name').validate({
|
|
460
|
+
* validator: v => v.length > 5,
|
|
461
|
+
* // `errors['name']` will be "name must have length > 5, got 'foo'"
|
|
462
|
+
* message: props => `${props.path} must have length > 5, got '${props.value}'`
|
|
463
|
+
* });
|
|
464
|
+
*
|
|
465
|
+
* To bypass Mongoose's error messages and just copy the error message that
|
|
466
|
+
* the validator throws, do this:
|
|
454
467
|
*
|
|
455
468
|
* schema.path('name').validate({
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
* ...
|
|
460
|
-
* respond(false); // validation failed
|
|
461
|
-
* });
|
|
462
|
-
* },
|
|
463
|
-
* message: 'Custom error message!' // Optional
|
|
469
|
+
* validator: () => { throw new Error('Oops!'); },
|
|
470
|
+
* // `errors['name']` will be "Oops!"
|
|
471
|
+
* message: props => props.reason.message
|
|
464
472
|
* });
|
|
465
473
|
*
|
|
466
|
-
*
|
|
474
|
+
* ####Asynchronous validation:
|
|
475
|
+
*
|
|
476
|
+
* Mongoose supports validators that return a promise. A validator that returns
|
|
477
|
+
* a promise is called an _async validator_. Async validators run in
|
|
478
|
+
* parallel, and `validate()` will wait until all async validators have settled.
|
|
479
|
+
*
|
|
467
480
|
* schema.path('name').validate({
|
|
468
481
|
* validator: function (value) {
|
|
469
482
|
* return new Promise(function (resolve, reject) {
|
|
@@ -485,13 +498,14 @@ SchemaType.prototype.get = function(fn) {
|
|
|
485
498
|
* var dvd = new Product(..);
|
|
486
499
|
* dvd.save(); // emits error on the `conn` above
|
|
487
500
|
*
|
|
488
|
-
* If you
|
|
501
|
+
* If you want to handle these errors at the Model level, add an `error`
|
|
502
|
+
* listener to your Model as shown below.
|
|
489
503
|
*
|
|
490
504
|
* // registering an error listener on the Model lets us handle errors more locally
|
|
491
505
|
* Product.on('error', handleError);
|
|
492
506
|
*
|
|
493
507
|
* @param {RegExp|Function|Object} obj validator
|
|
494
|
-
* @param {String} [errorMsg] optional error message
|
|
508
|
+
* @param {String|Function} [errorMsg] optional error message. If function, should return the error message as a string
|
|
495
509
|
* @param {String} [type] optional validator type
|
|
496
510
|
* @return {SchemaType} this
|
|
497
511
|
* @api public
|
|
@@ -836,7 +850,7 @@ SchemaType.prototype.doValidate = function(value, fn, scope) {
|
|
|
836
850
|
asyncValidate(validator, scope, value, validatorProperties, validate);
|
|
837
851
|
} else {
|
|
838
852
|
try {
|
|
839
|
-
ok = validator.call(scope, value);
|
|
853
|
+
ok = validator.call(scope, value, validatorProperties);
|
|
840
854
|
} catch (error) {
|
|
841
855
|
ok = false;
|
|
842
856
|
validatorProperties.reason = error;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoose",
|
|
3
3
|
"description": "Mongoose MongoDB ODM",
|
|
4
|
-
"version": "5.2.
|
|
4
|
+
"version": "5.2.7",
|
|
5
5
|
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongodb",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"mongodb-core": "3.1.0",
|
|
28
28
|
"mongoose-legacy-pluralize": "1.0.2",
|
|
29
29
|
"mpath": "0.4.1",
|
|
30
|
-
"mquery": "3.1.
|
|
30
|
+
"mquery": "3.1.2",
|
|
31
31
|
"ms": "2.0.0",
|
|
32
32
|
"regexp-clone": "0.0.1",
|
|
33
33
|
"sliced": "1.0.1"
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"bluebird": "3.5.0",
|
|
42
42
|
"co": "4.6.0",
|
|
43
43
|
"dox": "0.3.1",
|
|
44
|
-
"eslint": "
|
|
44
|
+
"eslint": "5.3.0",
|
|
45
45
|
"highlight.js": "9.1.0",
|
|
46
46
|
"jade": "1.11.0",
|
|
47
47
|
"lodash": "4.17.5",
|
|
@@ -58,14 +58,13 @@
|
|
|
58
58
|
"tbd": "0.6.4",
|
|
59
59
|
"uuid": "2.0.3",
|
|
60
60
|
"uuid-parse": "1.0.0",
|
|
61
|
-
"webpack": "4.
|
|
61
|
+
"webpack": "4.16.4",
|
|
62
62
|
"validator": "5.4.0"
|
|
63
63
|
},
|
|
64
64
|
"directories": {
|
|
65
65
|
"lib": "./lib/mongoose"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
|
-
"fix-lint": "eslint . --fix",
|
|
69
68
|
"lint": "eslint . --quiet",
|
|
70
69
|
"release": "git pull && git push origin master --tags && npm publish",
|
|
71
70
|
"release-legacy": "git pull origin 4.x && git push origin 4.x --tags && npm publish --tag legacy",
|