mongoose 9.10.4 → 9.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cast.js +9 -19
- package/lib/cursor/queryCursor.js +4 -1
- package/lib/document.js +344 -219
- package/lib/helpers/discriminator/getDiscriminatorValueFromFilter.js +30 -0
- package/lib/helpers/document/toObjectRecursive.js +49 -0
- package/lib/helpers/query/getEmbeddedDiscriminatorPath.js +4 -3
- package/lib/helpers/update/castArrayFilters.js +1 -1
- package/lib/query.js +29 -0
- package/lib/schema/array.js +13 -0
- package/lib/schema/documentArray.js +38 -17
- package/lib/schema/documentArrayElement.js +32 -0
- package/package.json +2 -2
- package/tstyche.matrix.json +9 -0
- package/types/connection.d.ts +1 -1
- package/types/document.d.ts +3 -1
- package/types/index.d.ts +2 -2
- package/types/models.d.ts +66 -35
- package/types/query.d.ts +84 -62
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isPOJO = require('../isPOJO');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the single discriminator value that a query filter condition selects,
|
|
7
|
+
* like `'Clicked'` for `'Clicked'`, `{ $eq: 'Clicked' }`, or `{ $in: ['Clicked'] }`.
|
|
8
|
+
* Returns the condition unchanged if it does not select exactly one value.
|
|
9
|
+
*
|
|
10
|
+
* @param {any} value the filter condition on the discriminator key
|
|
11
|
+
* @return {any}
|
|
12
|
+
* @api private
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
module.exports = function getDiscriminatorValueFromFilter(value) {
|
|
16
|
+
if (!isPOJO(value)) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
const keys = Object.keys(value);
|
|
20
|
+
if (keys.length !== 1) {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
if (keys[0] === '$eq') {
|
|
24
|
+
return value.$eq;
|
|
25
|
+
}
|
|
26
|
+
if (keys[0] === '$in' && Array.isArray(value.$in) && value.$in.length === 1) {
|
|
27
|
+
return value.$in[0];
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const isPOJO = require('../isPOJO');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert nested path documents underneath POJOs and arrays to POJOs without
|
|
7
|
+
* modifying the original value. Only clone containers that contain a nested
|
|
8
|
+
* path document. Preserve regular document children so casting can handle
|
|
9
|
+
* populated documents and subdocuments.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
module.exports = function toObjectRecursive(value, options) {
|
|
13
|
+
return _toObjectRecursive(value, options, true, new Set());
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function _toObjectRecursive(value, options, isRoot, seen) {
|
|
17
|
+
if (value == null) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (value.$__ != null && typeof value.toObject === 'function') {
|
|
22
|
+
return isRoot || value.$__isNested ? value.toObject(options) : value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!Array.isArray(value) && !isPOJO(value)) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (seen.has(value)) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
seen.add(value);
|
|
33
|
+
|
|
34
|
+
let ret = value;
|
|
35
|
+
for (const key of Object.keys(value)) {
|
|
36
|
+
const converted = _toObjectRecursive(value[key], options, false, seen);
|
|
37
|
+
if (converted === value[key]) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (ret === value) {
|
|
42
|
+
ret = Array.isArray(value) ? Object.assign([], value) : { ...value };
|
|
43
|
+
}
|
|
44
|
+
ret[key] = converted;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
seen.delete(value);
|
|
48
|
+
return ret;
|
|
49
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
|
|
4
4
|
const get = require('../get');
|
|
5
5
|
const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
|
|
6
|
+
const getDiscriminatorValueFromFilter = require('../discriminator/getDiscriminatorValueFromFilter');
|
|
6
7
|
const updatedPathsByArrayFilter = require('../update/updatedPathsByArrayFilter');
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -46,16 +47,16 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
|
|
|
46
47
|
let discriminatorKey = null;
|
|
47
48
|
|
|
48
49
|
if (discriminatorValuePath in filter) {
|
|
49
|
-
discriminatorKey = filter[discriminatorValuePath];
|
|
50
|
+
discriminatorKey = getDiscriminatorValueFromFilter(filter[discriminatorValuePath]);
|
|
50
51
|
}
|
|
51
52
|
if (discriminatorFilterPath in filter) {
|
|
52
|
-
discriminatorKey = filter[discriminatorFilterPath];
|
|
53
|
+
discriminatorKey = getDiscriminatorValueFromFilter(filter[discriminatorFilterPath]);
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
const wrapperPath = subpath.replace(/\.\d+$/, '');
|
|
56
57
|
if (schematype.$isMongooseDocumentArrayElement &&
|
|
57
58
|
get(filter[wrapperPath], '$elemMatch.' + key) != null) {
|
|
58
|
-
discriminatorKey = filter[wrapperPath].$elemMatch[key];
|
|
59
|
+
discriminatorKey = getDiscriminatorValueFromFilter(filter[wrapperPath].$elemMatch[key]);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
const discriminatorKeyUpdatePath = originalSubpath + '.' + key;
|
package/lib/query.js
CHANGED
|
@@ -2563,6 +2563,35 @@ Query.prototype.find = function(conditions) {
|
|
|
2563
2563
|
return this;
|
|
2564
2564
|
};
|
|
2565
2565
|
|
|
2566
|
+
/**
|
|
2567
|
+
* Executes the query as a `find()` and also runs a `countDocuments()` query
|
|
2568
|
+
* on the same filter, returning `[documents, count]`. The count ignores
|
|
2569
|
+
* `skip` and `limit` so that it returns the total number of matching documents.
|
|
2570
|
+
*
|
|
2571
|
+
* #### Example:
|
|
2572
|
+
*
|
|
2573
|
+
* const query = Adventure.find({ type: 'animal' }).sort({ name: 1 }).limit(10);
|
|
2574
|
+
* const [adventures, total] = await query.findAndCount();
|
|
2575
|
+
*
|
|
2576
|
+
* @return {Promise<Array>} a promise that resolves to `[documents, total]`
|
|
2577
|
+
* @api public
|
|
2578
|
+
*/
|
|
2579
|
+
|
|
2580
|
+
Query.prototype.findAndCount = async function findAndCount() {
|
|
2581
|
+
if (this.options.sort == null) {
|
|
2582
|
+
throw new MongooseError('Query.prototype.findAndCount() requires sort. Use `sort()` to set a sort order before calling `findAndCount()`.');
|
|
2583
|
+
}
|
|
2584
|
+
if (this.options.limit == null) {
|
|
2585
|
+
throw new MongooseError('Query.prototype.findAndCount() requires limit. Use `limit()` to set a limit before calling `findAndCount()`.');
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
this.op = 'find';
|
|
2589
|
+
|
|
2590
|
+
const countQuery = this.clone().countDocuments().skip(0).limit(null);
|
|
2591
|
+
|
|
2592
|
+
return Promise.all([this.exec(), countQuery.exec()]);
|
|
2593
|
+
};
|
|
2594
|
+
|
|
2566
2595
|
/**
|
|
2567
2596
|
* Merges another Query or conditions object into this one.
|
|
2568
2597
|
*
|
package/lib/schema/array.js
CHANGED
|
@@ -611,6 +611,19 @@ function cast$elemMatch(val, context) {
|
|
|
611
611
|
for (let i = 0; i < numKeys; ++i) {
|
|
612
612
|
const key = keys[i];
|
|
613
613
|
const value = val[key];
|
|
614
|
+
if (key === '$not') {
|
|
615
|
+
// `$not` wraps other operators, like `{ $not: { $gt: 5 } }` or
|
|
616
|
+
// `{ $not: { $elemMatch: ... } }`, so cast each wrapped operator.
|
|
617
|
+
// A RegExp is left as-is.
|
|
618
|
+
if (utils.isPOJO(value)) {
|
|
619
|
+
for (const notKey of Object.keys(value)) {
|
|
620
|
+
if (isOperator(notKey) && value[notKey] != null) {
|
|
621
|
+
value[notKey] = this.castForQuery(notKey, value[notKey], context);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
614
627
|
if (isOperator(key) && value != null) {
|
|
615
628
|
val[key] = this.castForQuery(key, value, context);
|
|
616
629
|
}
|
|
@@ -65,7 +65,6 @@ function SchemaDocumentArray(key, schema, options, schemaOptions, parentSchema)
|
|
|
65
65
|
$parentSchemaType,
|
|
66
66
|
Constructor
|
|
67
67
|
});
|
|
68
|
-
|
|
69
68
|
SchemaArray.call(this, key, embeddedSchemaType, options, null, parentSchema);
|
|
70
69
|
|
|
71
70
|
this.schema = schema;
|
|
@@ -234,8 +233,16 @@ SchemaDocumentArray.prototype.doValidate = async function doValidate(array, scop
|
|
|
234
233
|
// lazy load
|
|
235
234
|
MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentArray'));
|
|
236
235
|
|
|
237
|
-
|
|
236
|
+
let schemaTypeError;
|
|
237
|
+
try {
|
|
238
|
+
await SchemaType.prototype.doValidate.call(this, array, scope);
|
|
239
|
+
} catch (err) {
|
|
240
|
+
schemaTypeError = err;
|
|
241
|
+
}
|
|
238
242
|
if (options?.updateValidator) {
|
|
243
|
+
if (schemaTypeError != null) {
|
|
244
|
+
throw schemaTypeError;
|
|
245
|
+
}
|
|
239
246
|
return;
|
|
240
247
|
}
|
|
241
248
|
if (!utils.isMongooseDocumentArray(array)) {
|
|
@@ -258,14 +265,29 @@ SchemaDocumentArray.prototype.doValidate = async function doValidate(array, scop
|
|
|
258
265
|
doc = array[i] = new Constructor(doc, array, undefined, undefined, i);
|
|
259
266
|
}
|
|
260
267
|
|
|
261
|
-
if (options?.validateModifiedOnly && !doc.$isModified()) {
|
|
268
|
+
if (options?.validateModifiedOnly && !doc.$isNew && !doc.$isModified()) {
|
|
262
269
|
continue;
|
|
263
270
|
}
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
const subdocOptions = doc.$isNew && options?.validateModifiedOnly ?
|
|
273
|
+
{ ...options, validateModifiedOnly: false } :
|
|
274
|
+
options;
|
|
275
|
+
promises.push(doc.validate(null, subdocOptions));
|
|
266
276
|
}
|
|
267
277
|
|
|
268
|
-
|
|
278
|
+
let subdocumentError;
|
|
279
|
+
try {
|
|
280
|
+
await Promise.all(promises);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
subdocumentError = err;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (schemaTypeError != null) {
|
|
286
|
+
throw schemaTypeError;
|
|
287
|
+
}
|
|
288
|
+
if (subdocumentError != null) {
|
|
289
|
+
throw subdocumentError;
|
|
290
|
+
}
|
|
269
291
|
};
|
|
270
292
|
|
|
271
293
|
/**
|
|
@@ -281,15 +303,11 @@ SchemaDocumentArray.prototype.doValidate = async function doValidate(array, scop
|
|
|
281
303
|
|
|
282
304
|
SchemaDocumentArray.prototype.doValidateSync = function(array, scope, options) {
|
|
283
305
|
const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope);
|
|
284
|
-
if (schemaTypeError != null) {
|
|
285
|
-
return schemaTypeError;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
306
|
const count = array?.length;
|
|
289
|
-
let resultError =
|
|
307
|
+
let resultError = schemaTypeError;
|
|
290
308
|
|
|
291
309
|
if (!count) {
|
|
292
|
-
return;
|
|
310
|
+
return resultError;
|
|
293
311
|
}
|
|
294
312
|
|
|
295
313
|
// handle sparse arrays, do not use array.forEach which does not
|
|
@@ -310,11 +328,14 @@ SchemaDocumentArray.prototype.doValidateSync = function(array, scope, options) {
|
|
|
310
328
|
doc = array[i] = new Constructor(doc, array, undefined, undefined, i);
|
|
311
329
|
}
|
|
312
330
|
|
|
313
|
-
if (options?.validateModifiedOnly && !doc.$isModified()) {
|
|
331
|
+
if (options?.validateModifiedOnly && !doc.$isNew && !doc.$isModified()) {
|
|
314
332
|
continue;
|
|
315
333
|
}
|
|
316
334
|
|
|
317
|
-
const
|
|
335
|
+
const subdocOptions = doc.$isNew && options?.validateModifiedOnly ?
|
|
336
|
+
{ ...options, validateModifiedOnly: false } :
|
|
337
|
+
options;
|
|
338
|
+
const subdocValidateError = doc.$__validateSync(subdocOptions);
|
|
318
339
|
|
|
319
340
|
if (subdocValidateError && resultError == null) {
|
|
320
341
|
resultError = subdocValidateError;
|
|
@@ -463,10 +484,10 @@ SchemaDocumentArray.prototype.cast = function(value, doc, init, prev, options) {
|
|
|
463
484
|
}
|
|
464
485
|
|
|
465
486
|
subdoc = new Constructor(null, value, initDocumentOptions, selected, i);
|
|
466
|
-
// Don't pass `path` to $init -
|
|
467
|
-
// Element subdocuments use relative paths internally for change tracking.
|
|
468
|
-
if (options.path != null) {
|
|
469
|
-
const { path, ...initOptions } = options;
|
|
487
|
+
// Don't pass `path` or `arrayPathIndex` to $init - they're only for this DocumentArray itself,
|
|
488
|
+
// not its element fields. Element subdocuments use relative paths internally for change tracking.
|
|
489
|
+
if (options.path != null || options.arrayPathIndex != null) {
|
|
490
|
+
const { path, arrayPathIndex, ...initOptions } = options;
|
|
470
491
|
options = initOptions;
|
|
471
492
|
}
|
|
472
493
|
rawArray[i] = subdoc.$init(rawArray[i], options);
|
|
@@ -108,9 +108,41 @@ SchemaDocumentArrayElement.prototype.doValidate = async function doValidate(valu
|
|
|
108
108
|
value = new Constructor(value, scope, null, null, options?.index ?? null);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// The parent document array validates each of its subdocuments, so when it
|
|
112
|
+
// already did, this element's own validators are all that is left to run.
|
|
113
|
+
if (options?.skipSubdocumentValidation) {
|
|
114
|
+
if (options.skipSchemaValidators) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
return SchemaType.prototype.doValidate.call(this, value, scope, options);
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
return SchemaSubdocument.prototype.doValidate.call(this, value, scope, options);
|
|
112
121
|
};
|
|
113
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Synchronous validation on this individual array element
|
|
125
|
+
*
|
|
126
|
+
* @api private
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
SchemaDocumentArrayElement.prototype.doValidateSync = function doValidateSync(value, scope, options) {
|
|
130
|
+
const Constructor = getConstructor(this.Constructor, value);
|
|
131
|
+
|
|
132
|
+
if (value && !(value instanceof Constructor)) {
|
|
133
|
+
value = new Constructor(value, scope, null, null, options?.index ?? null);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (options?.skipSubdocumentValidation) {
|
|
137
|
+
if (options.skipSchemaValidators) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
return SchemaType.prototype.doValidateSync.call(this, value, scope, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return SchemaSubdocument.prototype.doValidateSync.call(this, value, scope, options);
|
|
144
|
+
};
|
|
145
|
+
|
|
114
146
|
/**
|
|
115
147
|
* Clone the current SchemaType
|
|
116
148
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoose",
|
|
3
3
|
"description": "Mongoose MongoDB ODM",
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.11.1",
|
|
5
5
|
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongodb",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@standard-schema/spec": "^1.1.0",
|
|
24
24
|
"kareem": "3.4.0",
|
|
25
|
-
"mongodb": "~7.
|
|
25
|
+
"mongodb": "~7.7",
|
|
26
26
|
"mpath": "0.9.0",
|
|
27
27
|
"mquery": "6.0.0",
|
|
28
28
|
"ms": "2.1.3",
|
package/types/connection.d.ts
CHANGED
|
@@ -203,7 +203,7 @@ declare module 'mongoose' {
|
|
|
203
203
|
ObtainSchemaGeneric<TSchema, 'TSchemaOptions'>
|
|
204
204
|
>,
|
|
205
205
|
TSchema,
|
|
206
|
-
ObtainSchemaGeneric<TSchema, 'TLeanResultType'
|
|
206
|
+
Default__v<Require_id<ObtainSchemaGeneric<TSchema, 'TLeanResultType'>>>
|
|
207
207
|
> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;
|
|
208
208
|
model<T, U, TQueryHelpers = {}>(
|
|
209
209
|
name: string,
|
package/types/document.d.ts
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -104,7 +104,7 @@ declare module 'mongoose' {
|
|
|
104
104
|
ObtainSchemaGeneric<TSchema, 'TSchemaOptions'>
|
|
105
105
|
>,
|
|
106
106
|
TSchema,
|
|
107
|
-
ObtainSchemaGeneric<TSchema, 'TLeanResultType'
|
|
107
|
+
Default__v<Require_id<ObtainSchemaGeneric<TSchema, 'TLeanResultType'>>>
|
|
108
108
|
> & ObtainSchemaGeneric<TSchema, 'TStaticMethods'>;
|
|
109
109
|
|
|
110
110
|
export function model<T>(name: string, schema?: Schema<T, any, any> | Schema<T & Document, any, any>, collection?: string, options?: CompileModelOptions): Model<T>;
|
|
@@ -738,7 +738,7 @@ declare module 'mongoose' {
|
|
|
738
738
|
static ObjectId: typeof Schema.Types.ObjectId;
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
-
export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number | Schema.Types.Number;
|
|
741
|
+
export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number | Schema.Types.Number | 'int32' | 'Int32' | typeof Schema.Types.Int32 | Schema.Types.Int32;
|
|
742
742
|
export type StringSchemaDefinition = typeof String | 'string' | 'String' | typeof Schema.Types.String | Schema.Types.String;
|
|
743
743
|
export type BooleanSchemaDefinition = typeof Boolean | 'boolean' | 'Boolean' | typeof Schema.Types.Boolean | Schema.Types.Boolean;
|
|
744
744
|
export type DateSchemaDefinition = DateConstructor | 'date' | 'Date' | typeof Schema.Types.Date | Schema.Types.Date;
|