从 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 - 默认函数的文档参数
- 数组是代理
typePojoToMixedstrictPopulate()- 子文档
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:password@development.xyz.mongodb.net/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。
以下遗留类型已被删除
ModelUpdateOptionsDocumentQueryHookSyncCallbackHookAsyncCallbackHookErrorCallbackHookNextFunctionHookDoneFunctionSchemaTypeOptsConnectionOptions
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。

