mongoose 6.1.7 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/dist/browser.umd.js +187 -193
- package/index.d.ts +96 -44
- package/lib/aggregate.js +6 -7
- package/lib/cast/objectid.js +1 -2
- package/lib/cast.js +21 -16
- package/lib/connection.js +37 -3
- package/lib/document.js +4 -4
- package/lib/drivers/node-mongodb-native/collection.js +1 -2
- package/lib/error/index.js +11 -0
- package/lib/error/syncIndexes.js +30 -0
- package/lib/helpers/document/compile.js +6 -2
- package/lib/helpers/model/discriminator.js +2 -1
- package/lib/helpers/path/setDottedPath.js +7 -3
- package/lib/helpers/populate/getModelsMapForPopulate.js +2 -2
- package/lib/helpers/populate/modelNamesFromRefPath.js +2 -2
- package/lib/helpers/query/cast$expr.js +284 -0
- package/lib/helpers/schema/applyPlugins.js +11 -0
- package/lib/helpers/setDefaultsOnInsert.js +16 -7
- package/lib/helpers/update/applyTimestampsToChildren.js +2 -2
- package/lib/helpers/update/castArrayFilters.js +1 -1
- package/lib/helpers/update/updatedPathsByArrayFilter.js +1 -1
- package/lib/index.js +14 -40
- package/lib/model.js +18 -13
- package/lib/query.js +16 -16
- package/lib/schema/SubdocumentPath.js +1 -1
- package/lib/schema/array.js +1 -1
- package/lib/schema/documentarray.js +1 -1
- package/lib/schema.js +26 -21
- package/lib/schematype.js +46 -1
- package/lib/statemachine.js +4 -5
- package/lib/utils.js +8 -26
- package/package.json +17 -14
- package/tools/repl.js +21 -23
|
@@ -9,6 +9,8 @@ let Document;
|
|
|
9
9
|
const getSymbol = require('../../helpers/symbols').getSymbol;
|
|
10
10
|
const scopeSymbol = require('../../helpers/symbols').scopeSymbol;
|
|
11
11
|
|
|
12
|
+
const isPOJO = utils.isPOJO;
|
|
13
|
+
|
|
12
14
|
/*!
|
|
13
15
|
* exports
|
|
14
16
|
*/
|
|
@@ -22,12 +24,14 @@ exports.defineKey = defineKey;
|
|
|
22
24
|
|
|
23
25
|
function compile(tree, proto, prefix, options) {
|
|
24
26
|
Document = Document || require('../../document');
|
|
27
|
+
const typeKey = options.typeKey;
|
|
25
28
|
|
|
26
29
|
for (const key of Object.keys(tree)) {
|
|
27
30
|
const limb = tree[key];
|
|
28
31
|
|
|
29
|
-
const hasSubprops =
|
|
30
|
-
(
|
|
32
|
+
const hasSubprops = isPOJO(limb) &&
|
|
33
|
+
Object.keys(limb).length > 0 &&
|
|
34
|
+
(!limb[typeKey] || (typeKey === 'type' && isPOJO(limb.type) && limb.type.type));
|
|
31
35
|
const subprops = hasSubprops ? limb : null;
|
|
32
36
|
|
|
33
37
|
defineKey({ prop: key, subprops: subprops, prototype: proto, prefix: prefix, options: options });
|
|
@@ -17,6 +17,7 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
|
|
20
|
+
|
|
20
21
|
if (!(schema && schema.instanceOfSchema)) {
|
|
21
22
|
throw new Error('You must pass a valid discriminator Schema');
|
|
22
23
|
}
|
|
@@ -201,7 +202,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
|
|
|
201
202
|
|
|
202
203
|
model.schema.discriminators[name] = schema;
|
|
203
204
|
|
|
204
|
-
if (model.discriminators[name]) {
|
|
205
|
+
if (model.discriminators[name] && !schema.options.overwriteModels) {
|
|
205
206
|
throw new Error('Discriminator with name "' + name + '" already exists');
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
module.exports = function setDottedPath(obj, path, val) {
|
|
4
|
-
|
|
4
|
+
if (path.indexOf('.') === -1) {
|
|
5
|
+
obj[path] = val;
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const parts = path.split('.');
|
|
9
|
+
const last = parts.pop();
|
|
5
10
|
let cur = obj;
|
|
6
|
-
for (const part of parts
|
|
11
|
+
for (const part of parts) {
|
|
7
12
|
if (cur[part] == null) {
|
|
8
13
|
cur[part] = {};
|
|
9
14
|
}
|
|
@@ -11,6 +16,5 @@ module.exports = function setDottedPath(obj, path, val) {
|
|
|
11
16
|
cur = cur[part];
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
const last = parts[parts.length - 1];
|
|
15
19
|
cur[last] = val;
|
|
16
20
|
};
|
|
@@ -696,7 +696,7 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz
|
|
|
696
696
|
schematype.caster.discriminators != null &&
|
|
697
697
|
Object.keys(schematype.caster.discriminators).length > 0) {
|
|
698
698
|
const subdocs = utils.getValue(cur, doc);
|
|
699
|
-
const remnant = options.path.
|
|
699
|
+
const remnant = options.path.substring(cur.length + 1);
|
|
700
700
|
const discriminatorKey = schematype.caster.schema.options.discriminatorKey;
|
|
701
701
|
modelNames = [];
|
|
702
702
|
for (const subdoc of subdocs) {
|
|
@@ -708,7 +708,7 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz
|
|
|
708
708
|
}
|
|
709
709
|
const _path = discriminatorSchema.path(remnant);
|
|
710
710
|
if (_path == null || _path.options.refPath == null) {
|
|
711
|
-
const docValue = utils.getValue(data.localField.
|
|
711
|
+
const docValue = utils.getValue(data.localField.substring(cur.length + 1), subdoc);
|
|
712
712
|
ret.forEach((v, i) => {
|
|
713
713
|
if (v === docValue) {
|
|
714
714
|
ret[i] = SkipPopulateValue(v);
|
|
@@ -35,8 +35,8 @@ module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, mod
|
|
|
35
35
|
for (let i = 0; i < chunks.length; i += 2) {
|
|
36
36
|
const chunk = chunks[i];
|
|
37
37
|
if (_remaining.startsWith(chunk + '.')) {
|
|
38
|
-
_refPath += _remaining.
|
|
39
|
-
_remaining = _remaining.
|
|
38
|
+
_refPath += _remaining.substring(0, chunk.length) + chunks[i + 1];
|
|
39
|
+
_remaining = _remaining.substring(chunk.length + 1);
|
|
40
40
|
} else if (i === chunks.length - 1) {
|
|
41
41
|
_refPath += _remaining;
|
|
42
42
|
_remaining = '';
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CastError = require('../../error/cast');
|
|
4
|
+
const StrictModeError = require('../../error/strict');
|
|
5
|
+
const castNumber = require('../../cast/number');
|
|
6
|
+
|
|
7
|
+
const booleanComparison = new Set(['$and', '$or', '$not']);
|
|
8
|
+
const comparisonOperator = new Set(['$cmp', '$eq', '$lt', '$lte', '$gt', '$gte']);
|
|
9
|
+
const arithmeticOperatorArray = new Set([
|
|
10
|
+
// avoid casting '$add' or '$subtract', because expressions can be either number or date,
|
|
11
|
+
// and we don't have a good way of inferring which arguments should be numbers and which should
|
|
12
|
+
// be dates.
|
|
13
|
+
'$multiply',
|
|
14
|
+
'$divide',
|
|
15
|
+
'$log',
|
|
16
|
+
'$mod',
|
|
17
|
+
'$trunc',
|
|
18
|
+
'$avg',
|
|
19
|
+
'$max',
|
|
20
|
+
'$min',
|
|
21
|
+
'$stdDevPop',
|
|
22
|
+
'$stdDevSamp',
|
|
23
|
+
'$sum'
|
|
24
|
+
]);
|
|
25
|
+
const arithmeticOperatorNumber = new Set([
|
|
26
|
+
'$abs',
|
|
27
|
+
'$exp',
|
|
28
|
+
'$ceil',
|
|
29
|
+
'$floor',
|
|
30
|
+
'$ln',
|
|
31
|
+
'$log10',
|
|
32
|
+
'$round',
|
|
33
|
+
'$sqrt',
|
|
34
|
+
'$sin',
|
|
35
|
+
'$cos',
|
|
36
|
+
'$tan',
|
|
37
|
+
'$asin',
|
|
38
|
+
'$acos',
|
|
39
|
+
'$atan',
|
|
40
|
+
'$atan2',
|
|
41
|
+
'$asinh',
|
|
42
|
+
'$acosh',
|
|
43
|
+
'$atanh',
|
|
44
|
+
'$sinh',
|
|
45
|
+
'$cosh',
|
|
46
|
+
'$tanh',
|
|
47
|
+
'$degreesToRadians',
|
|
48
|
+
'$radiansToDegrees'
|
|
49
|
+
]);
|
|
50
|
+
const arrayElementOperators = new Set([
|
|
51
|
+
'$arrayElemAt',
|
|
52
|
+
'$first',
|
|
53
|
+
'$last'
|
|
54
|
+
]);
|
|
55
|
+
const dateOperators = new Set([
|
|
56
|
+
'$year',
|
|
57
|
+
'$month',
|
|
58
|
+
'$week',
|
|
59
|
+
'$dayOfMonth',
|
|
60
|
+
'$dayOfYear',
|
|
61
|
+
'$hour',
|
|
62
|
+
'$minute',
|
|
63
|
+
'$second',
|
|
64
|
+
'$isoDayOfWeek',
|
|
65
|
+
'$isoWeekYear',
|
|
66
|
+
'$isoWeek',
|
|
67
|
+
'$millisecond'
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
module.exports = function cast$expr(val, schema, strictQuery) {
|
|
71
|
+
if (typeof val !== 'object' || val == null) {
|
|
72
|
+
throw new Error('`$expr` must be an object');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return _castExpression(val, schema, strictQuery);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
function _castExpression(val, schema, strictQuery) {
|
|
79
|
+
if (isPath(val)) {
|
|
80
|
+
// Assume path
|
|
81
|
+
return val;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (val.$cond != null) {
|
|
85
|
+
if (Array.isArray(val.$cond)) {
|
|
86
|
+
val.$cond = val.$cond.map(expr => _castExpression(expr, schema, strictQuery));
|
|
87
|
+
} else {
|
|
88
|
+
val.$cond.if = _castExpression(val.$cond.if, schema, strictQuery);
|
|
89
|
+
val.$cond.then = _castExpression(val.$cond.then, schema, strictQuery);
|
|
90
|
+
val.$cond.else = _castExpression(val.$cond.else, schema, strictQuery);
|
|
91
|
+
}
|
|
92
|
+
} else if (val.$ifNull != null) {
|
|
93
|
+
val.$ifNull.map(v => _castExpression(v, schema, strictQuery));
|
|
94
|
+
} else if (val.$switch != null) {
|
|
95
|
+
val.branches.map(v => _castExpression(v, schema, strictQuery));
|
|
96
|
+
val.default = _castExpression(val.default, schema, strictQuery);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const keys = Object.keys(val);
|
|
100
|
+
for (const key of keys) {
|
|
101
|
+
if (booleanComparison.has(key)) {
|
|
102
|
+
val[key] = val[key].map(v => _castExpression(v, schema, strictQuery));
|
|
103
|
+
} else if (comparisonOperator.has(key)) {
|
|
104
|
+
val[key] = castComparison(val[key], schema, strictQuery);
|
|
105
|
+
} else if (arithmeticOperatorArray.has(key)) {
|
|
106
|
+
val[key] = castArithmetic(val[key], schema, strictQuery);
|
|
107
|
+
} else if (arithmeticOperatorNumber.has(key)) {
|
|
108
|
+
val[key] = castNumberOperator(val[key], schema, strictQuery);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (val.$in) {
|
|
113
|
+
val.$in = castIn(val.$in, schema, strictQuery);
|
|
114
|
+
}
|
|
115
|
+
if (val.$size) {
|
|
116
|
+
val.$size = castNumberOperator(val.$size, schema, strictQuery);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_omitUndefined(val);
|
|
120
|
+
|
|
121
|
+
return val;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _omitUndefined(val) {
|
|
125
|
+
const keys = Object.keys(val);
|
|
126
|
+
for (const key of keys) {
|
|
127
|
+
if (val[key] === void 0) {
|
|
128
|
+
delete val[key];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// { $op: <number> }
|
|
134
|
+
function castNumberOperator(val) {
|
|
135
|
+
if (!isLiteral(val)) {
|
|
136
|
+
return val;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
return castNumber(val);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
throw new CastError('Number', val);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function castIn(val, schema, strictQuery) {
|
|
147
|
+
let search = val[0];
|
|
148
|
+
let path = val[1];
|
|
149
|
+
if (!isPath(path)) {
|
|
150
|
+
return val;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
path = path.slice(1);
|
|
154
|
+
const schematype = schema.path(path);
|
|
155
|
+
if (schematype == null) {
|
|
156
|
+
if (strictQuery === false) {
|
|
157
|
+
return val;
|
|
158
|
+
} else if (strictQuery === 'throw') {
|
|
159
|
+
throw new StrictModeError('$in');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!schematype.$isMongooseArray) {
|
|
166
|
+
throw new Error('Path must be an array for $in');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (schematype.$isMongooseDocumentArray) {
|
|
170
|
+
search = schematype.$embeddedSchemaType.cast(search);
|
|
171
|
+
} else {
|
|
172
|
+
search = schematype.caster.cast(search);
|
|
173
|
+
}
|
|
174
|
+
return [search, val[1]];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// { $op: [<number>, <number>] }
|
|
178
|
+
function castArithmetic(val) {
|
|
179
|
+
if (!Array.isArray(val)) {
|
|
180
|
+
if (!isLiteral(val)) {
|
|
181
|
+
return val;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
return castNumber(val);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
throw new CastError('Number', val);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return val.map(v => {
|
|
191
|
+
if (!isLiteral(v)) {
|
|
192
|
+
return v;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
return castNumber(v);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
throw new CastError('Number', v);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// { $op: [expression, expression] }
|
|
203
|
+
function castComparison(val, schema, strictQuery) {
|
|
204
|
+
if (!Array.isArray(val) || val.length !== 2) {
|
|
205
|
+
throw new Error('Comparison operator must be an array of length 2');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
val[0] = _castExpression(val[0], schema, strictQuery);
|
|
209
|
+
const lhs = val[0];
|
|
210
|
+
|
|
211
|
+
if (isLiteral(val[1])) {
|
|
212
|
+
let path = null;
|
|
213
|
+
let schematype = null;
|
|
214
|
+
let caster = null;
|
|
215
|
+
if (isPath(lhs)) {
|
|
216
|
+
path = lhs.slice(1);
|
|
217
|
+
schematype = schema.path(path);
|
|
218
|
+
} else if (typeof lhs === 'object' && lhs != null) {
|
|
219
|
+
for (const key of Object.keys(lhs)) {
|
|
220
|
+
if (dateOperators.has(key) && isPath(lhs[key])) {
|
|
221
|
+
path = lhs[key].slice(1) + '.' + key;
|
|
222
|
+
caster = castNumber;
|
|
223
|
+
} else if (arrayElementOperators.has(key) && isPath(lhs[key])) {
|
|
224
|
+
path = lhs[key].slice(1) + '.' + key;
|
|
225
|
+
schematype = schema.path(lhs[key].slice(1));
|
|
226
|
+
if (schematype != null) {
|
|
227
|
+
if (schematype.$isMongooseDocumentArray) {
|
|
228
|
+
schematype = schematype.$embeddedSchemaType;
|
|
229
|
+
} else if (schematype.$isMongooseArray) {
|
|
230
|
+
schematype = schematype.caster;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const is$literal = typeof val[1] === 'object' && val[1] != null && val[1].$literal != null;
|
|
238
|
+
if (schematype != null) {
|
|
239
|
+
if (is$literal) {
|
|
240
|
+
val[1] = { $literal: schematype.cast(val[1].$literal) };
|
|
241
|
+
} else {
|
|
242
|
+
val[1] = schematype.cast(val[1]);
|
|
243
|
+
}
|
|
244
|
+
} else if (caster != null) {
|
|
245
|
+
if (is$literal) {
|
|
246
|
+
try {
|
|
247
|
+
val[1] = { $literal: caster(val[1].$literal) };
|
|
248
|
+
} catch (err) {
|
|
249
|
+
throw new CastError(caster.name.replace(/^cast/, ''), val[1], path + '.$literal');
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
try {
|
|
253
|
+
val[1] = caster(val[1]);
|
|
254
|
+
} catch (err) {
|
|
255
|
+
throw new CastError(caster.name.replace(/^cast/, ''), val[1], path);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} else if (path != null && strictQuery === true) {
|
|
259
|
+
return void 0;
|
|
260
|
+
} else if (path != null && strictQuery === 'throw') {
|
|
261
|
+
throw new StrictModeError(path);
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
val[1] = _castExpression(val[1]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return val;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function isPath(val) {
|
|
271
|
+
return typeof val === 'string' && val.startsWith('$');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function isLiteral(val) {
|
|
275
|
+
if (typeof val === 'string' && val.startsWith('$')) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (typeof val === 'object' && val != null && Object.keys(val).find(key => key.startsWith('$'))) {
|
|
279
|
+
// The `$literal` expression can make an object a literal
|
|
280
|
+
// https://docs.mongodb.com/manual/reference/operator/aggregation/literal/#mongodb-expression-exp.-literal
|
|
281
|
+
return val.$literal != null;
|
|
282
|
+
}
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
@@ -7,7 +7,18 @@ module.exports = function applyPlugins(schema, plugins, options, cacheKey) {
|
|
|
7
7
|
schema[cacheKey] = true;
|
|
8
8
|
|
|
9
9
|
if (!options || !options.skipTopLevel) {
|
|
10
|
+
let pluginTags = null;
|
|
10
11
|
for (const plugin of plugins) {
|
|
12
|
+
const tags = plugin[1] == null ? null : plugin[1].tags;
|
|
13
|
+
if (!Array.isArray(tags)) {
|
|
14
|
+
schema.plugin(plugin[0], plugin[1]);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
pluginTags = pluginTags || new Set(schema.options.pluginTags || []);
|
|
19
|
+
if (!tags.find(tag => pluginTags.has(tag))) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
11
22
|
schema.plugin(plugin[0], plugin[1]);
|
|
12
23
|
}
|
|
13
24
|
}
|
|
@@ -79,14 +79,23 @@ module.exports = function(filter, schema, castedDoc, options) {
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
const def = schemaType.getDefault(null, true);
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
if (isModified(modified, path)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof def === 'undefined') {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (schemaType.splitPath().includes('$*')) {
|
|
89
|
+
// Skip defaults underneath maps. We should never do `$setOnInsert` on a path with `$*`
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
castedDoc = castedDoc || {};
|
|
94
|
+
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
|
95
|
+
if (get(castedDoc, path) == null) {
|
|
96
|
+
castedDoc.$setOnInsert[path] = def;
|
|
89
97
|
}
|
|
98
|
+
updatedValues[path] = def;
|
|
90
99
|
});
|
|
91
100
|
|
|
92
101
|
return castedDoc;
|
|
@@ -153,7 +153,7 @@ function applyTimestampsToUpdateKey(schema, key, update, now) {
|
|
|
153
153
|
// Single nested is easy
|
|
154
154
|
update[parentPath + '.' + updatedAt] = now;
|
|
155
155
|
} else if (parentSchemaType.$isMongooseDocumentArray) {
|
|
156
|
-
let childPath = key.
|
|
156
|
+
let childPath = key.substring(parentPath.length + 1);
|
|
157
157
|
|
|
158
158
|
if (/^\d+$/.test(childPath)) {
|
|
159
159
|
update[parentPath + '.' + childPath][updatedAt] = now;
|
|
@@ -161,7 +161,7 @@ function applyTimestampsToUpdateKey(schema, key, update, now) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
const firstDot = childPath.indexOf('.');
|
|
164
|
-
childPath = firstDot !== -1 ? childPath.
|
|
164
|
+
childPath = firstDot !== -1 ? childPath.substring(0, firstDot) : childPath;
|
|
165
165
|
|
|
166
166
|
update[parentPath + '.' + childPath + '.' + updatedAt] = now;
|
|
167
167
|
}
|
|
@@ -51,7 +51,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
|
|
|
51
51
|
const dot = key.indexOf('.');
|
|
52
52
|
let filterPath = dot === -1 ?
|
|
53
53
|
updatedPathsByFilter[key] + '.0' :
|
|
54
|
-
updatedPathsByFilter[key.
|
|
54
|
+
updatedPathsByFilter[key.substring(0, dot)] + '.0' + key.substring(dot);
|
|
55
55
|
if (filterPath == null) {
|
|
56
56
|
throw new Error(`Filter path not found for ${key}`);
|
|
57
57
|
}
|
|
@@ -19,7 +19,7 @@ module.exports = function updatedPathsByArrayFilter(update) {
|
|
|
19
19
|
throw new Error(`Path '${path}' contains the same array filter multiple times`);
|
|
20
20
|
}
|
|
21
21
|
cur[match.substring(2, match.length - 1)] = path.
|
|
22
|
-
|
|
22
|
+
substring(0, firstMatch - 1).
|
|
23
23
|
replace(/\$\[[^\]]+\]/g, '0');
|
|
24
24
|
}
|
|
25
25
|
return cur;
|
package/lib/index.js
CHANGED
|
@@ -947,55 +947,29 @@ Mongoose.prototype.ObjectId = SchemaTypes.ObjectId;
|
|
|
947
947
|
*
|
|
948
948
|
* mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
|
|
949
949
|
* mongoose.isValidObjectId('0123456789ab'); // true
|
|
950
|
-
* mongoose.isValidObjectId(6); //
|
|
950
|
+
* mongoose.isValidObjectId(6); // true
|
|
951
|
+
* mongoose.isValidObjectId({ test: 42 }); // false
|
|
951
952
|
*
|
|
952
953
|
* @method isValidObjectId
|
|
953
954
|
* @api public
|
|
954
955
|
*/
|
|
955
956
|
|
|
956
957
|
Mongoose.prototype.isValidObjectId = function(v) {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
}
|
|
960
|
-
const base = this || mongoose;
|
|
961
|
-
const ObjectId = base.driver.get().ObjectId;
|
|
962
|
-
if (v instanceof ObjectId) {
|
|
963
|
-
return true;
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
if (v._id != null) {
|
|
967
|
-
if (v._id instanceof ObjectId) {
|
|
968
|
-
return true;
|
|
969
|
-
}
|
|
970
|
-
if (v._id.toString instanceof Function) {
|
|
971
|
-
v = v._id.toString();
|
|
972
|
-
if (typeof v === 'string' && v.length === 12) {
|
|
973
|
-
return true;
|
|
974
|
-
}
|
|
975
|
-
if (typeof v === 'string' && /^[0-9A-Fa-f]{24}$/.test(v)) {
|
|
976
|
-
return true;
|
|
977
|
-
}
|
|
978
|
-
return false;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
if (v.toString instanceof Function) {
|
|
983
|
-
v = v.toString();
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
if (typeof v === 'string' && v.length === 12) {
|
|
987
|
-
return true;
|
|
988
|
-
}
|
|
989
|
-
if (typeof v === 'string' && /^[0-9A-Fa-f]{24}$/.test(v)) {
|
|
990
|
-
return true;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
return false;
|
|
958
|
+
const _mongoose = this instanceof Mongoose ? this : mongoose;
|
|
959
|
+
return _mongoose.Types.ObjectId.isValid(v);
|
|
994
960
|
};
|
|
995
961
|
|
|
996
|
-
|
|
962
|
+
/**
|
|
963
|
+
*
|
|
964
|
+
* Syncs all the indexes for the models registered with this connection.
|
|
965
|
+
*
|
|
966
|
+
* @param {Object} options
|
|
967
|
+
* @param {Boolean} options.continueOnError `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model.
|
|
968
|
+
* @returns
|
|
969
|
+
*/
|
|
970
|
+
Mongoose.prototype.syncIndexes = function(options) {
|
|
997
971
|
const _mongoose = this instanceof Mongoose ? this : mongoose;
|
|
998
|
-
return _mongoose.connection.syncIndexes();
|
|
972
|
+
return _mongoose.connection.syncIndexes(options);
|
|
999
973
|
};
|
|
1000
974
|
|
|
1001
975
|
/**
|
package/lib/model.js
CHANGED
|
@@ -48,6 +48,7 @@ const modifiedPaths = require('./helpers/update/modifiedPaths');
|
|
|
48
48
|
const parallelLimit = require('./helpers/parallelLimit');
|
|
49
49
|
const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline');
|
|
50
50
|
const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
|
|
51
|
+
const setDottedPath = require('./helpers/path/setDottedPath');
|
|
51
52
|
const util = require('util');
|
|
52
53
|
const utils = require('./utils');
|
|
53
54
|
|
|
@@ -832,7 +833,8 @@ Model.prototype.$__version = function(where, delta) {
|
|
|
832
833
|
if (where === true) {
|
|
833
834
|
// this is an insert
|
|
834
835
|
if (key) {
|
|
835
|
-
|
|
836
|
+
setDottedPath(delta, key, 0);
|
|
837
|
+
this.$__setValue(key, 0);
|
|
836
838
|
}
|
|
837
839
|
return;
|
|
838
840
|
}
|
|
@@ -1059,8 +1061,8 @@ Model.prototype.model = function model(name) {
|
|
|
1059
1061
|
};
|
|
1060
1062
|
|
|
1061
1063
|
/**
|
|
1062
|
-
* Returns
|
|
1063
|
-
* the given `filter`, and
|
|
1064
|
+
* Returns a document with `_id` only if at least one document exists in the database that matches
|
|
1065
|
+
* the given `filter`, and `null` otherwise.
|
|
1064
1066
|
*
|
|
1065
1067
|
* Under the hood, `MyModel.exists({ answer: 42 })` is equivalent to
|
|
1066
1068
|
* `MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()`
|
|
@@ -1097,10 +1099,6 @@ Model.exists = function exists(filter, options, callback) {
|
|
|
1097
1099
|
if (typeof callback === 'function') {
|
|
1098
1100
|
return query.exec(callback);
|
|
1099
1101
|
}
|
|
1100
|
-
options = options || {};
|
|
1101
|
-
if (!options.explain) {
|
|
1102
|
-
return query.then(doc => !!doc);
|
|
1103
|
-
}
|
|
1104
1102
|
|
|
1105
1103
|
return query;
|
|
1106
1104
|
};
|
|
@@ -1136,11 +1134,13 @@ Model.exists = function exists(filter, options, callback) {
|
|
|
1136
1134
|
* @param {Object|String} [options] If string, same as `options.value`.
|
|
1137
1135
|
* @param {String} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter.
|
|
1138
1136
|
* @param {Boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning.
|
|
1137
|
+
* @param {Boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name.
|
|
1139
1138
|
* @return {Model} The newly created discriminator model
|
|
1140
1139
|
* @api public
|
|
1141
1140
|
*/
|
|
1142
1141
|
|
|
1143
1142
|
Model.discriminator = function(name, schema, options) {
|
|
1143
|
+
|
|
1144
1144
|
let model;
|
|
1145
1145
|
if (typeof name === 'function') {
|
|
1146
1146
|
model = name;
|
|
@@ -1164,7 +1164,7 @@ Model.discriminator = function(name, schema, options) {
|
|
|
1164
1164
|
}
|
|
1165
1165
|
|
|
1166
1166
|
schema = discriminator(this, name, schema, value, true);
|
|
1167
|
-
if (this.db.models[name]) {
|
|
1167
|
+
if (this.db.models[name] && !schema.options.overwriteModels) {
|
|
1168
1168
|
throw new OverwriteModelError(name);
|
|
1169
1169
|
}
|
|
1170
1170
|
|
|
@@ -3069,10 +3069,10 @@ Model.create = function create(doc, options, callback) {
|
|
|
3069
3069
|
options = {};
|
|
3070
3070
|
// Handle falsy callbacks re: #5061
|
|
3071
3071
|
if (typeof last === 'function' || (arguments.length > 1 && !last)) {
|
|
3072
|
-
|
|
3073
|
-
|
|
3072
|
+
args = [...arguments];
|
|
3073
|
+
cb = args.pop();
|
|
3074
3074
|
} else {
|
|
3075
|
-
args =
|
|
3075
|
+
args = [...arguments];
|
|
3076
3076
|
}
|
|
3077
3077
|
|
|
3078
3078
|
if (args.length === 2 &&
|
|
@@ -3609,16 +3609,21 @@ Model.bulkWrite = function(ops, options, callback) {
|
|
|
3609
3609
|
* `bulkSave` uses `bulkWrite` under the hood, so it's mostly useful when dealing with many documents (10K+)
|
|
3610
3610
|
*
|
|
3611
3611
|
* @param {[Document]} documents
|
|
3612
|
+
* @param {Object} [options] options passed to the underlying `bulkWrite()`
|
|
3613
|
+
* @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
|
|
3614
|
+
* @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.
|
|
3615
|
+
* @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
|
|
3616
|
+
* @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
|
|
3612
3617
|
*
|
|
3613
3618
|
*/
|
|
3614
|
-
Model.bulkSave = function(documents) {
|
|
3619
|
+
Model.bulkSave = function(documents, options) {
|
|
3615
3620
|
const preSavePromises = documents.map(buildPreSavePromise);
|
|
3616
3621
|
|
|
3617
3622
|
const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true });
|
|
3618
3623
|
|
|
3619
3624
|
let bulkWriteResultPromise;
|
|
3620
3625
|
return Promise.all(preSavePromises)
|
|
3621
|
-
.then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations))
|
|
3626
|
+
.then(() => bulkWriteResultPromise = this.bulkWrite(writeOperations, options))
|
|
3622
3627
|
.then(() => documents.map(buildSuccessfulWriteHandlerPromise))
|
|
3623
3628
|
.then(() => bulkWriteResultPromise)
|
|
3624
3629
|
.catch((err) => {
|