从 6.x 迁移到 7.x
从 Mongoose 6.x 迁移到 Mongoose 7.x 时,您应该注意一些向后不兼容的更改。
如果您仍然使用 Mongoose 5.x,请阅读 Mongoose 5.x 到 6.x 迁移指南 并在升级到 Mongoose 6.x 后再进行操作。
strictQuery
- 移除
remove()
- 删除回调支持
- 移除
update()
- ObjectId 需要
new
id
设置器- 鉴别器模式默认使用基本模式选项
- 移除
castForQueryWrapper()
,更新castForQuery()
签名 - 在
Schema.prototype.add()
中复制模式选项 - ObjectId bsontype 现在使用小写 d
- 删除对自定义 Promise 库的支持
- 删除 mapReduce
- TypeScript 特定更改
strictQuery
strictQuery
现在默认为 false。
const mySchema = new Schema({ field: Number });
const MyModel = mongoose.model('Test', mySchema);
// Mongoose will not strip out `notInSchema: 1` because `strictQuery` is false by default
const docs = await MyModel.find({ notInSchema: 1 });
// Empty array in Mongoose 7. In Mongoose 6, this would contain all documents in MyModel
docs;
移除 remove()
文档和模型上的 remove()
方法已被移除。请改用 deleteOne()
或 deleteMany()
。
const mySchema = new Schema({ field: Number });
const MyModel = mongoose.model('Test', mySchema);
// Change this:
await MyModel.remove(filter);
// To this:
await MyModel.deleteOne(filter);
// Or this, if you want to delete multiple:
await MyModel.deleteMany(filter);
// For documents, change this:
await doc.remove();
// To this:
await doc.deleteOne();
请记住,deleteOne()
钩子默认被视为查询中间件。因此,对于中间件,请执行以下操作
// Replace this:
schema.pre('remove', function() {
/* ... */
});
// With this:
schema.pre('deleteOne', { document: true, query: false }, function() {
/* ... */
});
删除回调支持
以下函数不再接受回调。它们始终返回 Promise。
Aggregate.prototype.exec
Aggregate.prototype.explain
AggregationCursor.prototype.close
AggregationCursor.prototype.next
AggregationCursor.prototype.eachAsync
Connection.prototype.startSession
Connection.prototype.dropCollection
Connection.prototype.createCollection
Connection.prototype.dropDatabase
Connection.prototype.openUri
Connection.prototype.close
Connection.prototype.destroy
Document.prototype.populate
Document.prototype.validate
Mongoose.prototype.connect
Mongoose.prototype.createConnection
Model.prototype.save
Model.aggregate
Model.bulkWrite
Model.cleanIndexes
Model.countDocuments
Model.create
Model.createCollection
Model.createIndexes
Model.deleteOne
Model.deleteMany
Model.distinct
Model.ensureIndexes
Model.estimatedDocumentCount
Model.exists
Model.find
Model.findById
Model.findByIdAndUpdate
Model.findByIdAndReplace
Model.findOne
Model.findOneAndDelete
Model.findOneAndUpdate
Model.findOneAndRemove
Model.insertMany
Model.listIndexes
Model.replaceOne
Model.syncIndexes
Model.updateMany
Model.updateOne
Query.prototype.find
Query.prototype.findOne
Query.prototype.findOneAndDelete
Query.prototype.findOneAndUpdate
Query.prototype.findOneAndRemove
Query.prototype.findOneAndReplace
Query.prototype.validate
Query.prototype.deleteOne
Query.prototype.deleteMany
Query.prototype.exec
QueryCursor.prototype.close
QueryCursor.prototype.next
QueryCursor.prototype.eachAsync
如果您使用回调来使用上述函数,我们建议您切换到 async/await 或 Promise(如果 async 函数不适合您)。如果您需要帮助重构遗留代码库,请使用 ChatGPT 从 Mastering JS 回调到 async await 的此工具。
// Before
conn.startSession(function(err, session) {
// ...
});
// After
const session = await conn.startSession();
// Or:
conn.startSession().then(sesson => { /* ... */ });
// With error handling
try {
await conn.startSession();
} catch (err) { /* ... */ }
// Or:
const [err, session] = await conn.startSession().then(
session => ([null, session]),
err => ([err, null])
);
移除 update()
Model.update()
、Query.prototype.update()
和 Document.prototype.update()
已被移除。请改用 updateOne()
。
// Before
await Model.update(filter, update);
await doc.update(update);
// After
await Model.updateOne(filter, update);
await doc.updateOne(update);
ObjectId 需要 new
在 Mongoose 6 和更早版本中,您可以定义一个新的 ObjectId,而无需使用 new
关键字
// Works in Mongoose 6
// Throws "Class constructor ObjectId cannot be invoked without 'new'" in Mongoose 7
const oid = mongoose.Types.ObjectId('0'.repeat(24));
在 Mongoose 7 中,ObjectId
现在是一个 JavaScript 类,因此您需要使用 new
关键字。
// Works in Mongoose 6 and Mongoose 7
const oid = new mongoose.Types.ObjectId('0'.repeat(24));
id
设置器
从 Mongoose 7.4 开始,Mongoose 的内置 id
虚拟(将文档的 _id
存储为字符串)有一个设置器,它允许通过 id
修改文档的 _id
属性。
const doc = await TestModel.findOne();
doc.id = '000000000000000000000000';
doc._id; // ObjectId('000000000000000000000000')
如果您创建 new TestModel(obj)
,其中 obj
包含 id
和 _id
,或者如果您使用 doc.set()
,这会导致意想不到的行为。
// Because `id` is after `_id`, the `id` will overwrite the `_id`
const doc = new TestModel({
_id: '000000000000000000000000',
id: '111111111111111111111111'
});
doc._id; // ObjectId('111111111111111111111111')
id
设置器在 Mongoose 8 中被移除,原因是兼容性问题。
鉴别器模式默认使用基本模式选项
当您使用 Model.discriminator()
时,Mongoose 现在默认将使用鉴别器基本模式的选项。这意味着您无需显式设置子模式选项以匹配基本模式的选项。
const baseSchema = Schema({}, { typeKey: '$type' });
const Base = db.model('Base', baseSchema);
// In Mongoose 6.x, the `Base.discriminator()` call would throw because
// no `typeKey` option. In Mongoose 7, Mongoose uses the base schema's
// `typeKey` by default.
const childSchema = new Schema({}, {});
const Test = Base.discriminator('Child', childSchema);
Test.schema.options.typeKey; // '$type'
移除 castForQueryWrapper
,更新 castForQuery()
签名
Mongoose 现在始终使用 3 个参数调用 SchemaType castForQuery()
方法:$conditional
、value
和 context
。如果您已实现一个自定义模式类型,它定义了自己的 castForQuery()
方法,则需要按如下方式更新该方法。
// Mongoose 6.x format:
MySchemaType.prototype.castForQuery = function($conditional, value) {
if (arguments.length === 2) {
// Handle casting value with `$conditional` - $eq, $in, $not, etc.
} else {
value = $conditional;
// Handle casting `value` with no conditional
}
};
// Mongoose 7.x format
MySchemaType.prototype.castForQuery = function($conditional, value, context) {
if ($conditional != null) {
// Handle casting value with `$conditional` - $eq, $in, $not, etc.
} else {
// Handle casting `value` with no conditional
}
};
在 Schema.prototype.add()
中复制模式选项
Mongoose 现在在将一个模式添加到另一个模式时复制用户定义的模式选项。例如,下面的 childSchema
将获得 baseSchema
的 id
和 toJSON
选项。
const baseSchema = new Schema({ created: Date }, { id: true, toJSON: { virtuals: true } });
const childSchema = new Schema([baseSchema, { name: String }]);
childSchema.options.toJSON; // { virtuals: true } in Mongoose 7. undefined in Mongoose 6.
这适用于使用模式数组创建新模式以及调用 add()
的情况,如下所示。
childSchema.add(new Schema({}, { toObject: { virtuals: true } }));
childSchema.options.toObject; // { virtuals: true } in Mongoose 7. undefined in Mongoose 6.
ObjectId bsontype 现在使用小写 d
ObjectId 上的内部 _bsontype
属性在 Mongoose 7 中等于 'ObjectId'
,而在 Mongoose 6 中等于 'ObjectID'
。
const oid = new mongoose.Types.ObjectId();
oid._bsontype; // 'ObjectId' in Mongoose 7, 'ObjectID' in older versions of Mongoose
请更新任何您使用 _bsontype
来检查对象是否是 ObjectId 的地方。这也会影响使用 Mongoose 的库。
删除 mapReduce
MongoDB 不再支持 mapReduce
,因此 Mongoose 7 不再具有 Model.mapReduce()
函数。请使用聚合框架替换 mapReduce()
。
// The following no longer works in Mongoose 7.
const o = {
map: function() {
emit(this.author, 1);
},
reduce: function(k, vals) {
return vals.length;
}
};
await MR.mapReduce(o);
删除对自定义 Promise 库的支持
Mongoose 7 不再支持插入自定义 Promise 库。因此,以下操作在 Mongoose 7 中不再使 Mongoose 返回 Bluebird Promise。
const mongoose = require('mongoose');
// No-op on Mongoose 7
mongoose.Promise = require('bluebird');
如果您想全局使用 Bluebird 作为所有 Promise,您可以执行以下操作
global.Promise = require('bluebird');
TypeScript 特定更改
删除 LeanDocument
和对 extends Document
的支持
Mongoose 7 不再导出 LeanDocument
类型,也不再支持将 extends Document
的文档类型传递到 Model<>
中。
// No longer supported
interface ITest extends Document {
name?: string;
}
const Test = model<ITest>('Test', schema);
// Do this instead, no `extends Document`
interface ITest {
name?: string;
}
const Test = model<ITest>('Test', schema);
// If you need to access the hydrated document type, use the following code
type TestDocument = ReturnType<(typeof Test)['hydrate']>;
HydratedDocument
的新参数
Mongoose 的 HydratedDocument
类型将原始文档接口转换为已水化的 Mongoose 文档的类型,包括虚拟、方法等。在 Mongoose 7 中,HydratedDocument
的泛型参数已更改。在 Mongoose 6 中,泛型参数为
type HydratedDocument<
DocType,
TMethodsAndOverrides = {},
TVirtuals = {}
> = Document<unknown, any, DocType> &
Require_id<DocType> &
TMethodsAndOverrides &
TVirtuals;
在 Mongoose 7 中,新的类型如下所示。
type HydratedDocument<
DocType,
TOverrides = {},
TQueryHelpers = {}
> = Document<unknown, TQueryHelpers, DocType> &
Require_id<DocType> &
TOverrides;
在 Mongoose 7 中,第一个参数是原始文档接口,第二个参数是任何文档特定覆盖(通常是虚拟和方法),第三个参数是与文档模型关联的任何查询帮助程序。
关键区别在于,在 Mongoose 6 中,第三个泛型参数是文档的虚拟。在 Mongoose 7 中,第三个泛型参数是文档的查询帮助程序。
// Mongoose 6 version:
type UserDocument = HydratedDocument<TUser, TUserMethods, TUserVirtuals>;
// Mongoose 7:
type UserDocument = HydratedDocument<TUser, TUserMethods & TUserVirtuals, TUserQueryHelpers>;