mongoose 8.9.3 → 8.9.5

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/document.js CHANGED
@@ -55,6 +55,7 @@ const documentIsModified = require('./helpers/symbols').documentIsModified;
55
55
  const documentModifiedPaths = require('./helpers/symbols').documentModifiedPaths;
56
56
  const documentSchemaSymbol = require('./helpers/symbols').documentSchemaSymbol;
57
57
  const getSymbol = require('./helpers/symbols').getSymbol;
58
+ const modelSymbol = require('./helpers/symbols').modelSymbol;
58
59
  const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
59
60
  const scopeSymbol = require('./helpers/symbols').scopeSymbol;
60
61
  const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
@@ -1386,7 +1387,9 @@ Document.prototype.$set = function $set(path, val, type, options) {
1386
1387
  const model = val.constructor;
1387
1388
 
1388
1389
  // Check ref
1389
- const ref = schema.options.ref;
1390
+ const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref;
1391
+
1392
+ const ref = refOpt?.modelName || refOpt;
1390
1393
  if (ref != null && (ref === model.modelName || ref === model.baseModelName)) {
1391
1394
  return true;
1392
1395
  }
@@ -191,15 +191,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
191
191
  if (hasMatchFunction) {
192
192
  match = match.call(doc, doc);
193
193
  }
194
- if (Array.isArray(match)) {
195
- for (const item of match) {
196
- if (item != null && item.$where) {
197
- throw new MongooseError('Cannot use $where filter with populate() match');
198
- }
199
- }
200
- } else if (match != null && match.$where != null) {
201
- throw new MongooseError('Cannot use $where filter with populate() match');
202
- }
194
+ throwOn$where(match);
203
195
  data.match = match;
204
196
  data.hasMatchFunction = hasMatchFunction;
205
197
  data.isRefPath = isRefPath;
@@ -463,15 +455,7 @@ function _virtualPopulate(model, docs, options, _virtualRes) {
463
455
  data.match = match;
464
456
  data.hasMatchFunction = hasMatchFunction;
465
457
 
466
- if (Array.isArray(match)) {
467
- for (const item of match) {
468
- if (item != null && item.$where) {
469
- throw new MongooseError('Cannot use $where filter with populate() match');
470
- }
471
- }
472
- } else if (match != null && match.$where != null) {
473
- throw new MongooseError('Cannot use $where filter with populate() match');
474
- }
458
+ throwOn$where(match);
475
459
 
476
460
  // Get local fields
477
461
  const ret = _getLocalFieldValues(doc, localField, model, options, virtual);
@@ -759,3 +743,24 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz
759
743
 
760
744
  return modelNames;
761
745
  }
746
+
747
+ /**
748
+ * Throw an error if there are any $where keys
749
+ */
750
+
751
+ function throwOn$where(match) {
752
+ if (match == null) {
753
+ return;
754
+ }
755
+ if (typeof match !== 'object') {
756
+ return;
757
+ }
758
+ for (const key of Object.keys(match)) {
759
+ if (key === '$where') {
760
+ throw new MongooseError('Cannot use $where filter with populate() match');
761
+ }
762
+ if (match[key] != null && typeof match[key] === 'object') {
763
+ throwOn$where(match[key]);
764
+ }
765
+ }
766
+ }
package/lib/model.js CHANGED
@@ -3436,6 +3436,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
3436
3436
  * @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
3437
3437
  * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout).
3438
3438
  * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
3439
+ * @param {Boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents
3439
3440
  * @return {BulkWriteResult} the return value from `bulkWrite()`
3440
3441
  */
3441
3442
  Model.bulkSave = async function bulkSave(documents, options) {
@@ -3455,15 +3456,13 @@ Model.bulkSave = async function bulkSave(documents, options) {
3455
3456
  }
3456
3457
  }
3457
3458
 
3458
- await Promise.all(documents.map(buildPreSavePromise));
3459
+ await Promise.all(documents.map(doc => buildPreSavePromise(doc, options)));
3459
3460
 
3460
3461
  const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true, timestamps: options.timestamps });
3461
-
3462
- const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, options).then(
3462
+ const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, { skipValidation: true, ...options }).then(
3463
3463
  (res) => ({ bulkWriteResult: res, bulkWriteError: null }),
3464
3464
  (err) => ({ bulkWriteResult: null, bulkWriteError: err })
3465
3465
  );
3466
-
3467
3466
  // If not a MongoBulkWriteError, treat this as all documents failed to save.
3468
3467
  if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) {
3469
3468
  throw bulkWriteError;
@@ -3491,7 +3490,6 @@ Model.bulkSave = async function bulkSave(documents, options) {
3491
3490
  successfulDocuments.push(document);
3492
3491
  }
3493
3492
  }
3494
-
3495
3493
  await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document)));
3496
3494
 
3497
3495
  if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
@@ -3501,9 +3499,9 @@ Model.bulkSave = async function bulkSave(documents, options) {
3501
3499
  return bulkWriteResult;
3502
3500
  };
3503
3501
 
3504
- function buildPreSavePromise(document) {
3502
+ function buildPreSavePromise(document, options) {
3505
3503
  return new Promise((resolve, reject) => {
3506
- document.schema.s.hooks.execPre('save', document, (err) => {
3504
+ document.schema.s.hooks.execPre('save', document, [options], (err) => {
3507
3505
  if (err) {
3508
3506
  reject(err);
3509
3507
  return;
@@ -3704,8 +3702,9 @@ Model.castObject = function castObject(obj, options) {
3704
3702
  Model.castObject.call(schemaType.caster, val)
3705
3703
  ];
3706
3704
  }
3705
+
3706
+ continue;
3707
3707
  }
3708
- continue;
3709
3708
  }
3710
3709
  if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) {
3711
3710
  try {
@@ -209,7 +209,7 @@ SchemaBigInt.prototype.castForQuery = function($conditional, val, context) {
209
209
  return handler.call(this, val);
210
210
  }
211
211
 
212
- return this.applySetters(null, val, context);
212
+ return this.applySetters(val, context);
213
213
  }
214
214
 
215
215
  try {
@@ -253,7 +253,7 @@ SchemaBoolean.prototype.castForQuery = function($conditional, val, context) {
253
253
  return handler.call(this, val);
254
254
  }
255
255
 
256
- return this.applySetters(null, val, context);
256
+ return this.applySetters(val, context);
257
257
  }
258
258
 
259
259
  try {
@@ -522,6 +522,7 @@ SchemaDocumentArray.prototype.clone = function() {
522
522
  }
523
523
  schematype.Constructor.discriminators = Object.assign({},
524
524
  this.Constructor.discriminators);
525
+ schematype._appliedDiscriminators = this._appliedDiscriminators;
525
526
  return schematype;
526
527
  };
527
528
 
@@ -7,6 +7,7 @@
7
7
  const CastError = require('../error/cast');
8
8
  const SchemaType = require('../schemaType');
9
9
  const castInt32 = require('../cast/int32');
10
+ const handleBitwiseOperator = require('./operators/bitwise');
10
11
 
11
12
  /**
12
13
  * Int32 SchemaType constructor.
@@ -200,7 +201,11 @@ SchemaInt32.$conditionalHandlers = {
200
201
  $gt: handleSingle,
201
202
  $gte: handleSingle,
202
203
  $lt: handleSingle,
203
- $lte: handleSingle
204
+ $lte: handleSingle,
205
+ $bitsAllClear: handleBitwiseOperator,
206
+ $bitsAnyClear: handleBitwiseOperator,
207
+ $bitsAllSet: handleBitwiseOperator,
208
+ $bitsAnySet: handleBitwiseOperator
204
209
  };
205
210
 
206
211
  /*!
@@ -228,7 +233,7 @@ SchemaInt32.prototype.castForQuery = function($conditional, val, context) {
228
233
  return handler.call(this, val);
229
234
  }
230
235
 
231
- return this.applySetters(null, val, context);
236
+ return this.applySetters(val, context);
232
237
  }
233
238
 
234
239
  try {
@@ -393,5 +393,6 @@ SchemaSubdocument.prototype.clone = function() {
393
393
  schematype.requiredValidator = this.requiredValidator;
394
394
  }
395
395
  schematype.caster.discriminators = Object.assign({}, this.caster.discriminators);
396
+ schematype._appliedDiscriminators = this._appliedDiscriminators;
396
397
  return schematype;
397
398
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "8.9.3",
4
+ "version": "8.9.5",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -30,34 +30,32 @@
30
30
  "devDependencies": {
31
31
  "@babel/core": "7.26.0",
32
32
  "@babel/preset-env": "7.26.0",
33
- "@typescript-eslint/eslint-plugin": "^8.18.0",
34
- "@typescript-eslint/parser": "^8.18.0",
33
+ "@typescript-eslint/eslint-plugin": "^8.19.1",
34
+ "@typescript-eslint/parser": "^8.19.1",
35
35
  "acquit": "1.3.0",
36
36
  "acquit-ignore": "0.2.1",
37
37
  "acquit-require": "0.1.1",
38
38
  "assert-browserify": "2.0.0",
39
- "axios": "1.1.3",
40
39
  "babel-loader": "8.2.5",
41
40
  "broken-link-checker": "^0.7.8",
42
41
  "buffer": "^5.6.0",
43
42
  "cheerio": "1.0.0",
44
43
  "crypto-browserify": "3.12.1",
45
- "dotenv": "16.4.5",
46
44
  "dox": "1.0.0",
47
- "eslint": "8.57.0",
48
- "eslint-plugin-markdown": "^5.0.0",
45
+ "eslint": "8.57.1",
46
+ "eslint-plugin-markdown": "^5.1.0",
49
47
  "eslint-plugin-mocha-no-only": "1.2.0",
50
48
  "express": "^4.19.2",
51
49
  "fs-extra": "~11.2.0",
52
- "highlight.js": "11.10.0",
50
+ "highlight.js": "11.11.1",
53
51
  "lodash.isequal": "4.5.0",
54
52
  "lodash.isequalwith": "4.4.0",
55
- "markdownlint-cli2": "^0.15.0",
56
- "marked": "15.0.3",
53
+ "markdownlint-cli2": "^0.17.1",
54
+ "marked": "15.0.4",
57
55
  "mkdirp": "^3.0.1",
58
- "mocha": "10.8.2",
56
+ "mocha": "11.0.1",
59
57
  "moment": "2.30.1",
60
- "mongodb-memory-server": "10.1.2",
58
+ "mongodb-memory-server": "10.1.3",
61
59
  "ncp": "^2.0.0",
62
60
  "nyc": "15.1.0",
63
61
  "pug": "3.0.3",
@@ -67,7 +65,7 @@
67
65
  "tsd": "0.31.2",
68
66
  "typescript": "5.7.2",
69
67
  "uuid": "11.0.3",
70
- "webpack": "5.96.1"
68
+ "webpack": "5.97.1"
71
69
  },
72
70
  "directories": {
73
71
  "lib": "./lib/mongoose"
package/types/index.d.ts CHANGED
@@ -710,6 +710,14 @@ declare module 'mongoose' {
710
710
  [K in keyof T]: FlattenProperty<T[K]>;
711
711
  };
712
712
 
713
+ export type BufferToBinaryProperty<T> = T extends Buffer
714
+ ? mongodb.Binary
715
+ : T extends Types.DocumentArray<infer ItemType>
716
+ ? Types.DocumentArray<BufferToBinary<ItemType>>
717
+ : T extends Types.Subdocument<unknown, unknown, infer SubdocType>
718
+ ? HydratedSingleSubdocument<BufferToBinary<SubdocType>>
719
+ : BufferToBinary<T>;
720
+
713
721
  /**
714
722
  * Converts any Buffer properties into mongodb.Binary instances, which is what `lean()` returns
715
723
  */
@@ -719,15 +727,11 @@ declare module 'mongoose' {
719
727
  ? T
720
728
  : T extends TreatAsPrimitives
721
729
  ? T
722
- : T extends Record<string, any> ? {
723
- [K in keyof T]: T[K] extends Buffer
724
- ? mongodb.Binary
725
- : T[K] extends Types.DocumentArray<infer ItemType>
726
- ? Types.DocumentArray<BufferToBinary<ItemType>>
727
- : T[K] extends Types.Subdocument<unknown, unknown, infer SubdocType>
728
- ? HydratedSingleSubdocument<BufferToBinary<SubdocType>>
729
- : BufferToBinary<T[K]>;
730
- } : T;
730
+ : T extends Record<string, any>
731
+ ? {
732
+ [K in keyof T]: BufferToBinaryProperty<T[K]>
733
+ }
734
+ : T;
731
735
 
732
736
  /**
733
737
  * Converts any Buffer properties into { type: 'buffer', data: [1, 2, 3] } format for JSON serialization
package/types/models.d.ts CHANGED
@@ -35,6 +35,7 @@ declare module 'mongoose' {
35
35
  interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions {
36
36
  timestamps?: boolean;
37
37
  session?: ClientSession;
38
+ validateBeforeSave?: boolean;
38
39
  }
39
40
 
40
41
  /**
package/types/query.d.ts CHANGED
@@ -223,7 +223,7 @@ declare module 'mongoose' {
223
223
  type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete';
224
224
 
225
225
  type GetLeanResultType<RawDocType, ResultType, QueryOp> = QueryOp extends QueryOpThatReturnsDocument
226
- ? (ResultType extends any[] ? Default__v<Require_id<BufferToBinary<FlattenMaps<RawDocType>>>>[] : Default__v<Require_id<BufferToBinary<FlattenMaps<RawDocType>>>>)
226
+ ? (ResultType extends any[] ? Default__v<Require_id<FlattenMaps<BufferToBinary<RawDocType>>>>[] : Default__v<Require_id<FlattenMaps<BufferToBinary<RawDocType>>>>)
227
227
  : ResultType;
228
228
 
229
229
  type MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, TQueryHelpers, TDocOverrides = Record<string, never>> = QueryOp extends QueryOpThatReturnsDocument
package/valnotes.md ADDED
@@ -0,0 +1,85 @@
1
+ ## sift where
2
+
3
+ ```
4
+ it('match prevents using $where', async function() {
5
+ const ParentSchema = new Schema({
6
+ name: String,
7
+ child: {
8
+ type: mongoose.Schema.Types.ObjectId,
9
+ ref: 'Child'
10
+ },
11
+ children: [{
12
+ type: mongoose.Schema.Types.ObjectId,
13
+ ref: 'Child'
14
+ }]
15
+ });
16
+
17
+ const ChildSchema = new Schema({
18
+ name: String
19
+ });
20
+ ChildSchema.virtual('parent', {
21
+ ref: 'Parent',
22
+ localField: '_id',
23
+ foreignField: 'parent'
24
+ });
25
+
26
+ const Parent = db.model('Parent', ParentSchema);
27
+ const Child = db.model('Child', ChildSchema);
28
+
29
+ const child = await Child.create({ name: 'Luke' });
30
+ const parent = await Parent.create({ name: 'Anakin', child: child._id });
31
+
32
+ await assert.rejects(
33
+ () => Parent.findOne().populate({ path: 'child', match: () => ({ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }) }),
34
+ /Cannot use \$where filter with populate\(\) match/
35
+ );
36
+ await assert.rejects(
37
+ () => Parent.find().populate({ path: 'child', match: () => ({ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }) }),
38
+ /Cannot use \$where filter with populate\(\) match/
39
+ );
40
+ await assert.rejects(
41
+ () => parent.populate({ path: 'child', match: () => ({ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }) }),
42
+ /Cannot use \$where filter with populate\(\) match/
43
+ );
44
+ await assert.rejects(
45
+ () => Child.find().populate({ path: 'parent', match: () => ({ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }) }),
46
+ /Cannot use \$where filter with populate\(\) match/
47
+ );
48
+ await assert.rejects(
49
+ () => Child.find().populate({ path: 'parent', match: () => ({ $or: [{ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }] }) }),
50
+ /Cannot use \$where filter with populate\(\) match/
51
+ );
52
+ await assert.rejects(
53
+ () => Child.find().populate({ path: 'parent', match: () => ({ $and: [{ $where: 'typeof console !== "undefined" ? doesNotExist("foo") : true;' }] }) }),
54
+ /Cannot use \$where filter with populate\(\) match/
55
+ );
56
+
57
+ class MyClass {}
58
+ MyClass.prototype.$where = 'typeof console !== "undefined" ? doesNotExist("foo") : true;';
59
+ // OK because sift only looks through own properties
60
+ await Child.find().populate({ path: 'parent', match: () => new MyClass() });
61
+ });
62
+ ```
63
+
64
+ ```
65
+ /**
66
+ * Throw an error if there are any $where keys
67
+ */
68
+
69
+ function throwOn$where(match) {
70
+ if (match == null) {
71
+ return;
72
+ }
73
+ if (typeof match !== 'object') {
74
+ return;
75
+ }
76
+ for (const key of Object.keys(match)) {
77
+ if (key === '$where') {
78
+ throw new MongooseError('Cannot use $where filter with populate() match');
79
+ }
80
+ if (match[key] != null && typeof match[key] === 'object') {
81
+ throwOn$where(match[key]);
82
+ }
83
+ }
84
+ }
85
+ ```