從 7.x 遷移至 8.x
從 Mongoose 7.x 遷移至 Mongoose 8.x 時,您應該注意幾個向後不相容的變更。
如果您仍在使用 Mongoose 6.x 或更早的版本,請先閱讀Mongoose 6.x 至 7.x 遷移指南,並升級至 Mongoose 7.x,然後再升級至 Mongoose 8。
我們也建議您在升級至 Mongoose 8 之前,先檢閱 MongoDB Node.js 驅動程式 v6.0.0 的版本說明。
- 移除
findOneAndUpdate()
的rawResult
選項 Document.prototype.deleteOne()
現在會回傳一個查詢- MongoDB Node 驅動程式 6.0
- 移除
findOneAndRemove()
- 移除
count()
- 移除 id 設定器
null
對於非必要字串列舉值有效- 當
save()
更新現有文件時套用最小化 - 在區別器路徑之前套用基本綱要路徑
- 移除
findOneAndUpdate()
的overwrite
選項 - 變更具有
orFail()
和 upsert 的findOneAndUpdate()
行為 create()
等到所有儲存完成後才拋出任何錯誤Model.validate()
回傳物件的副本- 允許在 TypeScript 中將
null
用於選用欄位 - 在 TypeScript 中,模型建構函式屬性都是可選的
- 從綱要推斷
distinct()
回傳類型
移除 findOneAndUpdate()
的 rawResult
選項
findOneAndUpdate()
、findOneAndReplace()
和 findOneAndDelete()
的 rawResult
選項已被 includeResultMetadata
選項取代。
const filter = { name: 'Will Riker' };
const update = { age: 29 };
const res = await Character.findOneAndUpdate(filter, update, {
new: true,
upsert: true,
// Replace `rawResult: true` with `includeResultMetadata: true`
includeResultMetadata: true
});
Mongoose 8 中的 includeResultMetadata
行為與 rawResult
完全相同。
Document.prototype.deleteOne
現在會回傳一個查詢
在 Mongoose 7 中,doc.deleteOne()
會回傳一個解析為 doc
的 Promise。在 Mongoose 8 中,doc.deleteOne()
會回傳一個查詢,以便更輕鬆地進行鏈結,並與 doc.updateOne()
保持一致。
const numberOne = await Character.findOne({ name: 'Will Riker' });
// In Mongoose 7, q is a Promise that resolves to `numberOne`
// In Mongoose 8, q is a Query.
const q = numberOne.deleteOne();
// In Mongoose 7, `res === numberOne`
// In Mongoose 8, `res` is a `DeleteResult`.
const res = await q;
MongoDB Node 驅動程式 6
Mongoose 8 使用 MongoDB Node 驅動程式的 v6.x。MongoDB Node 驅動程式 v6 中有一些值得注意的變更會影響 Mongoose
ObjectId
建構函式不再接受長度為 12 的字串。在 Mongoose 7 中,new mongoose.Types.ObjectId('12charstring')
完全有效。在 Mongoose 8 中,new mongoose.Types.ObjectId('12charstring')
會拋出錯誤。已移除已棄用的 SSL 選項
sslCA
->tlsCAFile
sslCRL
->tlsCRLFile
sslCert
->tlsCertificateKeyFile
sslKey
->tlsCertificateKeyFile
sslPass
->tlsCertificateKeyFilePassword
sslValidate
->tlsAllowInvalidCertificates
tlsCertificateFile
->tlsCertificateKeyFile
移除 findOneAndRemove()
在 Mongoose 7 中,findOneAndRemove()
是 findOneAndDelete()
的別名,Mongoose 為了向後相容性而支援它。Mongoose 8 不再支援 findOneAndRemove()
。請改用 findOneAndDelete()
。
移除 count()
Mongoose 8 中移除了 Model.count()
和 Query.prototype.count()
。請改用 Model.countDocuments()
和 Query.prototype.countDocuments()
。
移除 id 設定器
在 Mongoose 7.4 中,Mongoose 引入了一個 id
設定器,使 doc.id = '0'.repeat(24)
等同於 doc._id = '0'.repeat(24)
。在 Mongoose 8 中,該設定器現在已移除。
null
對於非必要字串列舉值有效
在 Mongoose 8 之前,將具有 enum
的字串路徑設定為 null
會導致驗證錯誤,即使該路徑不是 required
。在 Mongoose 8 中,如果未設定 required
,即使具有 enum
,也可以將字串路徑設定為 null
。
const schema = new Schema({
status: {
type: String,
enum: ['on', 'off']
}
});
const Test = mongoose.model('Test', schema);
// Works fine in Mongoose 8
// Throws a `ValidationError` in Mongoose 7
await Test.create({ status: null });
當 save()
更新現有文件時套用最小化
在 Mongoose 7 中,Mongoose 只會在儲存新文件時套用最小化,而不會在更新現有文件時套用。
const schema = new Schema({
nested: {
field1: Number
}
});
const Test = mongoose.model('Test', schema);
// Both Mongoose 7 and Mongoose 8 strip out empty objects when saving
// a new document in MongoDB by default
const { _id } = await Test.create({ nested: {} });
let rawDoc = await Test.findById(_id).lean();
rawDoc.nested; // undefined
// Mongoose 8 will also strip out empty objects when saving an
// existing document in MongoDB
const doc = await Test.findById(_id);
doc.nested = {};
doc.markModified('nested');
await doc.save();
let rawDoc = await Test.findById(_id).lean();
rawDoc.nested; // undefined in Mongoose 8, {} in Mongoose 7
在區別器路徑之前套用基本綱要路徑
這表示,在 Mongoose 8 中,區別器路徑上的 getter 和 setter 會在基本路徑上的 getter 和 setter 之後執行。在 Mongoose 7 中,區別器路徑上的 getter 和 setter 會在基本路徑上的 getter 和 setter 之前執行。
const schema = new Schema({
name: {
type: String,
get(v) {
console.log('Base schema getter');
return v;
}
}
});
const Test = mongoose.model('Test', schema);
const D = Test.discriminator('D', new Schema({
otherProp: {
type: String,
get(v) {
console.log('Discriminator schema getter');
return v;
}
}
}));
const doc = new D({ name: 'test', otherProp: 'test' });
// In Mongoose 8, prints "Base schema getter" followed by "Discriminator schema getter"
// In Mongoose 7, prints "Discriminator schema getter" followed by "Base schema getter"
console.log(doc.toObject({ getters: true }));
移除 findOneAndUpdate()
的 overwrite
選項
Mongoose 7 及更早版本支援 findOneAndUpdate()
、updateOne()
和 update()
的 overwrite
選項。在 Mongoose 7 之前,overwrite
會跳過在 $set
中包裝 update
參數,這表示 findOneAndUpdate()
和 update()
會覆寫相符的文件。在 Mongoose 7 中,設定 overwrite
會將 findOneAndUpdate()
轉換為 findOneAndReplace()
,並將 updateOne()
轉換為 replaceOne()
,以保留向後相容性。
在 Mongoose 8 中,不再支援 overwrite
選項。如果您想要覆寫整個文件,請使用 findOneAndReplace()
或 replaceOne()
。
變更具有 orFail()
和 upsert 的 findOneAndUpdate()
行為
在 Mongoose 7 中,如果 upsert 了新文件,findOneAndUpdate(filter, update, { upsert: true }).orFail()
會拋出 DocumentNotFoundError
。換句話說,如果找不到任何文件,即使 upsert 了新文件,findOneAndUpdate().orFail()
也總是會拋出錯誤。
在 Mongoose 8 中,findOneAndUpdate(filter, update, { upsert: true }).orFail()
總是會成功。現在,如果沒有回傳任何文件,findOneAndUpdate().orFail()
就會拋出 DocumentNotFoundError
,而不是如果找不到任何文件。
Create 等到所有儲存完成後才拋出任何錯誤
在 Mongoose 7 中,如果任何 save()
預設拋出錯誤,create()
會立即拋出錯誤。Mongoose 8 則會等待所有 save()
呼叫完成後,再拋出第一個發生的錯誤。因此,create()
在 Mongoose 7 和 Mongoose 8 中都會拋出相同的錯誤,Mongoose 8 可能只是需要更長的時間才能拋出錯誤。
const schema = new Schema({
name: {
type: String,
enum: ['Badger', 'Mushroom']
}
});
schema.pre('save', async function() {
await new Promise(resolve => setTimeout(resolve, 1000));
});
const Test = mongoose.model('Test', schema);
const err = await Test.create([
{ name: 'Badger' },
{ name: 'Mushroom' },
{ name: 'Cow' }
]).then(() => null, err => err);
err; // ValidationError
// In Mongoose 7, there would be 0 documents, because `Test.create()`
// would throw before 'Badger' and 'Mushroom' are inserted
// In Mongoose 8, there will be 2 documents. `Test.create()` waits until
// 'Badger' and 'Mushroom' are inserted before throwing.
await Test.countDocuments();
Model.validate()
回傳物件的副本
在 Mongoose 7 中,Model.validate()
可能會修改傳入的物件。Mongoose 8 則會先複製傳入的物件。
const schema = new Schema({ answer: Number });
const Test = mongoose.model('Test', schema);
const obj = { answer: '42' };
const res = Test.validate(obj);
typeof obj.answer; // 'string' in Mongoose 8, 'number' in Mongoose 7
typeof res.answer; // 'number' in both Mongoose 7 and Mongoose 8
允許在 TypeScript 中將 null
用於選用欄位
在 Mongoose 8 中,TypeScript 中自動推斷的綱要類型允許可選欄位使用 null
。在 Mongoose 7 中,可選欄位只允許 undefined
,不允許 null
。
const schema = new Schema({ name: String });
const TestModel = model('Test', schema);
const doc = new TestModel();
// In Mongoose 8, this type is `string | null | undefined`.
// In Mongoose 7, this type is `string | undefined`
doc.name;
在 TypeScript 中,模型建構函式屬性都是可選的
在 Mongoose 8 中,預設情況下,模型建構函式上不需要任何屬性。
import {Schema, model, Model} from 'mongoose';
interface IDocument {
name: string;
createdAt: Date;
updatedAt: Date;
}
const documentSchema = new Schema<IDocument>(
{ name: { type: String, required: true } },
{ timestamps: true }
);
const TestModel = model<IDocument>('Document', documentSchema);
// Would throw a compile error in Mongoose 7, compiles in Mongoose 8
const newDoc = new TestModel({
name: 'Foo'
});
// Explicitly pass generic param to constructor to specify the expected
// type of the model constructor param. The following will cause TS
// to complain about missing `createdAt` and `updatedAt` in Mongoose 8.
const newDoc2 = new TestModel<IDocument>({
name: 'Foo'
});
從綱要推斷 distinct()
回傳類型
interface User {
name: string;
email: string;
avatar?: string;
}
const schema = new Schema<User>({
name: { type: String, required: true },
email: { type: String, required: true },
avatar: String
});
// Works in Mongoose 8. Compile error in Mongoose 7.
const names: string[] = await MyModel.distinct('name');