从 5.x 迁移到 6.x
请注意:我们计划在 2024 年 3 月 1 日停止对 Mongoose 5 的支持。请查看我们的 版本支持指南.
从 Mongoose 5.x 迁移到 Mongoose 6.x 时,您应该注意几个 向后不兼容的更改。
如果您仍然使用 Mongoose 4.x,请阅读 Mongoose 4.x 到 5.x 迁移指南 并先升级到 Mongoose 5.x。
- 版本要求
- MongoDB 驱动程序 4.0
- 不再有弃用警告选项
- 连接的
asPromise()
方法 mongoose.connect()
返回一个 Promise- 重复查询执行
Model.exists()
返回一个精简文档而不是布尔值strictQuery
现在默认等于strict
- MongoError 现在是 MongoServerError
- 简化的
isValidObjectId()
和独立的isObjectIdOrHexString()
- 默认情况下克隆鉴别器模式
- 模式定义的文档键顺序
sanitizeFilter
和trusted()
- 删除
omitUndefined
:Mongoose 现在在更新中删除undefined
键,而不是将它们设置为null
- 默认函数的文档参数
- 数组是代理
typePojoToMixed
strictPopulate()
- 子文档
ref
函数上下文 - 模式保留名称警告
- 子文档路径
- 创建聚合游标
autoCreate
默认设置为true
- 不再有
context: 'query'
- 使用填充路径的自定义验证器
- 副本集的断开连接事件
- 删除
execPopulate()
- 带有空数组的
create()
- 删除嵌套路径合并
- ObjectId
valueOf()
- 不可变的
createdAt
- 删除验证器
isAsync
- 删除
safe
- SchemaType
set
参数现在使用priorValue
作为第二个参数,而不是self
Query.prototype.populate()
没有默认模型toObject()
和toJSON()
使用嵌套模式minimize
- TypeScript 更改
- 删除
reconnectTries
和reconnectInterval
选项 - MongoDB 驱动的新的 URL 解析器与某些 npm 包不兼容
版本要求
Mongoose 现在需要 Node.js >= 12.0.0。Mongoose 仍然支持从 3.0.0 开始的 MongoDB 服务器版本。
MongoDB 驱动程序 4.0
Mongoose 现在使用 MongoDB Node 驱动程序 的 v4.x 版本。请参阅 MongoDB Node 驱动的迁移指南 以获取详细的信息。以下是其中一些最值得注意的更改
- MongoDB 驱动程序 4.x 是用 TypeScript 编写的,并有自己的 TypeScript 类型定义。这些可能会与
@types/mongodb
冲突,因此如果您遇到 TypeScript 编译器错误,请确保您升级到@types/mongodb
的最新版本,它是一个空存根。 - 连接的
poolSize
选项已被minPoolSize
和maxPoolSize
替换。Mongoose 5.x 的poolSize
选项等效于 Mongoose 6 的maxPoolSize
选项。maxPoolSize
的默认值已增加到 100。 updateOne()
和updateMany()
的结果现在不同。deleteOne()
和deleteMany()
的结果不再具有n
属性。
const res = await TestModel.updateMany({}, { someProperty: 'someValue' });
res.matchedCount; // Number of documents that were found that match the filter. Replaces `res.n`
res.modifiedCount; // Number of documents modified. Replaces `res.nModified`
res.upsertedCount; // Number of documents upserted. Replaces `res.upserted`
const res = await TestModel.deleteMany({});
// In Mongoose 6: `{ acknowledged: true, deletedCount: 2 }`
// In Mongoose 5: `{ n: 2, ok: 1, deletedCount: 2 }`
res;
res.deletedCount; // Number of documents that were deleted. Replaces `res.n`
不再有弃用警告选项
useNewUrlParser
、useUnifiedTopology
、useFindAndModify
和 useCreateIndex
不再是支持的选项。Mongoose 6 始终表现得好像 useNewUrlParser
、useUnifiedTopology
和 useCreateIndex
为 true
,而 useFindAndModify
为 false
。请从您的代码中删除这些选项。
// No longer necessary:
mongoose.set('useFindAndModify', false);
await mongoose.connect('mongodb://127.0.0.1:27017/test', {
useNewUrlParser: true, // <-- no longer necessary
useUnifiedTopology: true // <-- no longer necessary
});
连接的 asPromise()
方法
Mongoose 连接不再是 thenable。这意味着 await mongoose.createConnection(uri)
**不再等待 Mongoose 连接**。请改用 mongoose.createConnection(uri).asPromise()
。请参阅 #8810.
// The below no longer works in Mongoose 6
await mongoose.createConnection(uri);
// Do this instead
await mongoose.createConnection(uri).asPromise();
mongoose.connect()
返回一个 Promise
mongoose.connect()
函数现在始终返回一个 promise,**而不是** Mongoose 实例。
重复查询执行
Mongoose 不再允许两次执行相同的查询对象。如果您这样做,您将收到 Query was already executed
错误。两次执行同一个查询实例通常表明混合了回调和 promise,但如果您需要两次执行同一个查询,您可以调用 Query#clone()
来克隆查询并重新执行它。请参阅 gh-7398
// Results in 'Query was already executed' error, because technically this `find()` query executes twice.
await Model.find({}, function(err, result) {});
const q = Model.find();
await q;
await q.clone(); // Can `clone()` the query to allow executing the query again
Model.exists(...)
现在返回一个精简文档而不是布尔值
// in Mongoose 5.x, `existingUser` used to be a boolean
// now `existingUser` will be either `{ _id: ObjectId(...) }` or `null`.
const existingUser = await User.exists({ name: 'John' });
if (existingUser) {
console.log(existingUser._id);
}
strictQuery
现在默认等于 strict
Mongoose 不再支持 从 Mongoose 6.0.10 开始,我们恢复了 strictQuery
选项。您现在必须使用 strict
。strictQuery
选项。但是,strictQuery
默认情况下与 strict
绑定。这意味着,默认情况下,Mongoose 会过滤掉不在模式中的查询过滤器属性。
const userSchema = new Schema({ name: String });
const User = mongoose.model('User', userSchema);
// By default, this is equivalent to `User.find()` because Mongoose filters out `notInSchema`
await User.find({ notInSchema: 1 });
// Set `strictQuery: false` to opt in to filtering by properties that aren't in the schema
await User.find({ notInSchema: 1 }, null, { strictQuery: false });
// equivalent:
await User.find({ notInSchema: 1 }).setOptions({ strictQuery: false });
您也可以全局禁用 strictQuery
来覆盖
mongoose.set('strictQuery', false);
MongoError 现在是 MongoServerError
在 MongoDB Node.js Driver v4.x 中,'MongoError' 现在是 'MongoServerError'。请更改任何依赖于硬编码字符串 'MongoError' 的代码。
默认情况下克隆鉴别器模式
Mongoose 现在默认情况下克隆鉴别器模式。这意味着如果您使用递归嵌入的鉴别器,则需要将 { clone: false }
传递给 discriminator()
。
// In Mongoose 6, these two are equivalent:
User.discriminator('author', authorSchema);
User.discriminator('author', authorSchema.clone());
// To opt out if `clone()` is causing issues, pass `clone: false`
User.discriminator('author', authorSchema, { clone: false });
简化的 isValidObjectId()
和独立的 isObjectIdOrHexString()
在 Mongoose 5 中,mongoose.isValidObjectId()
对数字等值返回 false
,这与 MongoDB 驱动程序的 ObjectId.isValid()
函数不一致。从技术上讲,任何 JavaScript 数字都可以转换为 MongoDB ObjectId。
在 Mongoose 6 中,mongoose.isValidObjectId()
只是一个包装器,用于实现与 mongoose.Types.ObjectId.isValid()
的一致性。
Mongoose 6.2.5 现在包含一个 mongoose.isObjectIdOrHexString()
函数,它更好地捕获了 isValidObjectId()
的更常见用例:给定值是 ObjectId
实例还是表示 ObjectId
的 24 个字符的十六进制字符串?
// `isValidObjectId()` returns `true` for some surprising values, because these
// values are _technically_ ObjectId representations
mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true
mongoose.isValidObjectId('0123456789ab'); // true
mongoose.isValidObjectId(6); // true
mongoose.isValidObjectId(new User({ name: 'test' })); // true
// `isObjectIdOrHexString()` instead only returns `true` for ObjectIds and 24
// character hex strings.
mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId()); // true
mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f'); // true
mongoose.isObjectIdOrHexString('0123456789ab'); // false
mongoose.isObjectIdOrHexString(6); // false
模式定义的文档键顺序
Mongoose 现在以在模式中指定键的顺序保存具有键的对象,而不是以用户定义的对象中的顺序保存。因此,Object.keys(new User({ name: String, email: String }).toObject()
是 ['name', 'email']
还是 ['email', 'name']
取决于在模式中定义 name
和 email
的顺序。
const schema = new Schema({
profile: {
name: {
first: String,
last: String
}
}
});
const Test = db.model('Test', schema);
const doc = new Test({
profile: { name: { last: 'Musashi', first: 'Miyamoto' } }
});
// Note that 'first' comes before 'last', even though the argument to `new Test()` flips the key order.
// Mongoose uses the schema's key order, not the provided objects' key order.
assert.deepEqual(Object.keys(doc.toObject().profile.name), ['first', 'last']);
sanitizeFilter
和 trusted()
Mongoose 6 引入了一个新的 sanitizeFilter
选项,用于防御 查询选择器注入攻击。如果启用 sanitizeFilter
,Mongoose 将在查询过滤器中的任何对象中包装一个 $eq
// Mongoose will convert this filter into `{ username: 'val', pwd: { $eq: { $ne: null } } }`, preventing
// a query selector injection.
await Test.find({ username: 'val', pwd: { $ne: null } }).setOptions({ sanitizeFilter: true });
要显式允许查询选择器,请使用 mongoose.trusted()
// `mongoose.trusted()` allows query selectors through
await Test.find({ username: 'val', pwd: mongoose.trusted({ $ne: null }) }).setOptions({ sanitizeFilter: true });
删除 omitUndefined
:Mongoose 现在在更新中删除 undefined
键,而不是将它们设置为 null
在 Mongoose 5.x 中,在更新操作中将键设置为 undefined
等同于将其设置为 null
。
let res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
res.name; // `null` in Mongoose 5.x
// Equivalent to `findOneAndUpdate({}, {}, { new: true })` because `omitUndefined` will
// remove `name: undefined`
res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true, omitUndefined: true });
Mongoose 5.x 支持一个 omitUndefined
选项来删除 undefined
键。在 Mongoose 6.x 中,omitUndefined
选项已被删除,Mongoose 将始终删除未定义的键。
// In Mongoose 6, equivalent to `findOneAndUpdate({}, {}, { new: true })` because Mongoose will
// remove `name: undefined`
const res = await Test.findOneAndUpdate({}, { $set: { name: undefined } }, { new: true });
唯一的解决方法是在更新中显式将属性设置为 null
const res = await Test.findOneAndUpdate({}, { $set: { name: null } }, { new: true });
默认函数的文档参数
Mongoose 现在将文档作为第一个参数传递给 default
函数,这对于使用 箭头函数 以及默认值很有帮助。
如果您传递一个期望不同参数的函数到 default
,这可能会影响您,例如 default: mongoose.Types.ObjectId
。请参阅 gh-9633。如果您传递的默认函数**没有**使用文档,请将 default: myFunction
更改为 default: () => myFunction()
,以避免意外地传递可能改变行为的参数。
const schema = new Schema({
name: String,
age: Number,
canVote: {
type: Boolean,
// Default functions now receive a `doc` parameter, helpful for arrow functions
default: doc => doc.age >= 18
}
});
数组是代理
Mongoose 数组现在是 ES6 代理。您不再需要在直接设置数组索引后 markModified()
。
const post = await BlogPost.findOne();
post.tags[0] = 'javascript';
await post.save(); // Works, no need for `markModified()`!
typePojoToMixed
使用 type: { name: String }
声明的模式路径在 Mongoose 6 中成为单个嵌套子文档,而不是 Mongoose 5 中的 Mixed。这消除了对 typePojoToMixed
选项的需求。请参阅 gh-7181.
// In Mongoose 6, the below makes `foo` into a subdocument with a `name` property.
// In Mongoose 5, the below would make `foo` a `Mixed` type, _unless_ you set `typePojoToMixed: false`.
const schema = new Schema({
foo: { type: { name: String } }
});
strictPopulate()
如果对模式中未定义的路径执行 populate()
,Mongoose 现在会抛出错误。这仅适用于我们可以推断本地模式的情况,例如当您使用 Query#populate()
时,**而不是**当您在 POJO 上调用 Model.populate()
时。请参阅 gh-5124.
子文档 ref
函数上下文
使用函数 ref
或 refPath
填充子文档时,this
现在是正在填充的子文档,而不是顶层文档。请参阅 #8469.
const schema = new Schema({
works: [{
modelId: String,
data: {
type: mongoose.ObjectId,
ref: function(doc) {
// In Mongoose 6, `doc` is the array element, so you can access `modelId`.
// In Mongoose 5, `doc` was the top-level document.
return doc.modelId;
}
}
}]
});
模式保留名称警告
使用 save
、isNew
和其他 Mongoose 保留名称作为模式路径名称现在会触发警告,而不是错误。您可以通过在模式选项中设置 suppressReservedKeysWarning
来抑制警告:new Schema({ save: String }, { suppressReservedKeysWarning: true })
。请记住,这可能会破坏依赖于这些保留名称的插件。
子文档路径
单个嵌套子文档已重命名为“子文档路径”。因此,SchemaSingleNestedOptions
现在是 SchemaSubdocumentOptions
,而 mongoose.Schema.Types.Embedded
现在是 mongoose.Schema.Types.Subdocument
。请参阅 gh-10419
创建聚合游标
Aggregate#cursor()
现在返回一个 AggregationCursor 实例,以与 Query#cursor()
保持一致。您不再需要执行 Model.aggregate(pipeline).cursor().exec()
来获取聚合游标,只需执行 Model.aggregate(pipeline).cursor()
即可。
autoCreate
默认设置为 true
autoCreate
默认情况下为 true
,**除非** readPreference 为 secondary 或 secondaryPreferred,这意味着 Mongoose 将尝试在创建索引之前创建每个模型的底层集合。如果 readPreference 为 secondary 或 secondaryPreferred,Mongoose 将默认情况下将 autoCreate
和 autoIndex
设置为 false
,因为当连接到辅助服务器时,createCollection()
和 createIndex()
都会失败。
不再有 context: 'query'
查询的 context
选项已被删除。现在,Mongoose 始终使用 context = 'query'
。
使用填充路径的自定义验证器
Mongoose 6 始终使用已取消填充的路径调用验证器(即使用 ID 而不是文档本身)。在 Mongoose 5 中,如果路径已填充,Mongoose 将使用填充的文档调用验证器。请参阅 #8042
副本集的断开连接事件
连接到副本集时,连接现在在丢失到主服务器的连接时发出 'disconnected' 事件。在 Mongoose 5 中,连接仅在丢失到副本集的所有成员的连接时发出 'disconnected' 事件。
但是,Mongoose 6 **不会**在连接断开时缓冲命令。因此,您仍然可以成功执行命令,例如使用 readPreference = 'secondary'
的查询,即使 Mongoose 连接处于断开状态。
删除 execPopulate()
Document#populate()
现在返回一个 promise,并且不再是可链接的。
将
await doc.populate('path1').populate('path2').execPopulate();
替换为await doc.populate(['path1', 'path2']);
将
await doc.populate('path1', 'select1').populate('path2', 'select2').execPopulate();
替换为await doc.populate([{path: 'path1', select: 'select1'}, {path: 'path2', select: 'select2'}]);
带有空数组的 create()
在 v6.0 中,await Model.create([])
在提供空数组时返回空数组,在 v5.0 中它曾经返回 undefined
。如果你的任何代码正在检查输出是否为 undefined
,你需要修改它,假设 await Model.create(...)
在提供数组时始终返回数组。
删除嵌套路径合并
doc.set({ child: { age: 21 } })
现在无论 child
是嵌套路径还是子文档,都具有相同的操作方式:Mongoose 将覆盖 child
的值。在 Mongoose 5 中,如果 child
是嵌套路径,此操作将合并 child
。
ObjectId valueOf()
Mongoose 现在为 ObjectId 添加了 valueOf()
函数。这意味着你现在可以使用 ==
将 ObjectId 与字符串进行比较。
const a = ObjectId('6143b55ac9a762738b15d4f0');
a == '6143b55ac9a762738b15d4f0'; // true
不可变的 createdAt
如果你设置了 timestamps: true
,Mongoose 现在将使 createdAt
属性 不可变
。参见 gh-10139
删除验证器 isAsync
isAsync
不再是 validate
的选项。请改用 async function
。
删除 safe
safe
不再是模式、查询或 save()
的选项。请改用 writeConcern
。
SchemaType set
参数
Mongoose 现在使用 priorValue
作为第二个参数调用 setter 函数,而不是在 Mongoose 5 中使用 schemaType
。
const userSchema = new Schema({
name: {
type: String,
trimStart: true,
set: trimStartSetter
}
});
// in v5.x the parameters were (value, schemaType), in v6.x the parameters are (value, priorValue, schemaType).
function trimStartSetter(val, priorValue, schemaType) {
if (schemaType.options.trimStart && typeof val === 'string') {
return val.trimStart();
}
return val;
}
const User = mongoose.model('User', userSchema);
const user = new User({ name: 'Robert Martin' });
console.log(user.name); // 'robert martin'
toObject()
和 toJSON()
使用嵌套模式 minimize
此更改在技术上已在 5.10.5 版本中发布,但 导致用户从 5.9.x 迁移到 6.x 时出现问题。在 Mongoose < 5.10.5
中,toObject()
和 toJSON()
默认情况下会使用顶层模式的 minimize
选项。
const child = new Schema({ thing: Schema.Types.Mixed });
const parent = new Schema({ child }, { minimize: false });
const Parent = model('Parent', parent);
const p = new Parent({ child: { thing: {} } });
// In v5.10.4, would contain `child.thing` because `toObject()` uses `parent` schema's `minimize` option
// In `>= 5.10.5`, `child.thing` is omitted because `child` schema has `minimize: true`
console.log(p.toObject());
作为解决方法,你可以显式地将 minimize
传递给 toObject()
或 toJSON()
console.log(p.toObject({ minimize: false }));
或者在行内定义 child
模式(仅限 Mongoose 6)以继承父级的 minimize
选项。
const parent = new Schema({
// Implicitly creates a new schema with the top-level schema's `minimize` option.
child: { type: { thing: Schema.Types.Mixed } }
}, { minimize: false });
Query.prototype.populate()
没有默认模型
在 Mongoose 5 中,在没有 ref
的混合类型或其他路径上调用 populate()
将回退到使用查询的模型。
const testSchema = new mongoose.Schema({
data: String,
parents: Array // Array of mixed
});
const Test = mongoose.model('Test', testSchema);
// The below `populate()`...
await Test.findOne().populate('parents');
// Is a shorthand for the following populate in Mongoose 5
await Test.findOne().populate({ path: 'parents', model: Test });
在 Mongoose 6 中,填充没有 ref
、refPath
或 model
的路径是无操作的。
// The below `populate()` does nothing.
await Test.findOne().populate('parents');
MongoDB 驱动的新的 URL 解析器与某些 npm 包不兼容
Mongoose 6 使用的 MongoDB Node 驱动程序版本依赖于 URL 解析器模块,该模块与其他 npm 包存在一些已知兼容性问题。如果使用这些不兼容的包,可能会导致错误,例如 Invalid URL: mongodb+srv://username:[email protected]/abc
。 你可以在此处找到不兼容包的列表.
TypeScript 更改
Schema
类现在采用 3 个泛型参数,而不是 4 个。第三个泛型参数 SchemaDefinitionType
现在与第一个泛型参数 DocType
相同。将 new Schema<UserDocument, UserModel, User>(schemaDefinition)
替换为 new Schema<UserDocument, UserModel>(schemaDefinition)
Types.ObjectId
现在是一个类,这意味着使用 new mongoose.Types.ObjectId()
创建新的 ObjectId 时,你不能再省略 new
。目前,你仍然可以在 JavaScript 中省略 new
,但在 TypeScript 中你 **必须** 添加 new
。
以下遗留类型已被删除
ModelUpdateOptions
DocumentQuery
HookSyncCallback
HookAsyncCallback
HookErrorCallback
HookNextFunction
HookDoneFunction
SchemaTypeOpts
ConnectionOptions
Mongoose 6 为虚拟 getter 和 setter 中的 this
推断文档的类型。在 Mongoose 5.x 中,this
在以下代码中将是 any
。
schema.virtual('myVirtual').get(function() {
this; // any in Mongoose 5.x
});
在 Mongoose 6 中,this
将被设置为文档类型。
const schema = new Schema({ name: String });
schema.virtual('myVirtual').get(function() {
this.name; // string
});
删除 reconnectTries
和 reconnectInterval
选项
reconnectTries
和 reconnectInterval
选项已被删除,因为它们不再必要。
MongoDB 节点驱动程序将始终尝试重试任何操作,最多可重试 serverSelectionTimeoutMS
次,即使 MongoDB 停机时间很长。因此,它永远不会耗尽重试次数或尝试重新连接到 MongoDB。