@depup/mongoose 9.3.3-depup.0 → 9.8.0-depup.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/README.md +3 -9
- package/changes.json +3 -8
- package/eslint.config.mjs +4 -1
- package/lib/aggregate.js +54 -24
- package/lib/cast.js +1 -1
- package/lib/connection.js +20 -8
- package/lib/cursor/aggregationCursor.js +47 -16
- package/lib/cursor/queryCursor.js +23 -7
- package/lib/document.js +240 -105
- package/lib/drivers/node-mongodb-native/collection.js +47 -14
- package/lib/drivers/node-mongodb-native/connection.js +13 -0
- package/lib/error/cast.js +18 -9
- package/lib/error/divergentArray.js +1 -1
- package/lib/error/index.js +1 -1
- package/lib/error/messages.js +1 -0
- package/lib/helpers/clone.js +22 -5
- package/lib/helpers/document/applyDefaults.js +5 -0
- package/lib/helpers/populate/createPopulateQueryFilter.js +9 -1
- package/lib/helpers/populate/getModelsMapForPopulate.js +8 -6
- package/lib/helpers/populate/splitPopulateQuery.js +81 -0
- package/lib/helpers/query/castUpdate.js +4 -0
- package/lib/helpers/schema/getKeysInSchemaOrder.js +13 -16
- package/lib/helpers/update/applyTimestampsToUpdate.js +4 -2
- package/lib/model.js +159 -41
- package/lib/mongoose.js +3 -3
- package/lib/options/schemaTypeOptions.js +14 -0
- package/lib/plugins/saveSubdocs.js +16 -13
- package/lib/query.js +208 -102
- package/lib/queryHelpers.js +13 -12
- package/lib/schema/array.js +4 -6
- package/lib/schema/bigint.js +1 -3
- package/lib/schema/boolean.js +1 -3
- package/lib/schema/buffer.js +1 -3
- package/lib/schema/date.js +8 -8
- package/lib/schema/decimal128.js +1 -3
- package/lib/schema/documentArray.js +3 -5
- package/lib/schema/double.js +1 -3
- package/lib/schema/int32.js +1 -3
- package/lib/schema/map.js +1 -4
- package/lib/schema/number.js +2 -4
- package/lib/schema/objectId.js +7 -3
- package/lib/schema/string.js +20 -5
- package/lib/schema/subdocument.js +2 -4
- package/lib/schema/union.js +50 -0
- package/lib/schema/uuid.js +1 -3
- package/lib/schema.js +40 -14
- package/lib/schemaType.js +93 -12
- package/lib/standardSchema/convertErrorToIssues.js +20 -0
- package/lib/stateMachine.js +11 -6
- package/lib/tracing.js +38 -0
- package/lib/types/array/methods/index.js +4 -4
- package/lib/types/documentArray/methods/index.js +117 -3
- package/lib/types/subdocument.js +4 -2
- package/lib/utils.js +12 -2
- package/lib/validOptions.js +1 -0
- package/package.json +30 -33
- package/{tstyche.config.json → tstyche.json} +2 -1
- package/types/augmentations.d.ts +2 -2
- package/types/document.d.ts +61 -7
- package/types/expressions.d.ts +16 -0
- package/types/index.d.ts +25 -7
- package/types/inferhydrateddoctype.d.ts +1 -1
- package/types/inferrawdoctype.d.ts +6 -3
- package/types/inferschematype.d.ts +10 -2
- package/types/models.d.ts +30 -13
- package/types/mongooseoptions.d.ts +9 -1
- package/types/populate.d.ts +52 -0
- package/types/query.d.ts +39 -12
- package/types/schemaoptions.d.ts +5 -0
- package/types/schematypes.d.ts +10 -0
- package/types/tracing.d.ts +10 -0
- package/types/utility.d.ts +11 -2
- package/lib/helpers/createJSONSchemaTypeDefinition.js +0 -24
package/lib/model.js
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
const Aggregate = require('./aggregate');
|
|
8
8
|
const ChangeStream = require('./cursor/changeStream');
|
|
9
9
|
const Document = require('./document');
|
|
10
|
+
const { createTracedChannel } = require('./tracing');
|
|
11
|
+
const { trace: traceSave } = createTracedChannel('mongoose:model:save');
|
|
12
|
+
const { trace: traceInsertMany } = createTracedChannel('mongoose:model:insertMany');
|
|
13
|
+
const { trace: traceBulkWrite } = createTracedChannel('mongoose:model:bulkWrite');
|
|
10
14
|
const DocumentNotFoundError = require('./error/notFound');
|
|
11
15
|
const EventEmitter = require('events').EventEmitter;
|
|
12
16
|
const Kareem = require('kareem');
|
|
@@ -37,6 +41,7 @@ const applyVirtualsHelper = require('./helpers/document/applyVirtuals');
|
|
|
37
41
|
const assignVals = require('./helpers/populate/assignVals');
|
|
38
42
|
const castBulkWrite = require('./helpers/model/castBulkWrite');
|
|
39
43
|
const clone = require('./helpers/clone');
|
|
44
|
+
const convertErrorToStandardSchemaIssues = require('./standardSchema/convertErrorToIssues');
|
|
40
45
|
const createPopulateQueryFilter = require('./helpers/populate/createPopulateQueryFilter');
|
|
41
46
|
const decorateUpdateWithVersionKey = require('./helpers/update/decorateUpdateWithVersionKey');
|
|
42
47
|
const getDefaultBulkwriteResult = require('./helpers/getDefaultBulkwriteResult');
|
|
@@ -66,6 +71,7 @@ const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscrim
|
|
|
66
71
|
const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths');
|
|
67
72
|
const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField');
|
|
68
73
|
const setDottedPath = require('./helpers/path/setDottedPath');
|
|
74
|
+
const splitPopulateQuery = require('./helpers/populate/splitPopulateQuery');
|
|
69
75
|
const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter');
|
|
70
76
|
const util = require('util');
|
|
71
77
|
const utils = require('./utils');
|
|
@@ -189,8 +195,11 @@ Model.prototype.db;
|
|
|
189
195
|
*/
|
|
190
196
|
|
|
191
197
|
Model.useConnection = function useConnection(connection) {
|
|
192
|
-
if (
|
|
193
|
-
throw new MongooseError('
|
|
198
|
+
if (typeof connection?.model !== 'function' || typeof connection.collection !== 'function' || typeof connection.base?.version !== 'string') {
|
|
199
|
+
throw new MongooseError('`useConnection()` requires a Mongoose connection.');
|
|
200
|
+
}
|
|
201
|
+
if (this.db?.base?.version && this.db?.base?.version !== connection.base?.version) {
|
|
202
|
+
throw new MongooseError(`The connection passed to \`useConnection()\` has a different version of Mongoose (${connection.base?.version}) than the model you are using (${this.db?.base?.version}).`);
|
|
194
203
|
}
|
|
195
204
|
if (this.db) {
|
|
196
205
|
delete this.db.models[this.modelName];
|
|
@@ -635,7 +644,7 @@ Model.prototype.save = async function save(options) {
|
|
|
635
644
|
if (this.$__.saving) {
|
|
636
645
|
parallelSave = new ParallelSaveError(this);
|
|
637
646
|
} else {
|
|
638
|
-
this.$__.saving =
|
|
647
|
+
this.$__.saving = true;
|
|
639
648
|
}
|
|
640
649
|
|
|
641
650
|
options = new SaveOptions(options);
|
|
@@ -660,19 +669,29 @@ Model.prototype.save = async function save(options) {
|
|
|
660
669
|
|
|
661
670
|
this.$__.saveOptions = options;
|
|
662
671
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
672
|
+
const _this = this;
|
|
673
|
+
return traceSave(async function maybeTracedSave() {
|
|
674
|
+
try {
|
|
675
|
+
await _this.$__save(options);
|
|
676
|
+
} catch (error) {
|
|
677
|
+
_this.$__handleReject(error);
|
|
678
|
+
throw error;
|
|
679
|
+
} finally {
|
|
680
|
+
_this.$__.saving = null;
|
|
681
|
+
_this.$__.saveOptions = null;
|
|
682
|
+
_this.$__.$versionError = null;
|
|
683
|
+
_this.$op = null;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return _this;
|
|
687
|
+
}, () => ({
|
|
688
|
+
operation: 'save',
|
|
689
|
+
collection: _this.constructor.collection.name,
|
|
690
|
+
database: _this.constructor.db?.name,
|
|
691
|
+
serverAddress: _this.constructor.db?.host,
|
|
692
|
+
serverPort: _this.constructor.db?.port,
|
|
693
|
+
args: { options }
|
|
694
|
+
}));
|
|
676
695
|
};
|
|
677
696
|
|
|
678
697
|
Model.prototype.$save = Model.prototype.save;
|
|
@@ -2322,7 +2341,7 @@ Model.where = function where(path, val) {
|
|
|
2322
2341
|
*
|
|
2323
2342
|
* Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
|
|
2324
2343
|
*
|
|
2325
|
-
* Blog.$where('this.username.indexOf("val") !== -1').exec(
|
|
2344
|
+
* const result = await Blog.$where('this.username.indexOf("val") !== -1').exec();
|
|
2326
2345
|
*
|
|
2327
2346
|
* @param {string|Function} argument is a javascript string or anonymous function
|
|
2328
2347
|
* @method $where
|
|
@@ -2415,13 +2434,6 @@ Model.findOneAndUpdate = function(conditions, update, options) {
|
|
|
2415
2434
|
fields = options.fields || options.projection;
|
|
2416
2435
|
}
|
|
2417
2436
|
|
|
2418
|
-
update = clone(update, {
|
|
2419
|
-
depopulate: true,
|
|
2420
|
-
_isNested: true
|
|
2421
|
-
});
|
|
2422
|
-
|
|
2423
|
-
decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
|
|
2424
|
-
|
|
2425
2437
|
const mq = new this.Query({}, {}, this, this.$__collection);
|
|
2426
2438
|
mq.select(fields);
|
|
2427
2439
|
|
|
@@ -2861,6 +2873,10 @@ Model.create = async function create(doc, options) {
|
|
|
2861
2873
|
Model.insertOne = async function insertOne(doc, options) {
|
|
2862
2874
|
_checkContext(this, 'insertOne');
|
|
2863
2875
|
|
|
2876
|
+
if (doc == null || typeof doc !== 'object') {
|
|
2877
|
+
throw new ObjectParameterError(doc, 'doc', 'insertOne');
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2864
2880
|
const discriminatorKey = this.schema.options.discriminatorKey;
|
|
2865
2881
|
const Model = this.discriminators && doc[discriminatorKey] != null ?
|
|
2866
2882
|
this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) :
|
|
@@ -3015,6 +3031,18 @@ Model.insertMany = async function insertMany(arr, options) {
|
|
|
3015
3031
|
throw new MongooseError('Model.insertMany() no longer accepts a callback');
|
|
3016
3032
|
}
|
|
3017
3033
|
|
|
3034
|
+
const ThisModel = this;
|
|
3035
|
+
return traceInsertMany(function maybeTracedInsertMany() { return _insertMany.call(ThisModel, arr, options); }, () => ({
|
|
3036
|
+
operation: 'insertMany',
|
|
3037
|
+
collection: ThisModel.collection.name,
|
|
3038
|
+
database: ThisModel.db?.name,
|
|
3039
|
+
serverAddress: ThisModel.db?.host,
|
|
3040
|
+
serverPort: ThisModel.db?.port,
|
|
3041
|
+
args: { docs: arr, options }
|
|
3042
|
+
}));
|
|
3043
|
+
};
|
|
3044
|
+
|
|
3045
|
+
async function _insertMany(arr, options) {
|
|
3018
3046
|
options = options || {};
|
|
3019
3047
|
const preFilter = buildMiddlewareFilter(options, 'pre');
|
|
3020
3048
|
const postFilter = buildMiddlewareFilter(options, 'post');
|
|
@@ -3250,7 +3278,7 @@ Model.insertMany = async function insertMany(arr, options) {
|
|
|
3250
3278
|
|
|
3251
3279
|
const [result] = await this._middleware.execPost('insertMany', this, [docAttributes], { filter: postFilter });
|
|
3252
3280
|
return result;
|
|
3253
|
-
}
|
|
3281
|
+
}
|
|
3254
3282
|
|
|
3255
3283
|
/*!
|
|
3256
3284
|
* ignore
|
|
@@ -3378,6 +3406,19 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3378
3406
|
typeof arguments[2] === 'function') {
|
|
3379
3407
|
throw new MongooseError('Model.bulkWrite() no longer accepts a callback');
|
|
3380
3408
|
}
|
|
3409
|
+
|
|
3410
|
+
const ThisModel = this;
|
|
3411
|
+
return traceBulkWrite(function maybeTracedBulkWrite() { return _bulkWrite.call(ThisModel, ops, options); }, () => ({
|
|
3412
|
+
operation: 'bulkWrite',
|
|
3413
|
+
collection: ThisModel.collection.name,
|
|
3414
|
+
database: ThisModel.db?.name,
|
|
3415
|
+
serverAddress: ThisModel.db?.host,
|
|
3416
|
+
serverPort: ThisModel.db?.port,
|
|
3417
|
+
args: { ops, options }
|
|
3418
|
+
}));
|
|
3419
|
+
};
|
|
3420
|
+
|
|
3421
|
+
async function _bulkWrite(ops, options) {
|
|
3381
3422
|
options = options || {};
|
|
3382
3423
|
const preFilter = buildMiddlewareFilter(options, 'pre');
|
|
3383
3424
|
const postFilter = buildMiddlewareFilter(options, 'post');
|
|
@@ -3516,7 +3557,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3516
3557
|
await this.hooks.execPost('bulkWrite', this, [res], { filter: postFilter });
|
|
3517
3558
|
|
|
3518
3559
|
return res;
|
|
3519
|
-
}
|
|
3560
|
+
}
|
|
3520
3561
|
|
|
3521
3562
|
/**
|
|
3522
3563
|
* Takes an array of documents, gets the changes and inserts/updates documents in the database
|
|
@@ -3615,12 +3656,16 @@ async function buildPreSavePromise(document, options) {
|
|
|
3615
3656
|
}
|
|
3616
3657
|
|
|
3617
3658
|
async function handleSuccessfulWrite(document, options) {
|
|
3618
|
-
|
|
3659
|
+
const wasNew = document.$isNew;
|
|
3660
|
+
if (wasNew) {
|
|
3619
3661
|
_setIsNew(document, false);
|
|
3620
3662
|
}
|
|
3621
3663
|
|
|
3622
3664
|
document.$__reset();
|
|
3623
|
-
document
|
|
3665
|
+
// version key gets initialized to 0 when inserting a new document so avoid incrementing version key here
|
|
3666
|
+
if (!wasNew) {
|
|
3667
|
+
document._applyVersionIncrement();
|
|
3668
|
+
}
|
|
3624
3669
|
const postFilter = buildMiddlewareFilter(options, 'post');
|
|
3625
3670
|
return document.schema.s.hooks.execPost('save', document, [document], { filter: postFilter });
|
|
3626
3671
|
}
|
|
@@ -3861,7 +3906,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3861
3906
|
throw new MongooseError(`documents.${i} was not a mongoose document, documents must be an array of mongoose documents (instanceof mongoose.Document).`);
|
|
3862
3907
|
}
|
|
3863
3908
|
if (options.validateBeforeSave == null || options.validateBeforeSave) {
|
|
3864
|
-
const err = document
|
|
3909
|
+
const err = document.$__validateSync();
|
|
3865
3910
|
if (err != null) {
|
|
3866
3911
|
throw err;
|
|
3867
3912
|
}
|
|
@@ -3870,6 +3915,12 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op
|
|
|
3870
3915
|
|
|
3871
3916
|
const isANewDocument = document.isNew;
|
|
3872
3917
|
if (isANewDocument) {
|
|
3918
|
+
// Like `$__version()` on insert, initialize the version key so
|
|
3919
|
+
// inserted documents get the same shape as `save()` gives them
|
|
3920
|
+
const versionKey = document.$__schema.options.versionKey;
|
|
3921
|
+
if (versionKey) {
|
|
3922
|
+
document.$__setValue(versionKey, 0);
|
|
3923
|
+
}
|
|
3873
3924
|
const writeOperation = { insertOne: { document } };
|
|
3874
3925
|
utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps);
|
|
3875
3926
|
return writeOperation;
|
|
@@ -4188,13 +4239,24 @@ Model.aggregate = function aggregate(pipeline, options) {
|
|
|
4188
4239
|
* age: { type: Number, required: true }
|
|
4189
4240
|
* });
|
|
4190
4241
|
*
|
|
4242
|
+
* // Succeeds
|
|
4243
|
+
* await Model.validate({ name: 'John Smith', age: 31 });
|
|
4244
|
+
*
|
|
4191
4245
|
* try {
|
|
4192
|
-
* await Model.validate({ name: null }
|
|
4246
|
+
* await Model.validate({ name: null });
|
|
4193
4247
|
* } catch (err) {
|
|
4194
4248
|
* err instanceof mongoose.Error.ValidationError; // true
|
|
4195
4249
|
* Object.keys(err.errors); // ['name']
|
|
4196
4250
|
* }
|
|
4197
4251
|
*
|
|
4252
|
+
* Note: the `pathsToSkip` and `pathsToValidate` options **only** apply to validation, not
|
|
4253
|
+
* casting. This function will still throw an error for values that cannot be casted to the
|
|
4254
|
+
* schema-specified type. Remove any paths you do not want to cast.
|
|
4255
|
+
*
|
|
4256
|
+
* // The following will still throw an error because the value of `age` cannot be
|
|
4257
|
+
* // casted to a number. Remove the `age` property before calling `validate()`.
|
|
4258
|
+
* await Model.validate({ name: 'Test', age: 'not a number' }, ['name']);
|
|
4259
|
+
*
|
|
4198
4260
|
* @param {object} obj
|
|
4199
4261
|
* @param {object|Array|string} pathsOrOptions
|
|
4200
4262
|
* @param {object} [context]
|
|
@@ -4299,6 +4361,33 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
|
|
|
4299
4361
|
return obj;
|
|
4300
4362
|
};
|
|
4301
4363
|
|
|
4364
|
+
/**
|
|
4365
|
+
* Standard Schema adapter for this model.
|
|
4366
|
+
* Calls [`Model.validate()`](https://mongoosejs.com/docs/api/model.html#Model.validate()) internally with the provided `libraryOptions`
|
|
4367
|
+
* to cast and validate the given value.
|
|
4368
|
+
*
|
|
4369
|
+
* @api public
|
|
4370
|
+
* @property ~standard
|
|
4371
|
+
* @memberOf Model
|
|
4372
|
+
* @static
|
|
4373
|
+
*/
|
|
4374
|
+
|
|
4375
|
+
Object.defineProperty(Model, '~standard', {
|
|
4376
|
+
configurable: true,
|
|
4377
|
+
get() {
|
|
4378
|
+
return {
|
|
4379
|
+
version: 1,
|
|
4380
|
+
vendor: 'mongoose',
|
|
4381
|
+
validate: (value, options) => {
|
|
4382
|
+
return this.validate(value, options?.libraryOptions).then(
|
|
4383
|
+
value => ({ value }),
|
|
4384
|
+
error => ({ issues: convertErrorToStandardSchemaIssues(error) })
|
|
4385
|
+
);
|
|
4386
|
+
}
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
});
|
|
4390
|
+
|
|
4302
4391
|
/**
|
|
4303
4392
|
* Populates document references.
|
|
4304
4393
|
*
|
|
@@ -4465,7 +4554,6 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4465
4554
|
mod.foreignField.clear();
|
|
4466
4555
|
mod.foreignField.add(populateOptions.foreignField);
|
|
4467
4556
|
}
|
|
4468
|
-
const match = createPopulateQueryFilter(ids, mod.match, mod.foreignField, mod.model, mod.options.skipInvalidIds);
|
|
4469
4557
|
if (assignmentOpts.excludeId) {
|
|
4470
4558
|
// override the exclusion from the query so we can use the _id
|
|
4471
4559
|
// for document matching during assignment. we'll delete the
|
|
@@ -4486,6 +4574,17 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4486
4574
|
} else if (mod.options.limit != null) {
|
|
4487
4575
|
assignmentOpts.originalLimit = mod.options.limit;
|
|
4488
4576
|
}
|
|
4577
|
+
|
|
4578
|
+
// Execute a separate query per document if `perDocumentLimit` is set (gh-7318), or if
|
|
4579
|
+
// there are so many ids that the `$in` filter may overflow MongoDB's 16 MB BSON size
|
|
4580
|
+
// limit on queries (gh-5890).
|
|
4581
|
+
const splitParams = splitPopulateQuery(mod, ids, select, assignmentOpts);
|
|
4582
|
+
if (splitParams != null) {
|
|
4583
|
+
params.push(...splitParams);
|
|
4584
|
+
continue;
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
const match = createPopulateQueryFilter(ids, mod.match, mod.foreignField, mod.model, mod.options.skipInvalidIds);
|
|
4489
4588
|
params.push([mod, match, select, assignmentOpts]);
|
|
4490
4589
|
}
|
|
4491
4590
|
if (!hasOne) {
|
|
@@ -4506,6 +4605,9 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4506
4605
|
|
|
4507
4606
|
// Track deferred populates per-param (per model) to avoid mixing them
|
|
4508
4607
|
const deferredPopulatesPerParam = new Map();
|
|
4608
|
+
// Query results per param. Batches that were split off of a large populate query (gh-5890)
|
|
4609
|
+
// are assigned from only their own query's results rather than the combined `vals`.
|
|
4610
|
+
const valsByParam = [];
|
|
4509
4611
|
|
|
4510
4612
|
if (populateOptions.ordered) {
|
|
4511
4613
|
// Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on
|
|
@@ -4513,7 +4615,10 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4513
4615
|
for (let i = 0; i < params.length; i++) {
|
|
4514
4616
|
const arr = params[i];
|
|
4515
4617
|
const { docs, deferredPopulates } = await _execPopulateQuery.apply(null, arr);
|
|
4516
|
-
|
|
4618
|
+
valsByParam.push(docs);
|
|
4619
|
+
if (!arr[0]._assignFromOwnResults) {
|
|
4620
|
+
vals = vals.concat(docs);
|
|
4621
|
+
}
|
|
4517
4622
|
if (deferredPopulates.length > 0) {
|
|
4518
4623
|
deferredPopulatesPerParam.set(i, deferredPopulates);
|
|
4519
4624
|
}
|
|
@@ -4528,7 +4633,10 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4528
4633
|
const results = await Promise.all(promises);
|
|
4529
4634
|
for (let i = 0; i < results.length; i++) {
|
|
4530
4635
|
const { docs, deferredPopulates } = results[i];
|
|
4531
|
-
|
|
4636
|
+
valsByParam.push(docs);
|
|
4637
|
+
if (!params[i][0]._assignFromOwnResults) {
|
|
4638
|
+
vals = vals.concat(docs);
|
|
4639
|
+
}
|
|
4532
4640
|
if (deferredPopulates.length > 0) {
|
|
4533
4641
|
deferredPopulatesPerParam.set(i, deferredPopulates);
|
|
4534
4642
|
}
|
|
@@ -4536,13 +4644,15 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4536
4644
|
}
|
|
4537
4645
|
|
|
4538
4646
|
|
|
4539
|
-
for (
|
|
4647
|
+
for (let i = 0; i < params.length; i++) {
|
|
4648
|
+
const arr = params[i];
|
|
4540
4649
|
const mod = arr[0];
|
|
4541
4650
|
const assignmentOpts = arr[3];
|
|
4542
|
-
|
|
4651
|
+
const valsForMod = mod._assignFromOwnResults ? valsByParam[i] : vals;
|
|
4652
|
+
for (const val of valsForMod) {
|
|
4543
4653
|
mod.options._childDocs.push(val);
|
|
4544
4654
|
}
|
|
4545
|
-
_assign(model,
|
|
4655
|
+
_assign(model, valsForMod, mod, assignmentOpts);
|
|
4546
4656
|
}
|
|
4547
4657
|
|
|
4548
4658
|
// Handle deferred populate for cases with per-document match functions.
|
|
@@ -4582,13 +4692,15 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4582
4692
|
}
|
|
4583
4693
|
}
|
|
4584
4694
|
|
|
4585
|
-
for (
|
|
4586
|
-
|
|
4695
|
+
for (let i = 0; i < params.length; i++) {
|
|
4696
|
+
const mod = params[i][0];
|
|
4697
|
+
removeDeselectedForeignField(mod.foreignField, mod.options, mod._assignFromOwnResults ? valsByParam[i] : vals);
|
|
4587
4698
|
}
|
|
4588
|
-
for (
|
|
4589
|
-
const mod =
|
|
4699
|
+
for (let i = 0; i < params.length; i++) {
|
|
4700
|
+
const mod = params[i][0];
|
|
4590
4701
|
if (mod.options?.options?._leanTransform) {
|
|
4591
|
-
|
|
4702
|
+
const valsForMod = mod._assignFromOwnResults ? valsByParam[i] : vals;
|
|
4703
|
+
for (const doc of valsForMod) {
|
|
4592
4704
|
mod.options.options._leanTransform(doc);
|
|
4593
4705
|
}
|
|
4594
4706
|
}
|
|
@@ -4600,6 +4712,12 @@ async function _populatePath(model, docs, populateOptions) {
|
|
|
4600
4712
|
*/
|
|
4601
4713
|
|
|
4602
4714
|
function _execPopulateQuery(mod, match, select) {
|
|
4715
|
+
// `null` match means `mod`'s documents have no ids to query, possible if a large populate
|
|
4716
|
+
// was split into a query per document (gh-5890). Skip executing a query: `_assign()` still
|
|
4717
|
+
// sets the documents' populated paths to the appropriate default value.
|
|
4718
|
+
if (match == null) {
|
|
4719
|
+
return Promise.resolve({ docs: [], deferredPopulates: [] });
|
|
4720
|
+
}
|
|
4603
4721
|
let subPopulate = clone(mod.options.populate);
|
|
4604
4722
|
const queryOptions = {};
|
|
4605
4723
|
if (mod.options.skip !== undefined) {
|
package/lib/mongoose.js
CHANGED
|
@@ -228,7 +228,7 @@ Mongoose.prototype.setDriver = function setDriver(driver) {
|
|
|
228
228
|
* - `bufferCommands`: enable/disable mongoose's buffering mechanism for all connections and models
|
|
229
229
|
* - `bufferTimeoutMS`: If bufferCommands is on, this option sets the maximum amount of time Mongoose buffering will wait before throwing an error. If not specified, Mongoose will use 10000 (10 seconds).
|
|
230
230
|
* - `cloneSchemas`: `false` by default. Set to `true` to `clone()` all schemas before compiling into a model.
|
|
231
|
-
* - `debug`: If `true`, prints the operations mongoose sends to MongoDB to the console. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arguments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`.
|
|
231
|
+
* - `debug`: If `true`, prints the operations mongoose sends to MongoDB to the console. If an object is passed, you can set `color`, `shell`, and `timestamp` options. If `timestamp` is `true`, Mongoose prefixes console debug output with an ISO timestamp in brackets. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arguments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`.
|
|
232
232
|
* - `id`: If `true`, adds a `id` virtual to all schemas unless overwritten on a per-schema basis.
|
|
233
233
|
* - `maxTimeMS`: If set, attaches [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) to every query
|
|
234
234
|
* - `objectIdGetter`: `true` by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter.
|
|
@@ -398,8 +398,8 @@ Mongoose.prototype.get = Mongoose.prototype.set;
|
|
|
398
398
|
* @param {string} [options.user] username for authentication, equivalent to `options.auth.username`. Maintained for backwards compatibility.
|
|
399
399
|
* @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility.
|
|
400
400
|
* @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection.
|
|
401
|
-
* @param {number} [options.maxPoolSize=
|
|
402
|
-
* @param {number} [options.minPoolSize=
|
|
401
|
+
* @param {number} [options.maxPoolSize=100] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
|
|
402
|
+
* @param {number} [options.minPoolSize=0] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs).
|
|
403
403
|
* @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. Defaults to 0, which means Node.js will not time out the socket due to inactivity. A socket may be inactive because of either no activity or a long-running operation. 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.
|
|
404
404
|
* @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.
|
|
405
405
|
* @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.
|
|
@@ -94,6 +94,20 @@ Object.defineProperty(SchemaTypeOptions.prototype, 'cast', opts);
|
|
|
94
94
|
|
|
95
95
|
Object.defineProperty(SchemaTypeOptions.prototype, 'required', opts);
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Controls whether this path may be set to `null`. By default, Mongoose allows
|
|
99
|
+
* `null` for non-required paths. Set `allowNull: false` to allow `undefined`
|
|
100
|
+
* but disallow `null`.
|
|
101
|
+
*
|
|
102
|
+
* @api public
|
|
103
|
+
* @property allowNull
|
|
104
|
+
* @memberOf SchemaTypeOptions
|
|
105
|
+
* @type {boolean}
|
|
106
|
+
* @instance
|
|
107
|
+
*/
|
|
108
|
+
|
|
109
|
+
Object.defineProperty(SchemaTypeOptions.prototype, 'allowNull', opts);
|
|
110
|
+
|
|
97
111
|
/**
|
|
98
112
|
* The default value for this path. If a function, Mongoose executes the function
|
|
99
113
|
* and uses the return value as the default.
|
|
@@ -16,7 +16,10 @@ module.exports = function saveSubdocs(schema) {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
async
|
|
19
|
+
// These hooks are deliberately not `async` so that they don't allocate a
|
|
20
|
+
// promise and force an extra microtask hop on every `save()` when there are
|
|
21
|
+
// no subdocuments. kareem only awaits hooks that return a promise.
|
|
22
|
+
function saveSubdocsPreSave() {
|
|
20
23
|
if (this.$isSubdocument) {
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
@@ -28,15 +31,15 @@ async function saveSubdocsPreSave() {
|
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
const options = this.$__.saveOptions;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
34
|
+
return Promise.all(subdocs.map(subdoc => subdoc._execDocumentPreHooks('save', options, [options]))).then(() => {
|
|
35
|
+
// Invalidate subdocs cache because subdoc pre hooks can add new subdocuments
|
|
36
|
+
if (this.$__.saveOptions) {
|
|
37
|
+
this.$__.saveOptions.__subdocs = null;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
function saveSubdocsPostSave() {
|
|
40
43
|
if (this.$isSubdocument) {
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
@@ -53,10 +56,10 @@ async function saveSubdocsPostSave() {
|
|
|
53
56
|
promises.push(subdoc._execDocumentPostHooks('save', options));
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
return Promise.all(promises);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
function saveSubdocsPreDeleteOne() {
|
|
60
63
|
const removedSubdocs = this.$__.removedSubdocs;
|
|
61
64
|
if (!removedSubdocs?.length) {
|
|
62
65
|
return;
|
|
@@ -68,10 +71,10 @@ async function saveSubdocsPreDeleteOne() {
|
|
|
68
71
|
promises.push(subdoc._execDocumentPreHooks('deleteOne', options));
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
return Promise.all(promises);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
function saveSubdocsPostDeleteOne() {
|
|
75
78
|
const removedSubdocs = this.$__.removedSubdocs;
|
|
76
79
|
if (!removedSubdocs?.length) {
|
|
77
80
|
return;
|
|
@@ -84,7 +87,7 @@ async function saveSubdocsPostDeleteOne() {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
this.$__.removedSubdocs = null;
|
|
87
|
-
|
|
90
|
+
return Promise.all(promises);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
|