mongoose 8.10.1 → 8.11.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.
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const assert = require('assert');
4
3
  const { Long } = require('bson');
5
4
 
6
5
  /**
@@ -13,6 +12,10 @@ const { Long } = require('bson');
13
12
  * @api private
14
13
  */
15
14
 
15
+ const MAX_BIGINT = 9223372036854775807n;
16
+ const MIN_BIGINT = -9223372036854775808n;
17
+ const ERROR_MESSAGE = `Mongoose only supports BigInts between ${MIN_BIGINT} and ${MAX_BIGINT} because MongoDB does not support arbitrary precision integers`;
18
+
16
19
  module.exports = function castBigInt(val) {
17
20
  if (val == null) {
18
21
  return val;
@@ -21,6 +24,9 @@ module.exports = function castBigInt(val) {
21
24
  return null;
22
25
  }
23
26
  if (typeof val === 'bigint') {
27
+ if (val > MAX_BIGINT || val < MIN_BIGINT) {
28
+ throw new Error(ERROR_MESSAGE);
29
+ }
24
30
  return val;
25
31
  }
26
32
 
@@ -29,8 +35,12 @@ module.exports = function castBigInt(val) {
29
35
  }
30
36
 
31
37
  if (typeof val === 'string' || typeof val === 'number') {
32
- return BigInt(val);
38
+ val = BigInt(val);
39
+ if (val > MAX_BIGINT || val < MIN_BIGINT) {
40
+ throw new Error(ERROR_MESSAGE);
41
+ }
42
+ return val;
33
43
  }
34
44
 
35
- assert.ok(false);
45
+ throw new Error(`Cannot convert value to BigInt: "${val}"`);
36
46
  };
package/lib/connection.js CHANGED
@@ -23,6 +23,7 @@ const CreateCollectionsError = require('./error/createCollectionsError');
23
23
  const castBulkWrite = require('./helpers/model/castBulkWrite');
24
24
  const { modelSymbol } = require('./helpers/symbols');
25
25
  const isPromise = require('./helpers/isPromise');
26
+ const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
26
27
 
27
28
  const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
28
29
  const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
@@ -559,7 +560,9 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
559
560
  'bulkWrite'
560
561
  );
561
562
  }
562
- return getDefaultBulkwriteResult();
563
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
564
+ const res = new BulkWriteResult(getDefaultBulkwriteResult(), false);
565
+ return decorateBulkWriteResult(res, validationErrors, results);
563
566
  }
564
567
 
565
568
  let error;
@@ -567,16 +570,17 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
567
570
  then(res => ([res, null])).
568
571
  catch(err => ([null, err]));
569
572
 
573
+ for (let i = 0; i < validOpIndexes.length; ++i) {
574
+ results[validOpIndexes[i]] = null;
575
+ }
570
576
  if (error) {
571
577
  if (validationErrors.length > 0) {
578
+ decorateBulkWriteResult(error, validationErrors, results);
572
579
  error.mongoose = error.mongoose || {};
573
580
  error.mongoose.validationErrors = validationErrors;
574
581
  }
575
582
  }
576
583
 
577
- for (let i = 0; i < validOpIndexes.length; ++i) {
578
- results[validOpIndexes[i]] = null;
579
- }
580
584
  if (validationErrors.length > 0) {
581
585
  if (options.throwOnValidationError) {
582
586
  throw new MongooseBulkWriteError(
@@ -586,9 +590,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
586
590
  'bulkWrite'
587
591
  );
588
592
  } else {
589
- res.mongoose = res.mongoose || {};
590
- res.mongoose.validationErrors = validationErrors;
591
- res.mongoose.results = results;
593
+ decorateBulkWriteResult(res, validationErrors, results);
592
594
  }
593
595
  }
594
596
  }
package/lib/document.js CHANGED
@@ -3836,15 +3836,39 @@ Document.prototype.$toObject = function(options, json) {
3836
3836
  // Parent options should only bubble down for subdocuments, not populated docs
3837
3837
  options._parentOptions = this.$isSubdocument ? options : null;
3838
3838
 
3839
- // remember the root transform function
3840
- // to save it from being overwritten by sub-transform functions
3841
- // const originalTransform = options.transform;
3839
+ const schemaFieldsOnly = options._calledWithOptions.schemaFieldsOnly
3840
+ ?? options.schemaFieldsOnly
3841
+ ?? defaultOptions.schemaFieldsOnly
3842
+ ?? false;
3842
3843
 
3843
3844
  let ret;
3844
3845
  if (hasOnlyPrimitiveValues && !options.flattenObjectIds) {
3845
3846
  // Fast path: if we don't have any nested objects or arrays, we only need a
3846
3847
  // shallow clone.
3847
- ret = this.$__toObjectShallow();
3848
+ ret = this.$__toObjectShallow(schemaFieldsOnly);
3849
+ } else if (schemaFieldsOnly) {
3850
+ ret = {};
3851
+ for (const path of Object.keys(this.$__schema.paths)) {
3852
+ const value = this.$__getValue(path);
3853
+ if (value === undefined) {
3854
+ continue;
3855
+ }
3856
+ let pathToSet = path;
3857
+ let objToSet = ret;
3858
+ if (path.indexOf('.') !== -1) {
3859
+ const segments = path.split('.');
3860
+ pathToSet = segments[segments.length - 1];
3861
+ for (let i = 0; i < segments.length - 1; ++i) {
3862
+ objToSet[segments[i]] = objToSet[segments[i]] ?? {};
3863
+ objToSet = objToSet[segments[i]];
3864
+ }
3865
+ }
3866
+ if (value === null) {
3867
+ objToSet[pathToSet] = null;
3868
+ continue;
3869
+ }
3870
+ objToSet[pathToSet] = clone(value, options);
3871
+ }
3848
3872
  } else {
3849
3873
  ret = clone(this._doc, options) || {};
3850
3874
  }
@@ -3910,10 +3934,12 @@ Document.prototype.$toObject = function(options, json) {
3910
3934
  * Internal shallow clone alternative to `$toObject()`: much faster, no options processing
3911
3935
  */
3912
3936
 
3913
- Document.prototype.$__toObjectShallow = function $__toObjectShallow() {
3937
+ Document.prototype.$__toObjectShallow = function $__toObjectShallow(schemaFieldsOnly) {
3914
3938
  const ret = {};
3915
3939
  if (this._doc != null) {
3916
- for (const key of Object.keys(this._doc)) {
3940
+ const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc);
3941
+ for (const key of keys) {
3942
+ // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths
3917
3943
  const value = this._doc[key];
3918
3944
  if (value instanceof Date) {
3919
3945
  ret[key] = new Date(value);
@@ -4066,6 +4092,7 @@ Document.prototype.$__toObjectShallow = function $__toObjectShallow() {
4066
4092
  * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
4067
4093
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4068
4094
  * @param {Boolean} [options.useProjection=false] - If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema.
4095
+ * @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toObject()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
4069
4096
  * @return {Object} document as a plain old JavaScript object (POJO). This object may contain ObjectIds, Maps, Dates, mongodb.Binary, Buffers, and other non-POJO values.
4070
4097
  * @see mongodb.Binary https://mongodb.github.io/node-mongodb-native/4.9/classes/Binary.html
4071
4098
  * @api public
@@ -4336,6 +4363,7 @@ function omitDeselectedFields(self, json) {
4336
4363
  * @param {Object} options
4337
4364
  * @param {Boolean} [options.flattenMaps=true] if true, convert Maps to [POJOs](https://masteringjs.io/tutorials/fundamentals/pojo). Useful if you want to `JSON.stringify()` the result.
4338
4365
  * @param {Boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings.
4366
+ * @param {Boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toJSON()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema.
4339
4367
  * @return {Object}
4340
4368
  * @see Document#toObject https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()
4341
4369
  * @see JSON.stringify() in JavaScript https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html
@@ -4506,6 +4534,8 @@ Document.prototype.equals = function(doc) {
4506
4534
  * @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object.
4507
4535
  * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
4508
4536
  * @param {Object} [options.options=null] Additional options like `limit` and `lean`.
4537
+ * @param {Boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated
4538
+ * @param {Boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. We recommend setting this option to `true` if using transactions, especially if also populating multiple paths or paths with multiple models. MongoDB server does **not** support multiple operations in parallel on a single transaction.
4509
4539
  * @param {Function} [callback] Callback
4510
4540
  * @see population https://mongoosejs.com/docs/populate.html
4511
4541
  * @see Query#select https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
@@ -4532,6 +4562,7 @@ Document.prototype.populate = async function populate() {
4532
4562
  }
4533
4563
 
4534
4564
  const paths = utils.object.vals(pop);
4565
+
4535
4566
  let topLevelModel = this.constructor;
4536
4567
  if (this.$__isNested) {
4537
4568
  topLevelModel = this.$__[scopeSymbol].constructor;
@@ -10,3 +10,4 @@ exports.Collection = function() {
10
10
  exports.Connection = function() {
11
11
  throw new Error('Cannot create a connection from browser library');
12
12
  };
13
+ exports.BulkWriteResult = function() {};
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const BulkWriteResult = require('mongodb/lib/bulk/common').BulkWriteResult;
4
+
5
+ module.exports = BulkWriteResult;
@@ -13,6 +13,8 @@ const internalToObjectOptions = require('../../options').internalToObjectOptions
13
13
  const stream = require('stream');
14
14
  const util = require('util');
15
15
 
16
+ const formatToObjectOptions = Object.freeze({ ...internalToObjectOptions, copyTrustedSymbol: false });
17
+
16
18
  /**
17
19
  * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
18
20
  *
@@ -384,7 +386,9 @@ function format(obj, sub, color, shell) {
384
386
  }
385
387
 
386
388
  const clone = require('../../helpers/clone');
387
- let x = clone(obj, internalToObjectOptions);
389
+ // `sub` indicates `format()` was called recursively, so skip cloning because we already
390
+ // did a deep clone on the top-level object.
391
+ let x = sub ? obj : clone(obj, formatToObjectOptions);
388
392
  const constructorName = getConstructorName(x);
389
393
 
390
394
  if (constructorName === 'Binary') {
@@ -4,5 +4,6 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ exports.BulkWriteResult = require('./bulkWriteResult');
7
8
  exports.Collection = require('./collection');
8
9
  exports.Connection = require('./connection');
@@ -147,7 +147,7 @@ function cloneObject(obj, options, isArrayChild) {
147
147
  } else if (seen) {
148
148
  seen.set(obj, ret);
149
149
  }
150
- if (trustedSymbol in obj) {
150
+ if (trustedSymbol in obj && options?.copyTrustedSymbol !== false) {
151
151
  ret[trustedSymbol] = obj[trustedSymbol];
152
152
  }
153
153
 
@@ -1,26 +1,17 @@
1
1
  'use strict';
2
+
2
3
  function getDefaultBulkwriteResult() {
3
4
  return {
4
- result: {
5
- ok: 1,
6
- writeErrors: [],
7
- writeConcernErrors: [],
8
- insertedIds: [],
9
- nInserted: 0,
10
- nUpserted: 0,
11
- nMatched: 0,
12
- nModified: 0,
13
- nRemoved: 0,
14
- upserted: []
15
- },
16
- insertedCount: 0,
17
- matchedCount: 0,
18
- modifiedCount: 0,
19
- deletedCount: 0,
20
- upsertedCount: 0,
21
- upsertedIds: {},
22
- insertedIds: {},
23
- n: 0
5
+ ok: 1,
6
+ nInserted: 0,
7
+ nUpserted: 0,
8
+ nMatched: 0,
9
+ nModified: 0,
10
+ nRemoved: 0,
11
+ upserted: [],
12
+ writeErrors: [],
13
+ insertedIds: [],
14
+ writeConcernErrors: []
24
15
  };
25
16
  }
26
17
 
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = function decorateBulkWriteResult(resultOrError, validationErrors, results) {
4
+ resultOrError.mongoose = resultOrError.mongoose || {};
5
+ resultOrError.mongoose.validationErrors = validationErrors;
6
+ resultOrError.mongoose.results = results;
7
+ return resultOrError;
8
+ };
package/lib/model.js CHANGED
@@ -10,7 +10,6 @@ const Document = require('./document');
10
10
  const DocumentNotFoundError = require('./error/notFound');
11
11
  const EventEmitter = require('events').EventEmitter;
12
12
  const Kareem = require('kareem');
13
- const { MongoBulkWriteError } = require('mongodb');
14
13
  const MongooseBulkWriteError = require('./error/bulkWriteError');
15
14
  const MongooseError = require('./error/index');
16
15
  const ObjectParameterError = require('./error/objectParameter');
@@ -69,6 +68,7 @@ const utils = require('./utils');
69
68
  const minimize = require('./helpers/minimize');
70
69
  const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError');
71
70
  const ObjectExpectedError = require('./error/objectExpected');
71
+ const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');
72
72
 
73
73
  const modelCollectionSymbol = Symbol('mongoose#Model#collection');
74
74
  const modelDbSymbol = Symbol('mongoose#Model#db');
@@ -3104,11 +3104,9 @@ Model.$__insertMany = function(arr, options, callback) {
3104
3104
  const res = {
3105
3105
  acknowledged: true,
3106
3106
  insertedCount: 0,
3107
- insertedIds: {},
3108
- mongoose: {
3109
- validationErrors: validationErrors
3110
- }
3107
+ insertedIds: {}
3111
3108
  };
3109
+ decorateBulkWriteResult(res, validationErrors, validationErrors);
3112
3110
  return callback(null, res);
3113
3111
  }
3114
3112
  callback(null, []);
@@ -3161,10 +3159,7 @@ Model.$__insertMany = function(arr, options, callback) {
3161
3159
 
3162
3160
  // Decorate with mongoose validation errors in case of unordered,
3163
3161
  // because then still do `insertMany()`
3164
- res.mongoose = {
3165
- validationErrors: validationErrors,
3166
- results: results
3167
- };
3162
+ decorateBulkWriteResult(res, validationErrors, results);
3168
3163
  }
3169
3164
  return callback(null, res);
3170
3165
  }
@@ -3198,10 +3193,7 @@ Model.$__insertMany = function(arr, options, callback) {
3198
3193
  if (error.writeErrors != null) {
3199
3194
  for (let i = 0; i < error.writeErrors.length; ++i) {
3200
3195
  const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index);
3201
- error.writeErrors[i] = {
3202
- ...error.writeErrors[i],
3203
- index: originalIndex
3204
- };
3196
+ error.writeErrors[i] = { ...error.writeErrors[i], index: originalIndex };
3205
3197
  if (!ordered) {
3206
3198
  results[originalIndex] = error.writeErrors[i];
3207
3199
  }
@@ -3245,10 +3237,7 @@ Model.$__insertMany = function(arr, options, callback) {
3245
3237
  });
3246
3238
 
3247
3239
  if (rawResult && ordered === false) {
3248
- error.mongoose = {
3249
- validationErrors: validationErrors,
3250
- results: results
3251
- };
3240
+ decorateBulkWriteResult(error, validationErrors, results);
3252
3241
  }
3253
3242
 
3254
3243
  callback(error, null);
@@ -3399,7 +3388,11 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3399
3388
  const ordered = options.ordered == null ? true : options.ordered;
3400
3389
 
3401
3390
  if (ops.length === 0) {
3402
- return getDefaultBulkwriteResult();
3391
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
3392
+ const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
3393
+ bulkWriteResult.n = 0;
3394
+ decorateBulkWriteResult(bulkWriteResult, [], []);
3395
+ return bulkWriteResult;
3403
3396
  }
3404
3397
 
3405
3398
  const validations = ops.map(op => castBulkWrite(this, op, options));
@@ -3470,7 +3463,11 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3470
3463
  'bulkWrite'
3471
3464
  );
3472
3465
  }
3473
- return getDefaultBulkwriteResult();
3466
+ const BulkWriteResult = this.base.driver.get().BulkWriteResult;
3467
+ const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
3468
+ bulkWriteResult.result = getDefaultBulkwriteResult();
3469
+ decorateBulkWriteResult(bulkWriteResult, validationErrors, results);
3470
+ return bulkWriteResult;
3474
3471
  }
3475
3472
 
3476
3473
  let error;
@@ -3478,10 +3475,18 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3478
3475
  then(res => ([res, null])).
3479
3476
  catch(error => ([null, error]));
3480
3477
 
3478
+ const writeErrorsByIndex = {};
3479
+ if (error?.writeErrors) {
3480
+ for (const writeError of error.writeErrors) {
3481
+ writeErrorsByIndex[writeError.err.index] = writeError;
3482
+ }
3483
+ }
3484
+ for (let i = 0; i < validOpIndexes.length; ++i) {
3485
+ results[validOpIndexes[i]] = writeErrorsByIndex[i] ?? null;
3486
+ }
3481
3487
  if (error) {
3482
3488
  if (validationErrors.length > 0) {
3483
- error.mongoose = error.mongoose || {};
3484
- error.mongoose.validationErrors = validationErrors;
3489
+ decorateBulkWriteResult(error, validationErrors, results);
3485
3490
  }
3486
3491
 
3487
3492
  await new Promise((resolve, reject) => {
@@ -3495,9 +3500,6 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3495
3500
  });
3496
3501
  }
3497
3502
 
3498
- for (let i = 0; i < validOpIndexes.length; ++i) {
3499
- results[validOpIndexes[i]] = null;
3500
- }
3501
3503
  if (validationErrors.length > 0) {
3502
3504
  if (options.throwOnValidationError) {
3503
3505
  throw new MongooseBulkWriteError(
@@ -3507,9 +3509,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3507
3509
  'bulkWrite'
3508
3510
  );
3509
3511
  } else {
3510
- res.mongoose = res.mongoose || {};
3511
- res.mongoose.validationErrors = validationErrors;
3512
- res.mongoose.results = results;
3512
+ decorateBulkWriteResult(res, validationErrors, results);
3513
3513
  }
3514
3514
  }
3515
3515
  }
@@ -3575,7 +3575,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
3575
3575
  (err) => ({ bulkWriteResult: null, bulkWriteError: err })
3576
3576
  );
3577
3577
  // If not a MongoBulkWriteError, treat this as all documents failed to save.
3578
- if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) {
3578
+ if (bulkWriteError != null && bulkWriteError.name !== 'MongoBulkWriteError') {
3579
3579
  throw bulkWriteError;
3580
3580
  }
3581
3581
 
@@ -4381,6 +4381,7 @@ Model.validate = async function validate(obj, pathsOrOptions, context) {
4381
4381
  * @param {Object} [options.options=null] Additional options like `limit` and `lean`.
4382
4382
  * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
4383
4383
  * @param {Boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated
4384
+ * @param {Boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. Set this option to `true` if populating multiple paths or paths with multiple models in transactions.
4384
4385
  * @return {Promise}
4385
4386
  * @api public
4386
4387
  */
@@ -4398,11 +4399,21 @@ Model.populate = async function populate(docs, paths) {
4398
4399
  }
4399
4400
 
4400
4401
  // each path has its own query options and must be executed separately
4401
- const promises = [];
4402
- for (const path of paths) {
4403
- promises.push(_populatePath(this, docs, path));
4402
+ if (paths.find(p => p.ordered)) {
4403
+ // Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on
4404
+ // one transaction in parallel.
4405
+ // Note that if _any_ path has `ordered`, we make the top-level populate `ordered` as well.
4406
+ for (const path of paths) {
4407
+ await _populatePath(this, docs, path);
4408
+ }
4409
+ } else {
4410
+ // By default, populate in parallel
4411
+ const promises = [];
4412
+ for (const path of paths) {
4413
+ promises.push(_populatePath(this, docs, path));
4414
+ }
4415
+ await Promise.all(promises);
4404
4416
  }
4405
- await Promise.all(promises);
4406
4417
 
4407
4418
  return docs;
4408
4419
  };
@@ -4522,12 +4533,22 @@ async function _populatePath(model, docs, populateOptions) {
4522
4533
  return;
4523
4534
  }
4524
4535
 
4525
- const promises = [];
4526
- for (const arr of params) {
4527
- promises.push(_execPopulateQuery.apply(null, arr).then(valsFromDb => { vals = vals.concat(valsFromDb); }));
4536
+ if (populateOptions.ordered) {
4537
+ // Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on
4538
+ // one transaction in parallel.
4539
+ for (const arr of params) {
4540
+ await _execPopulateQuery.apply(null, arr).then(valsFromDb => { vals = vals.concat(valsFromDb); });
4541
+ }
4542
+ } else {
4543
+ // By default, populate in parallel
4544
+ const promises = [];
4545
+ for (const arr of params) {
4546
+ promises.push(_execPopulateQuery.apply(null, arr).then(valsFromDb => { vals = vals.concat(valsFromDb); }));
4547
+ }
4548
+
4549
+ await Promise.all(promises);
4528
4550
  }
4529
4551
 
4530
- await Promise.all(promises);
4531
4552
 
4532
4553
  for (const arr of params) {
4533
4554
  const mod = arr[0];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Double type constructor
3
+ *
4
+ * #### Example:
5
+ *
6
+ * const pi = new mongoose.Types.Double(3.1415);
7
+ *
8
+ * @constructor Double
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ module.exports = require('bson').Double;
@@ -12,6 +12,7 @@ exports.Document = // @deprecate
12
12
  exports.Embedded = require('./arraySubdocument');
13
13
 
14
14
  exports.DocumentArray = require('./documentArray');
15
+ exports.Double = require('./double');
15
16
  exports.Decimal128 = require('./decimal128');
16
17
  exports.ObjectId = require('./objectid');
17
18
 
package/lib/utils.js CHANGED
@@ -551,8 +551,8 @@ exports.populate = function populate(path, select, model, match, options, subPop
551
551
  };
552
552
  }
553
553
 
554
- if (typeof obj.path !== 'string') {
555
- throw new TypeError('utils.populate: invalid path. Expected string. Got typeof `' + typeof path + '`');
554
+ if (typeof obj.path !== 'string' && !(Array.isArray(obj.path) && obj.path.every(el => typeof el === 'string'))) {
555
+ throw new TypeError('utils.populate: invalid path. Expected string or array of strings. Got typeof `' + typeof path + '`');
556
556
  }
557
557
 
558
558
  return _populateObj(obj);
@@ -600,7 +600,11 @@ function _populateObj(obj) {
600
600
  }
601
601
 
602
602
  const ret = [];
603
- const paths = oneSpaceRE.test(obj.path) ? obj.path.split(manySpaceRE) : [obj.path];
603
+ const paths = oneSpaceRE.test(obj.path)
604
+ ? obj.path.split(manySpaceRE)
605
+ : Array.isArray(obj.path)
606
+ ? obj.path
607
+ : [obj.path];
604
608
  if (obj.options != null) {
605
609
  obj.options = clone(obj.options);
606
610
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.10.1",
4
+ "version": "8.11.0",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",