Skip to content

Commit a7e7ce5

Browse files
authored
Merge branch '8.15' into csfle
2 parents 387c957 + e5dcf43 commit a7e7ce5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1665
-520
lines changed

.eslintrc.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ module.exports = {
77
ignorePatterns: [
88
'tools',
99
'dist',
10-
'website.js',
1110
'test/files/*',
1211
'benchmarks',
1312
'*.min.js',

.github/workflows/test.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
strategy:
4040
fail-fast: false
4141
matrix:
42-
node: [16, 18, 20, 22]
42+
node: [16, 18, 20, 22, 24]
4343
os: [ubuntu-22.04, ubuntu-24.04]
4444
mongodb: [6.0.15, 7.0.12, 8.0.0]
4545
include:
@@ -74,12 +74,6 @@ jobs:
7474
- name: NPM Test with Coverage
7575
run: npm run test-coverage
7676
if: matrix.coverage == true
77-
- name: Archive code coverage results
78-
uses: actions/upload-artifact@v4
79-
if: matrix.coverage == true
80-
with:
81-
name: coverage
82-
path: coverage
8377

8478
test-deno:
8579
runs-on: ubuntu-22.04

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ index.html
5959
# yarn package-lock
6060
yarn.lock
6161

62+
# deno lock
63+
deno.lock
64+
6265
# npm pack output
6366
mongoose.tgz
6467
mongoose-*.tgz

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,41 @@
1+
8.14.2 / 2025-05-08
2+
===================
3+
* fix(query): handle casting array filter paths underneath array filter paths with embedded discriminators #15388 #15386
4+
* docs(typescript): correct schema and model generic params in TS virtuals docs #15391
5+
* docs+types(schema): add alternative optimisticConcurrency syntaxes to docs + types #15405 #10591
6+
* chore: add Node 24 to CI matrix #15408 [stscoundrel](https://github.com/stscoundrel)
7+
8+
7.8.7 / 2025-04-30
9+
==================
10+
* types(aggregate): allow calling project() with a string #15304 #15300
11+
* docs: update deleteOne & deleteMany API def #15360 [Elliot67](https://github.com/Elliot67) [SethFalco](https://github.com/SethFalco)
12+
13+
8.14.1 / 2025-04-29
14+
===================
15+
* fix: correct change tracking with maps of arrays of primitives and maps of maps #15374 #15350
16+
* fix(populate): consistently convert Buffer representation of UUID to hex string to avoid confusing populate assignment #15383 #15382
17+
* docs: add TypeScript Query guide with info on lean() + transform() #15377 #15311
18+
19+
8.14.0 / 2025-04-25
20+
===================
21+
* feat: upgrade MongoDB driver -> 6.16 #15371
22+
* feat: implement Query findById methods #15337 [sderrow](https://github.com/sderrow)
23+
* feat(subdocument): support schematype-level minimize option to disable minimizing empty subdocuments #15336 #15313
24+
* feat: add skipOriginalStackTraces option to avoid stack trace performance overhead #15345 #15194
25+
* fix(model): disallow Model.findOneAndUpdate(update) and fix TypeScript types re: findOneAndUpdate #15365 #15363
26+
* types: correctly recurse in InferRawDocType #15357 #14954 [JavaScriptBach](https://github.com/JavaScriptBach)
27+
* types: include virtuals in toJSON and toObject output if virtuals: true set #15346 #15316
28+
* types: make init hooks types accurately reflect runtime behavior #15331 #15301
29+
30+
8.13.3 / 2025-04-24
31+
===================
32+
* fix: export MongooseBulkSaveIncompleteError #15370 #15369
33+
* fix: clone POJOs and arrays when casting query filter to avoid mutating objects #15367 #15364
34+
* types(connection): add Connection.prototype.bulkWrite() to types #15368 #15359
35+
* docs: add version requirements to v7 migration docs #15361 [SethFalco](https://github.com/SethFalco)
36+
* docs: update links in deleteOne & deleteMany API def #15360 [Elliot67](https://github.com/Elliot67)
37+
* docs: adds Model#count to list of fns callback removed from #15349 [SethFalco](https://github.com/SethFalco)
38+
139
8.13.2 / 2025-04-03
240
===================
341
* fix: avoid double calling validators on paths in document arrays underneath subdocuments #15338 #15335

benchmarks/findOneWithCast.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use strict';
2+
3+
const mongoose = require('../');
4+
5+
run().catch(err => {
6+
console.error(err);
7+
process.exit(-1);
8+
});
9+
10+
async function run() {
11+
await mongoose.connect('mongodb://127.0.0.1:27017/mongoose_benchmark');
12+
13+
const SchemaParticipant = new mongoose.Schema(
14+
{
15+
user: mongoose.Schema.Types.UUID,
16+
},
17+
{
18+
_id: false
19+
}
20+
);
21+
22+
const TestSchema = new mongoose.Schema(
23+
{
24+
participants1: { type: [SchemaParticipant] },
25+
participants2: { type: [SchemaParticipant] },
26+
participants3: { type: [SchemaParticipant] },
27+
participants4: { type: [SchemaParticipant] },
28+
participants5: { type: [SchemaParticipant] },
29+
date: { type: Number },
30+
},
31+
{
32+
collection: 'test_uuid_mutations',
33+
}
34+
);
35+
36+
const TestModel = mongoose.model('Test', TestSchema);
37+
38+
if (!process.env.MONGOOSE_BENCHMARK_SKIP_SETUP) {
39+
await TestModel.deleteMany({});
40+
}
41+
42+
const peer = {
43+
user: '1583b99d-8462-4343-8dfd-9105252e5662',
44+
};
45+
46+
const numIterations = 500;
47+
const queryStart = Date.now();
48+
for (let i = 0; i < numIterations; ++i) {
49+
for (let j = 0; j < 10; ++j) {
50+
await TestModel.findOne({
51+
$or: [
52+
{ participants1: { $elemMatch: peer } },
53+
{ participants2: { $elemMatch: peer } },
54+
{ participants3: { $elemMatch: peer } },
55+
{ participants4: { $elemMatch: peer } },
56+
{ participants5: { $elemMatch: peer } }
57+
]
58+
});
59+
}
60+
}
61+
const queryEnd = Date.now();
62+
63+
const results = {
64+
'Average findOne time ms': +((queryEnd - queryStart) / numIterations).toFixed(2)
65+
};
66+
67+
console.log(JSON.stringify(results, null, ' '));
68+
process.exit(0);
69+
}

docs/layout.pug

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ html(lang='en')
4141
- if (versions.currentVersion.listed !== versions.latestVersion.listed)
4242
li.pure-menu-item
4343
a.pure-menu-link(href=`${versions.latestVersion.path}/docs/index.html`) Version #{versions.latestVersion.listed}
44-
each pastVersion in versions.pastVersions
44+
each pastVersion in versions.pastVersions
4545
li.pure-menu-item
4646
a.pure-menu-link(href=`/docs/${pastVersion.path}/index.html`) Version #{pastVersion.listed}
4747
li.pure-menu-item.search
@@ -105,10 +105,14 @@ html(lang='en')
105105
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/schemas.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/schemas.html` ? 'selected' : '') Schemas
106106
li.pure-menu-item.sub-item
107107
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/statics-and-methods.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/statics-and-methods.html` ? 'selected' : '') Statics and Methods
108+
li.pure-menu-item.sub-item
109+
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/queries.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/queries.html` ? 'selected' : '') Queries
108110
li.pure-menu-item.sub-item
109111
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/query-helpers.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/query-helpers.html` ? 'selected' : '') Query Helpers
110112
li.pure-menu-item.sub-item
111113
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/populate.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/populate.html` ? 'selected' : '') Populate
114+
li.pure-menu-item.sub-item
115+
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/virtuals.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/virtuals.html` ? 'selected' : '') Virtuals
112116
li.pure-menu-item.sub-item
113117
a.pure-menu-link(href=`${versions.versionedPath}/docs/typescript/subdocuments.html`, class=outputUrl === `${versions.versionedPath}/docs/typescript/subdocuments.html` ? 'selected' : '') Subdocuments
114118
li.pure-menu-item

docs/migrating_to_7.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ you should be aware of when migrating from Mongoose 6.x to Mongoose 7.x.
1111

1212
If you're still on Mongoose 5.x, please read the [Mongoose 5.x to 6.x migration guide](migrating_to_6.html) and upgrade to Mongoose 6.x first.
1313

14+
* [Version Requirements](#version-requirements)
1415
* [`strictQuery`](#strictquery)
1516
* [Removed `remove()`](#removed-remove)
1617
* [Dropped callback support](#dropped-callback-support)
@@ -28,6 +29,12 @@ If you're still on Mongoose 5.x, please read the [Mongoose 5.x to 6.x migration
2829
* [Removed `LeanDocument` and support for `extends Document`](#removed-leandocument-and-support-for-extends-document)
2930
* [New parameters for `HydratedDocument`](#new-parameters-for-hydrateddocument)
3031

32+
## Version Requirements {#version-requirements}
33+
34+
Mongoose now requires Node.js >= 14.0.0 and MongoDB Node Driver >= 5.0.0.
35+
36+
See [the MongoDB Node Driver migration guide](https://github.com/mongodb/node-mongodb-native/blob/main/etc/notes/CHANGES_5.0.0.md) for detailed info.
37+
3138
## `strictQuery` {#strictquery}
3239

3340
`strictQuery` is now false by default.

docs/typescript/queries.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Queries in TypeScript
2+
3+
Mongoose's [Query class](../api/query.html) is a chainable query builder that represents a MongoDB query.
4+
When you call `find()`, `findOne()`, `updateOne()`, `findOneAndUpdate()`, etc. on a model, Mongoose will return a Query instance.
5+
Queries have a `.then()` function that returns a Promise, so you can use them with `await`.
6+
7+
In TypeScript, the Query class takes the following generic parameters:
8+
9+
```ts
10+
class Query<
11+
ResultType, // The type of the result of the query, like `DocType[]`
12+
DocType, // The hydrated document type of the query's associated model
13+
THelpers = {}, // Query helpers
14+
RawDocType = unknown, // The "lean" document type of the query's associated model
15+
QueryOp = 'find', // The operation that will be executed, like 'find', 'findOne', 'updateOne', etc.
16+
TDocOverrides = Record<string, never> // Methods and virtuals on the hydrated document
17+
>
18+
```
19+
20+
## Using `lean()` in TypeScript
21+
22+
The [`lean()` method](../tutorials/lean.html) tells Mongoose to skip [hydrating](../api/model.html#model_Model-hydrate) the result documents, making queries faster and more memory efficient.
23+
`lean()` comes with some caveats in TypeScript when working with the query `transform()` function.
24+
In general, we recommend calling `lean()` before using the `transform()` function to ensure accurate types.
25+
26+
```ts
27+
// Put `lean()` **before** `transform()` in TypeScript because `transform` modifies the query ResultType into a shape
28+
// that `lean()` does not know how to handle.
29+
const result = await ProjectModel
30+
.find()
31+
.lean()
32+
.transform((docs) => new Map(docs.map((doc) => [doc._id.toString(), doc])));
33+
34+
// Do **not** do the following
35+
const result = await ProjectModel
36+
.find()
37+
.transform((docs) => new Map(docs.map((doc) => [doc._id.toString(), doc])))
38+
.lean();
39+
```
40+
41+
In general, if you're having trouble with `lean()` inferring the correct type, you can try moving `lean()` earlier in the query chain.

docs/typescript/virtuals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ interface UserVirtuals {
7272
fullName: string;
7373
}
7474

75-
type UserModel = Model<UserDoc, {}, UserVirtuals>; // <-- add virtuals here...
75+
type UserModel = Model<UserDoc, {}, {}, UserVirtuals>; // <-- add virtuals here...
7676

77-
const schema = new Schema<UserDoc, UserModel, UserVirtuals>({ // <-- and here
77+
const schema = new Schema<UserDoc, UserModel, {}, {}, UserVirtuals>({ // <-- and here
7878
firstName: String,
7979
lastName: String
8080
});

lib/document.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2620,7 +2620,6 @@ Document.prototype.validate = async function validate(pathsToValidate, options)
26202620
if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') {
26212621
throw new MongooseError('Document.prototype.validate() no longer accepts a callback');
26222622
}
2623-
let parallelValidate;
26242623
this.$op = 'validate';
26252624

26262625
if (arguments.length === 1) {
@@ -2638,16 +2637,9 @@ Document.prototype.validate = async function validate(pathsToValidate, options)
26382637
if (this.$isSubdocument != null) {
26392638
// Skip parallel validate check for subdocuments
26402639
} else if (this.$__.validating && !_skipParallelValidateCheck) {
2641-
parallelValidate = new ParallelValidateError(this, {
2642-
parentStack: options && options.parentStack,
2643-
conflictStack: this.$__.validating.stack
2644-
});
2640+
throw new ParallelValidateError(this);
26452641
} else if (!_skipParallelValidateCheck) {
2646-
this.$__.validating = new ParallelValidateError(this, { parentStack: options && options.parentStack });
2647-
}
2648-
2649-
if (parallelValidate != null) {
2650-
throw parallelValidate;
2642+
this.$__.validating = true;
26512643
}
26522644

26532645
return new Promise((resolve, reject) => {
@@ -3818,6 +3810,8 @@ Document.prototype.$toObject = function(options, json) {
38183810
let _minimize;
38193811
if (options._calledWithOptions.minimize != null) {
38203812
_minimize = options.minimize;
3813+
} else if (this.$__schemaTypeOptions?.minimize != null) {
3814+
_minimize = this.$__schemaTypeOptions.minimize;
38213815
} else if (defaultOptions != null && defaultOptions.minimize != null) {
38223816
_minimize = defaultOptions.minimize;
38233817
} else {

lib/error/index.js

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,32 +65,32 @@ MongooseError.messages = require('./messages');
6565
MongooseError.Messages = MongooseError.messages;
6666

6767
/**
68-
* An instance of this error class will be returned when `save()` fails
69-
* because the underlying
70-
* document was not found. The constructor takes one parameter, the
71-
* conditions that mongoose passed to `updateOne()` when trying to update
72-
* the document.
68+
* An instance of this error class will be thrown when mongoose failed to
69+
* cast a value.
7370
*
7471
* @api public
7572
* @memberOf Error
7673
* @static
7774
*/
7875

79-
MongooseError.DocumentNotFoundError = require('./notFound');
76+
MongooseError.CastError = require('./cast');
8077

8178
/**
82-
* An instance of this error class will be returned when mongoose failed to
83-
* cast a value.
79+
* An instance of this error class will be thrown when `save()` fails
80+
* because the underlying
81+
* document was not found. The constructor takes one parameter, the
82+
* conditions that mongoose passed to `updateOne()` when trying to update
83+
* the document.
8484
*
8585
* @api public
8686
* @memberOf Error
8787
* @static
8888
*/
8989

90-
MongooseError.CastError = require('./cast');
90+
MongooseError.DocumentNotFoundError = require('./notFound');
9191

9292
/**
93-
* An instance of this error class will be returned when [validation](https://mongoosejs.com/docs/validation.html) failed.
93+
* An instance of this error class will be thrown when [validation](https://mongoosejs.com/docs/validation.html) failed.
9494
* The `errors` property contains an object whose keys are the paths that failed and whose values are
9595
* instances of CastError or ValidationError.
9696
*
@@ -137,7 +137,7 @@ MongooseError.ValidationError = require('./validation');
137137
MongooseError.ValidatorError = require('./validator');
138138

139139
/**
140-
* An instance of this error class will be returned when you call `save()` after
140+
* An instance of this error class will be thrown when you call `save()` after
141141
* the document in the database was changed in a potentially unsafe way. See
142142
* the [`versionKey` option](https://mongoosejs.com/docs/guide.html#versionKey) for more information.
143143
*
@@ -149,7 +149,7 @@ MongooseError.ValidatorError = require('./validator');
149149
MongooseError.VersionError = require('./version');
150150

151151
/**
152-
* An instance of this error class will be returned when you call `save()` multiple
152+
* An instance of this error class will be thrown when you call `save()` multiple
153153
* times on the same document in parallel. See the [FAQ](https://mongoosejs.com/docs/faq.html) for more
154154
* information.
155155
*
@@ -181,6 +181,16 @@ MongooseError.OverwriteModelError = require('./overwriteModel');
181181

182182
MongooseError.MissingSchemaError = require('./missingSchema');
183183

184+
/**
185+
* Thrown when some documents failed to save when calling `bulkSave()`
186+
*
187+
* @api public
188+
* @memberOf Error
189+
* @static
190+
*/
191+
192+
MongooseError.MongooseBulkSaveIncompleteError = require('./bulkSaveIncompleteError');
193+
184194
/**
185195
* Thrown when the MongoDB Node driver can't connect to a valid server
186196
* to send an operation to.
@@ -193,7 +203,7 @@ MongooseError.MissingSchemaError = require('./missingSchema');
193203
MongooseError.MongooseServerSelectionError = require('./serverSelection');
194204

195205
/**
196-
* An instance of this error will be returned if you used an array projection
206+
* An instance of this error will be thrown if you used an array projection
197207
* and then modified the array in an unsafe way.
198208
*
199209
* @api public

lib/helpers/clone.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ function clone(obj, options, isArrayChild) {
5757
return clonedDoc;
5858
}
5959
}
60-
const isSingleNested = obj.$isSingleNested;
6160

6261
if (isPOJO(obj) && obj.$__ != null && obj._doc != null) {
6362
return obj._doc;
@@ -70,10 +69,6 @@ function clone(obj, options, isArrayChild) {
7069
ret = obj.toObject(options);
7170
}
7271

73-
if (options && options.minimize && !obj.constructor.$__required && isSingleNested && Object.keys(ret).length === 0) {
74-
return undefined;
75-
}
76-
7772
return ret;
7873
}
7974

0 commit comments

Comments
 (0)