从 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
选项 - 更改了
findOneAndUpdate()
使用orFail()
和 upsert 的行为 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()
Model.count()
和 Query.prototype.count()
已在 Mongoose 8 中删除。请改用 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 中,即使设置了 enum
,如果未设置 required
,将字符串路径设置为 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
会跳过将 update
参数包装在 $set
中,这意味着 findOneAndUpdate()
和 update()
会覆盖匹配的文档。在 Mongoose 7 中,设置 overwrite
会将 findOneAndUpdate()
转换为 findOneAndReplace()
,将 updateOne()
转换为 replaceOne()
,以保持向后兼容性。
在 Mongoose 8 中,不再支持 overwrite
选项。如果您想覆盖整个文档,请使用 findOneAndReplace()
或 replaceOne()
。
更改了 findOneAndUpdate()
使用 orFail()
和 upsert 的行为
在 Mongoose 7 中,findOneAndUpdate(filter, update, { upsert: true }).orFail()
如果插入新文档,则会抛出 DocumentNotFoundError
。换句话说,即使插入了新文档,findOneAndUpdate().orFail()
也始终会抛出错误,因为没有找到文档。
在 Mongoose 8 中,findOneAndUpdate(filter, update, { upsert: true }).orFail()
始终成功。findOneAndUpdate().orFail()
现在在没有返回文档时抛出 DocumentNotFoundError
,而不是在没有找到文档时。
Create 等待所有保存完成,然后再抛出任何错误
在 Mongoose 7 中,create()
如果任何 save()
抛出错误,则会立即抛出错误(默认情况下)。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');