mongoose 6.3.0 → 6.3.3
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/README.md +2 -2
- package/dist/browser.umd.js +1 -1
- package/lib/collection.js +0 -7
- package/lib/connection.js +2 -1
- package/lib/document.js +65 -68
- package/lib/error/cast.js +8 -2
- package/lib/helpers/common.js +0 -8
- package/lib/helpers/immediate.js +3 -1
- package/lib/helpers/isAsyncFunction.js +19 -7
- package/lib/helpers/model/castBulkWrite.js +12 -0
- package/lib/helpers/populate/getModelsMapForPopulate.js +13 -10
- package/lib/helpers/query/cast$expr.js +4 -1
- package/lib/helpers/schematype/handleImmutable.js +4 -1
- package/lib/helpers/timestamps/setupTimestamps.js +2 -2
- package/lib/index.js +1 -1
- package/lib/internal.js +0 -1
- package/lib/model.js +42 -28
- package/lib/query.js +7 -3
- package/lib/queryhelpers.js +11 -1
- package/lib/schema/SubdocumentPath.js +3 -15
- package/lib/schema/documentarray.js +7 -7
- package/lib/schema/number.js +1 -5
- package/lib/schema/objectid.js +2 -4
- package/lib/schema/string.js +3 -6
- package/lib/schema.js +1 -1
- package/lib/schematype.js +2 -2
- package/lib/types/DocumentArray/methods/index.js +9 -1
- package/lib/types/array/methods/index.js +8 -9
- package/lib/utils.js +4 -0
- package/package.json +39 -19
- package/tsconfig.json +2 -2
- package/types/aggregate.d.ts +1 -2
- package/types/connection.d.ts +4 -5
- package/types/cursor.d.ts +3 -2
- package/types/document.d.ts +18 -15
- package/types/error.d.ts +124 -124
- package/types/index.d.ts +182 -154
- package/types/mongooseoptions.d.ts +1 -2
- package/types/schemaoptions.d.ts +1 -2
- package/CHANGELOG.md +0 -7249
- package/History.md +0 -1
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;
|
package/lib/connection.js
CHANGED
|
@@ -1357,7 +1357,8 @@ Connection.prototype.optionsProvideAuthenticationData = function(options) {
|
|
|
1357
1357
|
* that this connection uses to talk to MongoDB.
|
|
1358
1358
|
*
|
|
1359
1359
|
* #### Example:
|
|
1360
|
-
* const conn = await mongoose.createConnection('mongodb://localhost:27017/test')
|
|
1360
|
+
* const conn = await mongoose.createConnection('mongodb://localhost:27017/test').
|
|
1361
|
+
* asPromise();
|
|
1361
1362
|
*
|
|
1362
1363
|
* conn.getClient(); // MongoClient { ... }
|
|
1363
1364
|
*
|
package/lib/document.js
CHANGED
|
@@ -53,6 +53,7 @@ const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
|
|
|
53
53
|
const scopeSymbol = require('./helpers/symbols').scopeSymbol;
|
|
54
54
|
const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
|
|
55
55
|
const parentPaths = require('./helpers/path/parentPaths');
|
|
56
|
+
|
|
56
57
|
let DocumentArray;
|
|
57
58
|
let MongooseArray;
|
|
58
59
|
let Embedded;
|
|
@@ -1048,7 +1049,6 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1048
1049
|
const merge = options.merge;
|
|
1049
1050
|
const adhoc = type && type !== true;
|
|
1050
1051
|
const constructing = type === true;
|
|
1051
|
-
const typeKey = this.$__schema.options.typeKey;
|
|
1052
1052
|
let adhocs;
|
|
1053
1053
|
let keys;
|
|
1054
1054
|
let i = 0;
|
|
@@ -1150,14 +1150,15 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1150
1150
|
}
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
-
// Ensure all properties are in correct order
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1153
|
+
// Ensure all properties are in correct order
|
|
1154
|
+
const orderedDoc = {};
|
|
1155
|
+
const orderedKeys = Object.keys(this.$__schema.tree);
|
|
1156
|
+
for (let i = 0, len = orderedKeys.length; i < len; ++i) {
|
|
1157
|
+
(key = orderedKeys[i]) &&
|
|
1158
|
+
(this._doc.hasOwnProperty(key)) &&
|
|
1159
|
+
(orderedDoc[key] = undefined);
|
|
1160
1160
|
}
|
|
1161
|
+
this._doc = Object.assign(orderedDoc, this._doc);
|
|
1161
1162
|
|
|
1162
1163
|
return this;
|
|
1163
1164
|
}
|
|
@@ -1381,6 +1382,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1381
1382
|
}
|
|
1382
1383
|
|
|
1383
1384
|
let popOpts;
|
|
1385
|
+
const typeKey = this.$__schema.options.typeKey;
|
|
1384
1386
|
if (schema.options &&
|
|
1385
1387
|
Array.isArray(schema.options[typeKey]) &&
|
|
1386
1388
|
schema.options[typeKey].length &&
|
|
@@ -1400,7 +1402,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1400
1402
|
// later in `$__set()` because we don't take `_doc` when we iterate through
|
|
1401
1403
|
// a single nested doc. That's to make sure we get the correct context.
|
|
1402
1404
|
// Otherwise we would double-call the setter, see gh-7196.
|
|
1403
|
-
val = schema.applySetters(val, this, false, priorVal);
|
|
1405
|
+
val = schema.applySetters(val, this, false, priorVal, options);
|
|
1404
1406
|
}
|
|
1405
1407
|
|
|
1406
1408
|
if (Array.isArray(val) &&
|
|
@@ -1454,14 +1456,18 @@ Document.prototype.$set = function $set(path, val, type, options) {
|
|
|
1454
1456
|
}
|
|
1455
1457
|
|
|
1456
1458
|
if (shouldSet) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
if (
|
|
1464
|
-
|
|
1459
|
+
let savedState = null;
|
|
1460
|
+
let savedStatePath = null;
|
|
1461
|
+
if (!constructing) {
|
|
1462
|
+
const doc = this.$isSubdocument ? this.ownerDocument() : this;
|
|
1463
|
+
savedState = doc.$__.savedState;
|
|
1464
|
+
savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path;
|
|
1465
|
+
if (savedState != null) {
|
|
1466
|
+
const firstDot = savedStatePath.indexOf('.');
|
|
1467
|
+
const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot);
|
|
1468
|
+
if (!savedState.hasOwnProperty(topLevelPath)) {
|
|
1469
|
+
savedState[topLevelPath] = utils.clone(doc.$__getValue(topLevelPath));
|
|
1470
|
+
}
|
|
1465
1471
|
}
|
|
1466
1472
|
}
|
|
1467
1473
|
|
|
@@ -1618,7 +1624,6 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
|
|
|
1618
1624
|
|
|
1619
1625
|
const shouldModify = this.$__shouldModify(pathToMark, path, options, constructing, parts,
|
|
1620
1626
|
schema, val, priorVal);
|
|
1621
|
-
const _this = this;
|
|
1622
1627
|
|
|
1623
1628
|
if (shouldModify) {
|
|
1624
1629
|
this.markModified(pathToMark);
|
|
@@ -1634,14 +1639,6 @@ Document.prototype.$__set = function(pathToMark, path, options, constructing, pa
|
|
|
1634
1639
|
item && item.__parentArray && (item.__parentArray = val);
|
|
1635
1640
|
});
|
|
1636
1641
|
}
|
|
1637
|
-
|
|
1638
|
-
// Small hack for gh-1638: if we're overwriting the entire array, ignore
|
|
1639
|
-
// paths that were modified before the array overwrite
|
|
1640
|
-
this.$__.activePaths.forEach(function(modifiedPath) {
|
|
1641
|
-
if (modifiedPath.startsWith(path + '.')) {
|
|
1642
|
-
_this.$__.activePaths.ignore(modifiedPath);
|
|
1643
|
-
}
|
|
1644
|
-
});
|
|
1645
1642
|
}
|
|
1646
1643
|
} else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) {
|
|
1647
1644
|
val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol];
|
|
@@ -1832,7 +1829,6 @@ Document.prototype.$__path = function(path) {
|
|
|
1832
1829
|
*/
|
|
1833
1830
|
|
|
1834
1831
|
Document.prototype.markModified = function(path, scope) {
|
|
1835
|
-
// console.log('MarkModified', path, new Error().stack);
|
|
1836
1832
|
this.$__.activePaths.modify(path);
|
|
1837
1833
|
if (scope != null && !this.$isSubdocument) {
|
|
1838
1834
|
this.$__.pathsToScopes = this.$__pathsToScopes || {};
|
|
@@ -2524,9 +2520,11 @@ function _getPathsToValidate(doc) {
|
|
|
2524
2520
|
// the children as well
|
|
2525
2521
|
for (const path of paths) {
|
|
2526
2522
|
const _pathType = doc.$__schema.path(path);
|
|
2523
|
+
if (!_pathType) {
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2527
2526
|
|
|
2528
|
-
if (!_pathType ||
|
|
2529
|
-
!_pathType.$isMongooseArray ||
|
|
2527
|
+
if (!_pathType.$isMongooseArray ||
|
|
2530
2528
|
// To avoid potential performance issues, skip doc arrays whose children
|
|
2531
2529
|
// are not required. `getPositionalPathType()` may be slow, so avoid
|
|
2532
2530
|
// it unless we have a case of #6364
|
|
@@ -2538,8 +2536,7 @@ function _getPathsToValidate(doc) {
|
|
|
2538
2536
|
|
|
2539
2537
|
// gh-11380: optimization. If the array isn't a document array and there's no validators
|
|
2540
2538
|
// on the array type, there's no need to run validation on the individual array elements.
|
|
2541
|
-
if (_pathType &&
|
|
2542
|
-
_pathType.$isMongooseArray &&
|
|
2539
|
+
if (_pathType.$isMongooseArray &&
|
|
2543
2540
|
!_pathType.$isMongooseDocumentArray && // Skip document arrays...
|
|
2544
2541
|
!_pathType.$embeddedSchemaType.$isMongooseArray && // and arrays of arrays
|
|
2545
2542
|
_pathType.$embeddedSchemaType.validators.length === 0) {
|
|
@@ -3131,48 +3128,48 @@ Document.prototype.$isValid = function(path) {
|
|
|
3131
3128
|
|
|
3132
3129
|
Document.prototype.$__reset = function reset() {
|
|
3133
3130
|
let _this = this;
|
|
3134
|
-
DocumentArray || (DocumentArray = require('./types/DocumentArray'));
|
|
3135
3131
|
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3132
|
+
// Skip for subdocuments
|
|
3133
|
+
const subdocs = this.$parent() === this ? this.$getAllSubdocs() : [];
|
|
3134
|
+
const resetArrays = new Set();
|
|
3135
|
+
for (const subdoc of subdocs) {
|
|
3136
|
+
const fullPathWithIndexes = subdoc.$__fullPathWithIndexes();
|
|
3137
|
+
if (this.isModified(fullPathWithIndexes) || isParentInit(fullPathWithIndexes)) {
|
|
3138
|
+
subdoc.$__reset();
|
|
3139
|
+
if (subdoc.$isDocumentArrayElement) {
|
|
3140
|
+
if (!resetArrays.has(subdoc.parentArray())) {
|
|
3141
|
+
const array = subdoc.parentArray();
|
|
3142
|
+
// Mark path to array as init for gh-6818
|
|
3143
|
+
this.$__.activePaths.init(fullPathWithIndexes.replace(/\.\d+$/, '').slice(-subdoc.$basePath - 1));
|
|
3144
|
+
array[arrayAtomicsBackupSymbol] = array[arrayAtomicsSymbol];
|
|
3145
|
+
array[arrayAtomicsSymbol] = {};
|
|
3146
|
+
|
|
3147
|
+
resetArrays.add(array);
|
|
3148
|
+
}
|
|
3149
|
+
} else {
|
|
3150
|
+
if (subdoc.$parent() === this) {
|
|
3151
|
+
this.$__.activePaths.init(subdoc.$basePath);
|
|
3152
|
+
} else if (subdoc.$parent() != null && subdoc.$parent().$isSubdocument) {
|
|
3153
|
+
// If map path underneath subdocument, may end up with a case where
|
|
3154
|
+
// map path is modified but parent still needs to be reset. See gh-10295
|
|
3155
|
+
subdoc.$parent().$__reset();
|
|
3149
3156
|
}
|
|
3150
|
-
doc.$__reset();
|
|
3151
3157
|
}
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3152
3160
|
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
map('init', 'modify', function(i) {
|
|
3161
|
-
return _this.$__getValue(i);
|
|
3162
|
-
}).
|
|
3163
|
-
filter(function(val) {
|
|
3164
|
-
return val && !Array.isArray(val) && val.$isSingleNested;
|
|
3165
|
-
}).
|
|
3166
|
-
forEach(function(doc) {
|
|
3167
|
-
doc.$__reset();
|
|
3168
|
-
if (doc.$parent() === _this) {
|
|
3169
|
-
_this.$__.activePaths.init(doc.$basePath);
|
|
3170
|
-
} else if (doc.$parent() != null && doc.$parent().$isSubdocument) {
|
|
3171
|
-
// If map path underneath subdocument, may end up with a case where
|
|
3172
|
-
// map path is modified but parent still needs to be reset. See gh-10295
|
|
3173
|
-
doc.$parent().$__reset();
|
|
3161
|
+
function isParentInit(path) {
|
|
3162
|
+
path = path.indexOf('.') === -1 ? [path] : path.split('.');
|
|
3163
|
+
let cur = '';
|
|
3164
|
+
for (let i = 0; i < path.length; ++i) {
|
|
3165
|
+
cur += (cur.length ? '.' : '') + path[i];
|
|
3166
|
+
if (_this.$__.activePaths[cur] === 'init') {
|
|
3167
|
+
return true;
|
|
3174
3168
|
}
|
|
3175
|
-
}
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
return false;
|
|
3172
|
+
}
|
|
3176
3173
|
|
|
3177
3174
|
// clear atomics
|
|
3178
3175
|
this.$__dirty().forEach(function(dirt) {
|
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,12 @@ 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
|
+
reason.constructor.name !== 'Error') {
|
|
147
|
+
ret += ' because of "' + reason.constructor.name + '"';
|
|
148
|
+
}
|
|
143
149
|
return ret;
|
|
144
150
|
}
|
|
145
151
|
}
|
package/lib/helpers/common.js
CHANGED
|
@@ -80,14 +80,6 @@ function modifiedPaths(update, path, result) {
|
|
|
80
80
|
|
|
81
81
|
const _path = path + key;
|
|
82
82
|
result[_path] = true;
|
|
83
|
-
if (_path.indexOf('.') !== -1) {
|
|
84
|
-
const sp = _path.split('.');
|
|
85
|
-
let cur = sp[0];
|
|
86
|
-
for (let i = 1; i < sp.length; ++i) {
|
|
87
|
-
result[cur] = true;
|
|
88
|
-
cur += '.' + sp[i];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
83
|
if (!Buffer.isBuffer(val) && isMongooseObject(val)) {
|
|
92
84
|
val = val.toObject({ transform: false, virtuals: false });
|
|
93
85
|
}
|
package/lib/helpers/immediate.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
10
|
-
const nextTick = process.nextTick
|
|
10
|
+
const nextTick = typeof process !== 'undefined' && typeof process.nextTick === 'function' ?
|
|
11
|
+
process.nextTick.bind(process) :
|
|
12
|
+
cb => setTimeout(cb, 0); // Fallback for browser build
|
|
11
13
|
|
|
12
14
|
module.exports = function immediate(cb) {
|
|
13
15
|
return nextTick(cb);
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
let asyncFunctionPrototype = null;
|
|
4
|
+
// try/catch for Babel compatibility, because Babel preset-env requires
|
|
5
|
+
// regenerator-runtime for async/await and we don't want to include that
|
|
6
|
+
// for a simple check.
|
|
7
|
+
try {
|
|
8
|
+
asyncFunctionPrototype = Object.getPrototypeOf(async function() {});
|
|
9
|
+
} catch (err) {}
|
|
4
10
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
if (asyncFunctionPrototype == null) {
|
|
12
|
+
module.exports = function isAsyncFunction() {
|
|
13
|
+
return false;
|
|
14
|
+
};
|
|
15
|
+
} else {
|
|
16
|
+
module.exports = function isAsyncFunction(v) {
|
|
17
|
+
return (
|
|
18
|
+
typeof v === 'function' &&
|
|
19
|
+
Object.getPrototypeOf(v) === asyncFunctionPrototype
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -27,6 +27,12 @@ module.exports = function castBulkWrite(originalModel, op, options) {
|
|
|
27
27
|
doc.$session(options.session);
|
|
28
28
|
}
|
|
29
29
|
op['insertOne']['document'] = doc;
|
|
30
|
+
|
|
31
|
+
if (options.skipValidation || op['insertOne'].skipValidation) {
|
|
32
|
+
callback(null);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
30
36
|
op['insertOne']['document'].$validate({ __noPromise: true }, function(error) {
|
|
31
37
|
if (error) {
|
|
32
38
|
return callback(error, null);
|
|
@@ -154,6 +160,12 @@ module.exports = function castBulkWrite(originalModel, op, options) {
|
|
|
154
160
|
}
|
|
155
161
|
op['replaceOne']['replacement'] = doc;
|
|
156
162
|
|
|
163
|
+
if (options.skipValidation || op['replaceOne'].skipValidation) {
|
|
164
|
+
op['replaceOne']['replacement'] = op['replaceOne']['replacement'].toBSON();
|
|
165
|
+
callback(null);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
157
169
|
op['replaceOne']['replacement'].$validate({ __noPromise: true }, function(error) {
|
|
158
170
|
if (error) {
|
|
159
171
|
return callback(error, null);
|
|
@@ -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
|
|
|
@@ -4,7 +4,7 @@ const CastError = require('../../error/cast');
|
|
|
4
4
|
const StrictModeError = require('../../error/strict');
|
|
5
5
|
const castNumber = require('../../cast/number');
|
|
6
6
|
|
|
7
|
-
const booleanComparison = new Set(['$and', '$or'
|
|
7
|
+
const booleanComparison = new Set(['$and', '$or']);
|
|
8
8
|
const comparisonOperator = new Set(['$cmp', '$eq', '$lt', '$lte', '$gt', '$gte']);
|
|
9
9
|
const arithmeticOperatorArray = new Set([
|
|
10
10
|
// avoid casting '$add' or '$subtract', because expressions can be either number or date,
|
|
@@ -66,6 +66,7 @@ const dateOperators = new Set([
|
|
|
66
66
|
'$isoWeek',
|
|
67
67
|
'$millisecond'
|
|
68
68
|
]);
|
|
69
|
+
const expressionOperator = new Set(['$not']);
|
|
69
70
|
|
|
70
71
|
module.exports = function cast$expr(val, schema, strictQuery) {
|
|
71
72
|
if (typeof val !== 'object' || val === null) {
|
|
@@ -106,6 +107,8 @@ function _castExpression(val, schema, strictQuery) {
|
|
|
106
107
|
val[key] = castArithmetic(val[key], schema, strictQuery);
|
|
107
108
|
} else if (arithmeticOperatorNumber.has(key)) {
|
|
108
109
|
val[key] = castNumberOperator(val[key], schema, strictQuery);
|
|
110
|
+
} else if (expressionOperator.has(key)) {
|
|
111
|
+
val[key] = _castExpression(val[key], schema, strictQuery);
|
|
109
112
|
}
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -19,13 +19,16 @@ module.exports = function(schematype) {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
function createImmutableSetter(path, immutable) {
|
|
22
|
-
return function immutableSetter(v) {
|
|
22
|
+
return function immutableSetter(v, _priorVal, _doc, options) {
|
|
23
23
|
if (this == null || this.$__ == null) {
|
|
24
24
|
return v;
|
|
25
25
|
}
|
|
26
26
|
if (this.isNew) {
|
|
27
27
|
return v;
|
|
28
28
|
}
|
|
29
|
+
if (options && options.overwriteImmutable) {
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
29
32
|
|
|
30
33
|
const _immutable = typeof immutable === 'function' ?
|
|
31
34
|
immutable.call(this, this) :
|
|
@@ -48,8 +48,8 @@ module.exports = function setupTimestamps(schema, timestamps) {
|
|
|
48
48
|
currentTime() :
|
|
49
49
|
this.ownerDocument().constructor.base.now();
|
|
50
50
|
|
|
51
|
-
if (!skipCreatedAt && this.isNew && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
|
|
52
|
-
this.$set(createdAt, defaultTimestamp);
|
|
51
|
+
if (!skipCreatedAt && (this.isNew || this.$isSubdocument) && createdAt && !this.$__getValue(createdAt) && this.$__isSelected(createdAt)) {
|
|
52
|
+
this.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
if (!skipUpdatedAt && updatedAt && (this.isNew || this.$isModified())) {
|
package/lib/index.js
CHANGED
|
@@ -267,7 +267,7 @@ Mongoose.prototype.get = Mongoose.prototype.set;
|
|
|
267
267
|
* @param {Number} [options.connectTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _during initial connection_. Defaults to 30000. This option is passed transparently to [Node.js' `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback).
|
|
268
268
|
* @param {Number} [options.socketTimeoutMS=30000] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. This is set to `30000` by default, you should set this to 2-3x your longest running operation if you expect some of your database operations to run longer than 20 seconds. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes.
|
|
269
269
|
* @param {Number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both.
|
|
270
|
-
* @return {Connection} the created Connection object. Connections are thenable, so you can do `await mongoose.createConnection()
|
|
270
|
+
* @return {Connection} the created Connection object. Connections are not thenable, so you can't do `await mongoose.createConnection()`. To await use mongoose.createConnection(uri).asPromise() instead.
|
|
271
271
|
* @api public
|
|
272
272
|
*/
|
|
273
273
|
|
package/lib/internal.js
CHANGED
package/lib/model.js
CHANGED
|
@@ -1067,13 +1067,33 @@ Model.prototype.$__deleteOne = Model.prototype.$__remove;
|
|
|
1067
1067
|
* doc.model('User').findById(id, callback);
|
|
1068
1068
|
*
|
|
1069
1069
|
* @param {String} name model name
|
|
1070
|
+
* @method model
|
|
1070
1071
|
* @api public
|
|
1072
|
+
* @return {Model}
|
|
1071
1073
|
*/
|
|
1072
1074
|
|
|
1073
1075
|
Model.prototype.model = function model(name) {
|
|
1074
1076
|
return this[modelDbSymbol].model(name);
|
|
1075
1077
|
};
|
|
1076
1078
|
|
|
1079
|
+
/**
|
|
1080
|
+
* Returns another Model instance.
|
|
1081
|
+
*
|
|
1082
|
+
* #### Example:
|
|
1083
|
+
*
|
|
1084
|
+
* const doc = new Tank;
|
|
1085
|
+
* doc.model('User').findById(id, callback);
|
|
1086
|
+
*
|
|
1087
|
+
* @param {String} name model name
|
|
1088
|
+
* @method $model
|
|
1089
|
+
* @api public
|
|
1090
|
+
* @return {Model}
|
|
1091
|
+
*/
|
|
1092
|
+
|
|
1093
|
+
Model.prototype.$model = function $model(name) {
|
|
1094
|
+
return this[modelDbSymbol].model(name);
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1077
1097
|
/**
|
|
1078
1098
|
* Returns a document with `_id` only if at least one document exists in the database that matches
|
|
1079
1099
|
* the given `filter`, and `null` otherwise.
|
|
@@ -1362,23 +1382,27 @@ Model.createCollection = function createCollection(options, callback) {
|
|
|
1362
1382
|
}
|
|
1363
1383
|
|
|
1364
1384
|
const schemaCollation = this &&
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1385
|
+
this.schema &&
|
|
1386
|
+
this.schema.options &&
|
|
1387
|
+
this.schema.options.collation;
|
|
1368
1388
|
if (schemaCollation != null) {
|
|
1369
1389
|
options = Object.assign({ collation: schemaCollation }, options);
|
|
1370
1390
|
}
|
|
1371
1391
|
const capped = this &&
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
if (capped) {
|
|
1376
|
-
|
|
1392
|
+
this.schema &&
|
|
1393
|
+
this.schema.options &&
|
|
1394
|
+
this.schema.options.capped;
|
|
1395
|
+
if (capped != null) {
|
|
1396
|
+
if (typeof capped === 'number') {
|
|
1397
|
+
options = Object.assign({ capped: true, size: capped }, options);
|
|
1398
|
+
} else if (typeof capped === 'object') {
|
|
1399
|
+
options = Object.assign({ capped: true }, capped, options);
|
|
1400
|
+
}
|
|
1377
1401
|
}
|
|
1378
1402
|
const timeseries = this &&
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1403
|
+
this.schema &&
|
|
1404
|
+
this.schema.options &&
|
|
1405
|
+
this.schema.options.timeseries;
|
|
1382
1406
|
if (timeseries != null) {
|
|
1383
1407
|
options = Object.assign({ timeseries }, options);
|
|
1384
1408
|
if (options.expireAfterSeconds != null) {
|
|
@@ -2148,14 +2172,7 @@ Model.find = function find(conditions, projection, options, callback) {
|
|
|
2148
2172
|
|
|
2149
2173
|
const mq = new this.Query({}, {}, this, this.$__collection);
|
|
2150
2174
|
mq.select(projection);
|
|
2151
|
-
|
|
2152
2175
|
mq.setOptions(options);
|
|
2153
|
-
if (this.schema.discriminatorMapping &&
|
|
2154
|
-
this.schema.discriminatorMapping.isRoot &&
|
|
2155
|
-
mq.selectedInclusively()) {
|
|
2156
|
-
// Need to select discriminator key because original schema doesn't have it
|
|
2157
|
-
mq.select(this.schema.options.discriminatorKey);
|
|
2158
|
-
}
|
|
2159
2176
|
|
|
2160
2177
|
callback = this.$handleCallbackError(callback);
|
|
2161
2178
|
|
|
@@ -2261,11 +2278,6 @@ Model.findOne = function findOne(conditions, projection, options, callback) {
|
|
|
2261
2278
|
const mq = new this.Query({}, {}, this, this.$__collection);
|
|
2262
2279
|
mq.select(projection);
|
|
2263
2280
|
mq.setOptions(options);
|
|
2264
|
-
if (this.schema.discriminatorMapping &&
|
|
2265
|
-
this.schema.discriminatorMapping.isRoot &&
|
|
2266
|
-
mq.selectedInclusively()) {
|
|
2267
|
-
mq.select(this.schema.options.discriminatorKey);
|
|
2268
|
-
}
|
|
2269
2281
|
|
|
2270
2282
|
callback = this.$handleCallbackError(callback);
|
|
2271
2283
|
return mq.findOne(conditions, callback);
|
|
@@ -3488,6 +3500,7 @@ function _setIsNew(doc, val) {
|
|
|
3488
3500
|
const subdocs = doc.$getAllSubdocs();
|
|
3489
3501
|
for (const subdoc of subdocs) {
|
|
3490
3502
|
subdoc.$isNew = val;
|
|
3503
|
+
subdoc.$emit('isNew', val);
|
|
3491
3504
|
}
|
|
3492
3505
|
}
|
|
3493
3506
|
|
|
@@ -3568,6 +3581,7 @@ function _setIsNew(doc, val) {
|
|
|
3568
3581
|
* @param {String|number} [options.w=1] The [write concern](https://docs.mongodb.com/manual/reference/write-concern/). See [`Query#w()`](/docs/api.html#query_Query-w) for more information.
|
|
3569
3582
|
* @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
|
|
3570
3583
|
* @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
|
|
3584
|
+
* @param {Boolean} [options.skipValidation=false] Set to true to skip Mongoose schema validation on bulk write operations. Mongoose currently runs validation on `insertOne` and `replaceOne` operations by default.
|
|
3571
3585
|
* @param {Boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://docs.mongodb.com/manual/core/schema-validation/) for all writes in this bulk.
|
|
3572
3586
|
* @param {Boolean} [options.strict=null] Overwrites the [`strict` option](/docs/guide.html#strict) on schema. If false, allows filtering and writing fields not defined in the schema for all writes in this bulk.
|
|
3573
3587
|
* @param {Function} [callback] callback `function(error, bulkWriteOpResult) {}`
|
|
@@ -4246,7 +4260,7 @@ Model.aggregate = function aggregate(pipeline, options, callback) {
|
|
|
4246
4260
|
* }
|
|
4247
4261
|
*
|
|
4248
4262
|
* @param {Object} obj
|
|
4249
|
-
* @param {Array} pathsToValidate
|
|
4263
|
+
* @param {Array|String} pathsToValidate
|
|
4250
4264
|
* @param {Object} [context]
|
|
4251
4265
|
* @param {Function} [callback]
|
|
4252
4266
|
* @return {Promise|undefined}
|
|
@@ -4265,7 +4279,7 @@ Model.validate = function validate(obj, pathsToValidate, context, callback) {
|
|
|
4265
4279
|
let paths = Object.keys(schema.paths);
|
|
4266
4280
|
|
|
4267
4281
|
if (pathsToValidate != null) {
|
|
4268
|
-
const _pathsToValidate = new Set(pathsToValidate);
|
|
4282
|
+
const _pathsToValidate = typeof pathsToValidate === 'string' ? new Set(pathsToValidate.split(' ')) : new Set(pathsToValidate);
|
|
4269
4283
|
paths = paths.filter(p => {
|
|
4270
4284
|
const pieces = p.split('.');
|
|
4271
4285
|
let cur = pieces[0];
|
|
@@ -4561,9 +4575,9 @@ function populate(model, docs, options, callback) {
|
|
|
4561
4575
|
}
|
|
4562
4576
|
// If no models to populate but we have a nested populate,
|
|
4563
4577
|
// keep trying, re: gh-8946
|
|
4564
|
-
if (
|
|
4565
|
-
const opts = utils.populate(
|
|
4566
|
-
path:
|
|
4578
|
+
if (populateOptions.populate != null) {
|
|
4579
|
+
const opts = utils.populate(populateOptions.populate).map(pop => Object.assign({}, pop, {
|
|
4580
|
+
path: populateOptions.path + '.' + pop.path
|
|
4567
4581
|
}));
|
|
4568
4582
|
return model.populate(docs, opts, callback);
|
|
4569
4583
|
}
|
package/lib/query.js
CHANGED
|
@@ -53,6 +53,7 @@ const queryOptionMethods = new Set([
|
|
|
53
53
|
'maxScan',
|
|
54
54
|
'maxTimeMS',
|
|
55
55
|
'maxscan',
|
|
56
|
+
'populate',
|
|
56
57
|
'projection',
|
|
57
58
|
'read',
|
|
58
59
|
'select',
|
|
@@ -4609,7 +4610,10 @@ Query.prototype.transform = function(fn) {
|
|
|
4609
4610
|
* // Throws if no doc returned
|
|
4610
4611
|
* await Model.findOne({ foo: 'bar' }).orFail();
|
|
4611
4612
|
*
|
|
4612
|
-
* // Throws if no document was updated
|
|
4613
|
+
* // Throws if no document was updated. Note that `orFail()` will still
|
|
4614
|
+
* // throw if the only document that matches is `{ foo: 'bar', name: 'test' }`,
|
|
4615
|
+
* // because `orFail()` will throw if no document was _updated_, not
|
|
4616
|
+
* // if no document was _found_.
|
|
4613
4617
|
* await Model.updateOne({ foo: 'bar' }, { name: 'test' }).orFail();
|
|
4614
4618
|
*
|
|
4615
4619
|
* // Throws "No docs found!" error if no docs match `{ foo: 'bar' }`
|
|
@@ -4976,7 +4980,7 @@ function castQuery(query) {
|
|
|
4976
4980
|
* a response for each query has also been returned, the results are passed to
|
|
4977
4981
|
* the callback.
|
|
4978
4982
|
*
|
|
4979
|
-
* @param {Object|String} path either the path to populate or an object specifying all parameters
|
|
4983
|
+
* @param {Object|String|Array<String>} path either the path(s) to populate or an object specifying all parameters
|
|
4980
4984
|
* @param {Object|String} [select] Field selection for the population query
|
|
4981
4985
|
* @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
|
|
4982
4986
|
* @param {Object} [match] Conditions for the population query
|
|
@@ -5757,4 +5761,4 @@ Query.prototype.model;
|
|
|
5757
5761
|
* Export
|
|
5758
5762
|
*/
|
|
5759
5763
|
|
|
5760
|
-
module.exports = Query;
|
|
5764
|
+
module.exports = Query;
|