Skip to content

Commit 4280457

Browse files
authored
Merge pull request #13937 from Automattic/8.0
8.0
2 parents 7efa151 + 502ec4b commit 4280457

File tree

125 files changed

+2466
-2882
lines changed

Some content is hidden

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

125 files changed

+2466
-2882
lines changed

.eslintrc.js

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ module.exports = {
2323
'**/*.md/*.ts',
2424
'**/*.md/*.typescript'
2525
],
26+
parserOptions: {
27+
project: './tsconfig.json'
28+
},
2629
extends: [
2730
'plugin:@typescript-eslint/eslint-recommended',
2831
'plugin:@typescript-eslint/recommended'

.github/workflows/test.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Setup node
2828
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
2929
with:
30-
node-version: 14
30+
node-version: 18
3131

3232
- run: npm install
3333

@@ -39,7 +39,7 @@ jobs:
3939
strategy:
4040
fail-fast: false
4141
matrix:
42-
node: [14, 16, 18, 20]
42+
node: [16, 18, 20]
4343
os: [ubuntu-20.04, ubuntu-22.04]
4444
mongodb: [4.4.18, 5.0.14, 6.0.4]
4545
include:
@@ -108,7 +108,7 @@ jobs:
108108
- name: Setup Deno
109109
uses: denoland/setup-deno@v1
110110
with:
111-
deno-version: v1.34.x
111+
deno-version: v1.36.x
112112
- run: deno --version
113113
- run: npm install
114114
- name: Run Deno tests

.github/workflows/tsd.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Setup node
2828
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
2929
with:
30-
node-version: 14
30+
node-version: 18
3131

3232
- run: npm install
3333

CHANGELOG.md

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
8.0.0-rc0 / 2023-10-24
2+
======================
3+
* BREAKING CHANGE: use MongoDB node driver 6, drop support for rawResult option and findOneAndRemove() #13753
4+
* BREAKING CHANGE: apply minimize by default when updating document #13843
5+
* BREAKING CHANGE: remove `id` setter #13784
6+
* BREAKING CHANGE: remove overwrite option for updateOne(), findOneAndUpdate(), etc. #13989 #13578
7+
* BREAKING CHANGE: make model.prototype.deleteOne() return query, not promise #13660 #13369
8+
* BREAKING CHANGE: remove `Model.count()`, `Query.prototype.count()` #13618 #13598
9+
* BREAKING CHANGE: allow null values for string enum #13620 #3044
10+
* BREAKING CHANGE: make base schema paths come before discriminator schema paths when running setters, validators, etc. #13846 #13794
11+
* BREAKING CHANGE: make Model.validate() use Model.castObject() to cast, and return casted copy of object instead of modifying in place #13287 #12668
12+
* BREAKING CHANGE: make internal file names all camelCase #13950 #13909 #13308
13+
* BREAKING CHANGE: make create() wait for all documents to finish inserting or error out before throwing an error if ordered = false #13621 #4628
14+
* BREAKING CHANGE: refactor out `mongoose/lib/mongoose.js` file to allow importing Mongoose without MongoDB driver #13905
15+
* BREAKING CHANGE(types): allow `null` for optional fields #13901
16+
* BREAKING CHANGE(types): infer return types types for Model.distinct and Query.distinct #13836 [kaulshashank](https://github.com/kaulshashank)
17+
118
7.6.3 / 2023-10-17
219
==================
320
* fix(populate): handle multiple spaces when specifying paths to populate using space-delimited paths #13984 #13951

docs/middleware.md

-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ In query middleware functions, `this` refers to the query.
4848
* [find](api/query.html#query_Query-find)
4949
* [findOne](api/query.html#query_Query-findOne)
5050
* [findOneAndDelete](api/query.html#query_Query-findOneAndDelete)
51-
* [findOneAndRemove](api/query.html#query_Query-findOneAndRemove)
5251
* [findOneAndReplace](api/query.html#query_Query-findOneAndReplace)
5352
* [findOneAndUpdate](api/query.html#query_Query-findOneAndUpdate)
5453
* [remove](api/model.html#model_Model-remove)
@@ -81,7 +80,6 @@ Here are the possible strings that can be passed to `pre()`
8180
* find
8281
* findOne
8382
* findOneAndDelete
84-
* findOneAndRemove
8583
* findOneAndReplace
8684
* findOneAndUpdate
8785
* init

docs/migrating_to_8.md

+300
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# Migrating from 7.x to 8.x
2+
3+
<style>
4+
ul > li {
5+
padding: 4px 0px;
6+
}
7+
</style>
8+
9+
There are several backwards-breaking changes
10+
you should be aware of when migrating from Mongoose 7.x to Mongoose 8.x.
11+
12+
If you're still on Mongoose 6.x or earlier, please read the [Mongoose 6.x to 7.x migration guide](migrating_to_7.html) and upgrade to Mongoose 7.x first before upgrading to Mongoose 8.
13+
14+
* [Removed `rawResult` option for `findOneAndUpdate()`](#removed-rawresult-option-for-findoneandupdate)
15+
* [`Document.prototype.deleteOne()` now returns a query](#document-prototype-deleteone-now-returns-a-query)
16+
* [MongoDB Node Driver 6.0](#mongodb-node-driver-6)
17+
* [Removed `findOneAndRemove()`](#removed-findoneandremove)
18+
* [Removed `count()`](#removed-count)
19+
* [Removed id Setter](#removed-id-setter)
20+
* [`null` is valid for non-required string enums](#null-is-valid-for-non-required-string-enums)
21+
* [Apply minimize when `save()` updates an existing document](#apply-minimize-when-save-updates-an-existing-document)
22+
* [Apply base schema paths before discriminator paths](#apply-base-schema-paths-before-discriminator-paths)
23+
* [Removed `overwrite` option for `findOneAndUpdate()`](#removed-overwrite-option-for-findoneandupdate)
24+
* [Changed behavior for `findOneAndUpdate()` with `orFail()` and upsert](#changed-behavior-for-findoneandupdate-with-orfail-and-upsert)
25+
* [`create()` waits until all saves are done before throwing any error](#create-waits-until-all-saves-are-done-before-throwing-any-error)
26+
* [`Model.validate()` returns copy of object](#model-validate-returns-copy-of-object)
27+
* [Allow `null` For Optional Fields in TypeScript](#allow-null-for-optional-fields-in-typescript)
28+
* [Model constructor properties are all optional in TypeScript](#model-constructor-properties-are-all-optional-in-typescript)
29+
* [Infer `distinct()` return types from schema](#infer-distinct-return-types-from-schema)
30+
31+
<h2 id="removed-rawresult-option-for-findoneandupdate"><a href="#removed-rawresult-option-for-findoneandupdate">Removed <code>rawResult</code> option for <code>findOneAndUpdate()</code></a></h2>
32+
33+
The `rawResult` option for `findOneAndUpdate()`, `findOneAndReplace()`, and `findOneAndDelete()` has been replaced by the `includeResultMetadata` option.
34+
35+
```javascript
36+
const filter = { name: 'Will Riker' };
37+
const update = { age: 29 };
38+
39+
const res = await Character.findOneAndUpdate(filter, update, {
40+
new: true,
41+
upsert: true,
42+
// Replace `rawResult: true` with `includeResultMetadata: true`
43+
includeResultMetadata: true
44+
});
45+
```
46+
47+
`includeResultMetadata` in Mongoose 8 behaves identically to `rawResult`.
48+
49+
<h2 id="document-prototype-deleteone-now-returns-a-query"><a href="#document-prototype-deleteone-now-returns-a-query"><code>Document.prototype.deleteOne</code> now returns a query</a></h2>
50+
51+
In Mongoose 7, `doc.deleteOne()` returned a promise that resolved to `doc`.
52+
In Mongoose 8, `doc.deleteOne()` returns a query for easier chaining, as well as consistency with `doc.updateOne()`.
53+
54+
```javascript
55+
const numberOne = await Character.findOne({ name: 'Will Riker' });
56+
57+
// In Mongoose 7, q is a Promise that resolves to `numberOne`
58+
// In Mongoose 8, q is a Query.
59+
const q = numberOne.deleteOne();
60+
61+
// In Mongoose 7, `res === numberOne`
62+
// In Mongoose 8, `res` is a `DeleteResult`.
63+
const res = await q;
64+
```
65+
66+
<h2 id="mongodb-node-driver-6"><a href="#mongodb-node-driver-6">MongoDB Node Driver 6</a></h2>
67+
68+
Mongoose 8 uses [v6.x of the MongoDB Node driver](https://github.com/mongodb/node-mongodb-native/blob/main/HISTORY.md#600-2023-08-28).
69+
There's a few noteable changes in MongoDB Node driver v6 that affect Mongoose:
70+
71+
1. The `ObjectId` constructor no longer accepts strings of length 12. In Mongoose 7, `new mongoose.Types.ObjectId('12charstring')` was perfectly valid. In Mongoose 8, `new mongoose.Types.ObjectId('12charstring')` throws an error.
72+
73+
<h2 id="removed-findoneandremove"><a href="#removed-findoneandremove">Removed <code>findOneAndRemove()</code></a></h2>
74+
75+
In Mongoose 7, `findOneAndRemove()` was an alias for `findOneAndDelete()` that Mongoose supported for backwards compatibility.
76+
Mongoose 8 no longer supports `findOneAndRemove()`.
77+
Use `findOneAndDelete()` instead.
78+
79+
<h2 id="removed-count"><a href="#removed-count">Removed <code>count()</code></a></h2>
80+
81+
`Model.count()` and `Query.prototype.count()` were removed in Mongoose 8. Use `Model.countDocuments()` and `Query.prototype.countDocuments()` instead.
82+
83+
<h2 id="removed-id-setter"><a href="#removed-id-setter">Removed id Setter</a></h2>
84+
85+
In Mongoose 7.4, Mongoose introduced an `id` setter that made `doc.id = '0'.repeat(24)` equivalent to `doc._id = '0'.repeat(24)`.
86+
In Mongoose 8, that setter is now removed.
87+
88+
<h2 id="null-is-valid-for-non-required-string-enums"><a href="#null-is-valid-for-non-required-string-enums"><code>null</code> is valid for non-required string enums</a></h2>
89+
90+
Before Mongoose 8, setting a string path with an `enum` to `null` would lead to a validation error, even if that path wasn't `required`.
91+
In Mongoose 8, it is valid to set a string path to `null` if `required` is not set, even with `enum`.
92+
93+
```javascript
94+
const schema = new Schema({
95+
status: {
96+
type: String,
97+
enum: ['on', 'off']
98+
}
99+
});
100+
const Test = mongoose.model('Test', schema);
101+
102+
// Works fine in Mongoose 8
103+
// Throws a `ValidationError` in Mongoose 7
104+
await Test.create({ status: null });
105+
```
106+
107+
<h2 id="apply-minimize-when-save-updates-an-existing-document"><a href="#apply-minimize-when-save-updates-an-existing-document">Apply minimize when <code>save()</code> updates an existing document</a></h2>
108+
109+
In Mongoose 7, Mongoose would only apply minimize when saving a new document, not when updating an existing document.
110+
111+
```javascript
112+
const schema = new Schema({
113+
nested: {
114+
field1: Number
115+
}
116+
});
117+
const Test = mongoose.model('Test', schema);
118+
119+
// Both Mongoose 7 and Mongoose 8 strip out empty objects when saving
120+
// a new document in MongoDB by default
121+
const { _id } = await Test.create({ nested: {} });
122+
let rawDoc = await Test.findById(_id).lean();
123+
rawDoc.nested; // undefined
124+
125+
// Mongoose 8 will also strip out empty objects when saving an
126+
// existing document in MongoDB
127+
const doc = await Test.findById(_id);
128+
doc.nested = {};
129+
doc.markModified('nested');
130+
await doc.save();
131+
132+
let rawDoc = await Test.findById(_id).lean();
133+
rawDoc.nested; // undefined in Mongoose 8, {} in Mongoose 7
134+
```
135+
136+
<h2 id="apply-base-schema-paths-before-discriminator-paths"><a href="#apply-base-schema-paths-before-discriminator-paths">Apply base schema paths before discriminator paths</a></h2>
137+
138+
This means that, in Mongoose 8, getters and setters on discriminator paths run *after* getters and setters on base paths.
139+
In Mongoose 7, getters and setters on discriminator paths ran *before* getters and setters on base paths.
140+
141+
```javascript
142+
143+
const schema = new Schema({
144+
name: {
145+
type: String,
146+
get(v) {
147+
console.log('Base schema getter');
148+
return v;
149+
}
150+
}
151+
});
152+
153+
const Test = mongoose.model('Test', schema);
154+
const D = Test.discriminator('D', new Schema({
155+
otherProp: {
156+
type: String,
157+
get(v) {
158+
console.log('Discriminator schema getter');
159+
return v;
160+
}
161+
}
162+
}));
163+
164+
const doc = new D({ name: 'test', otherProp: 'test' });
165+
// In Mongoose 8, prints "Base schema getter" followed by "Discriminator schema getter"
166+
// In Mongoose 7, prints "Discriminator schema getter" followed by "Base schema getter"
167+
console.log(doc.toObject({ getters: true }));
168+
```
169+
170+
<h2 id="removed-overwrite-option-for-findoneandupdate"><a href="#removed-overwrite-option-for-findoneandupdate">Removed <code>overwrite</code> option for <code>findOneAndUpdate()</code></a></h2>
171+
172+
Mongoose 7 and earlier supported an `overwrite` option for `findOneAndUpdate()`, `updateOne()`, and `update()`.
173+
Before Mongoose 7, `overwrite` would skip wrapping the `update` parameter in `$set`, which meant that `findOneAndUpdate()` and `update()` would overwrite the matched document.
174+
In Mongoose 7, setting `overwrite` would convert `findOneAndUpdate()` to `findOneAndReplace()` and `updateOne()` to `replaceOne()` to retain backwards compatibility.
175+
176+
In Mongoose 8, the `overwrite` option is no longer supported.
177+
If you want to overwrite the entire document, use `findOneAndReplace()` or `replaceOne()`.
178+
179+
<h2 id="changed-behavior-for-findoneandupdate-with-orfail-and-upsert"><a href="#changed-behavior-for-findoneandupdate-with-orfail-and-upsert">Changed behavior for <code>findOneAndUpdate()</code> with <code>orFail()</code> and upsert</a></h2>
180+
181+
In Mongoose 7, `findOneAndUpdate(filter, update, { upsert: true }).orFail()` would throw a `DocumentNotFoundError` if a new document was upserted.
182+
In other words, `findOneAndUpdate().orFail()` always threw an error if no document was found, even if a new document was upserted.
183+
184+
In Mongoose 8, `findOneAndUpdate(filter, update, { upsert: true }).orFail()` always succeeds.
185+
`findOneAndUpdate().orFail()` now throws a `DocumentNotFoundError` if there's no document returned, rather than if no document was found.
186+
187+
<h2 id="create-waits-until-all-saves-are-done-before-throwing-any-error"><a href="#create-waits-until-all-saves-are-done-before-throwing-any-error"><code>create()</code> waits until all saves are done before throwing any error</a></h2>
188+
189+
In Mongoose 7, `create()` would immediately throw if any `save()` threw an error by default.
190+
Mongoose 8 instead waits for all `save()` calls to finish before throwing the first error that occurred.
191+
So `create()` will throw the same error in both Mongoose 7 and Mongoose 8, Mongoose 8 just may take longer to throw the error.
192+
193+
```javascript
194+
const schema = new Schema({
195+
name: {
196+
type: String,
197+
enum: ['Badger', 'Mushroom']
198+
}
199+
});
200+
schema.pre('save', async function() {
201+
await new Promise(resolve => setTimeout(resolve, 1000));
202+
});
203+
const Test = mongoose.model('Test', schema);
204+
205+
const err = await Test.create([
206+
{ name: 'Badger' },
207+
{ name: 'Mushroom' },
208+
{ name: 'Cow' }
209+
]).then(() => null, err => err);
210+
err; // ValidationError
211+
212+
// In Mongoose 7, there would be 0 documents, because `Test.create()`
213+
// would throw before 'Badger' and 'Mushroom' are inserted
214+
// In Mongoose 8, there will be 2 documents. `Test.create()` waits until
215+
// 'Badger' and 'Mushroom' are inserted before throwing.
216+
await Test.countDocuments();
217+
```
218+
219+
<h2 id="model-validate-returns-copy-of-object"><a href="#model-validate-returns-copy-of-object"><code>Model.validate()</code> returns copy of object</a></h2>
220+
221+
In Mongoose 7, `Model.validate()` would potentially modify the passed in object.
222+
Mongoose 8 instead copies the passed in object first.
223+
224+
```javascript
225+
const schema = new Schema({ answer: Number });
226+
const Test = mongoose.model('Test', schema);
227+
228+
const obj = { answer: '42' };
229+
const res = Test.validate(obj);
230+
231+
typeof obj.answer; // 'string' in Mongoose 8, 'number' in Mongoose 7
232+
typeof res.answer; // 'number' in both Mongoose 7 and Mongoose 8
233+
```
234+
235+
<h2 id="allow-null-for-optional-fields-in-typescript"><a href="#allow-null-for-optional-fields-in-typescript">Allow <code>null</code> For Optional Fields in TypeScript</a></h2>
236+
237+
In Mongoose 8, automatically inferred schema types in TypeScript allow `null` for optional fields.
238+
In Mongoose 7, optional fields only allowed `undefined`, not `null`.
239+
240+
```typescript
241+
const schema = new Schema({ name: String });
242+
const TestModel = model('Test', schema);
243+
244+
const doc = new TestModel();
245+
246+
// In Mongoose 8, this type is `string | null | undefined`.
247+
// In Mongoose 7, this type is `string | undefined`
248+
doc.name;
249+
```
250+
251+
<h2 id="model-constructor-properties-are-all-optional-in-typescript"><a href="#model-constructor-properties-are-all-optional-in-typescript">Model constructor properties are all optional in TypeScript</a></h2>
252+
253+
In Mongoose 8, no properties are required on model constructors by default.
254+
255+
```ts
256+
import {Schema, model, Model} from 'mongoose';
257+
258+
interface IDocument {
259+
name: string;
260+
createdAt: Date;
261+
updatedAt: Date;
262+
}
263+
264+
const documentSchema = new Schema<IDocument>(
265+
{ name: { type: String, required: true } },
266+
{ timestamps: true }
267+
);
268+
269+
const TestModel = model<IDocument>('Document', documentSchema);
270+
271+
// Would throw a compile error in Mongoose 7, compiles in Mongoose 8
272+
const newDoc = new TestModel({
273+
name: 'Foo'
274+
});
275+
276+
// Explicitly pass generic param to constructor to specify the expected
277+
// type of the model constructor param. The following will cause TS
278+
// to complain about missing `createdAt` and `updatedAt` in Mongoose 8.
279+
const newDoc2 = new TestModel<IDocument>({
280+
name: 'Foo'
281+
});
282+
```
283+
284+
<h2 id="infer-distinct-return-types-from-schema"><a href="#infer-distinct-return-types-from-schema">Infer <code>distinct()</code> return types from schema</a></h2>
285+
286+
```ts
287+
interface User {
288+
name: string;
289+
email: string;
290+
avatar?: string;
291+
}
292+
const schema = new Schema<User>({
293+
name: { type: String, required: true },
294+
email: { type: String, required: true },
295+
avatar: String
296+
});
297+
298+
// Works in Mongoose 8. Compile error in Mongoose 7.
299+
const names: string[] = await MyModel.distinct('name');
300+
```

docs/models.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ If you attempt to `save()` a document from a View, you will get an error from th
193193

194194
## Yet more
195195

196-
The [API docs](api/model.html#model_Model) cover many additional methods available like [count](api/model.html#model_Model-count), [mapReduce](api/model.html#model_Model-mapReduce), [aggregate](api/model.html#model_Model-aggregate), and [more](api/model.html#model_Model-findOneAndRemove).
196+
The [API docs](api/model.html#model_Model) cover many additional methods available like [count](api/model.html#model_Model-count), [mapReduce](api/model.html#model_Model-mapReduce), [aggregate](api/model.html#model_Model-aggregate), and more.
197197

198198
## Next Up
199199

0 commit comments

Comments
 (0)