从 6.x 迁移到 7.x
¥Migrating from 6.x to 7.x
从 Mongoose 6.x 迁移到 Mongoose 7.x 时,你应该注意一些向后破坏的更改。
¥There are several backwards-breaking changes you should be aware of when migrating from Mongoose 6.x to Mongoose 7.x.
如果你仍在使用 Mongoose 5.x,请先阅读 Mongoose 5.x 到 6.x 迁移指南 并升级到 Mongoose 6.x。
¥If you're still on Mongoose 5.x, please read the Mongoose 5.x to 6.x migration guide and upgrade to Mongoose 6.x first.
删除了
castForQueryWrapper()
,更新了castForQuery()
签名¥Removed
castForQueryWrapper()
, updatedcastForQuery()
signature
strictQuery
strictQuery
现在默认为 false。
¥strictQuery
is now false by default.
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()
¥Removed remove()
文档和模型上的 remove()
方法已被删除。请改用 deleteOne()
或 deleteMany()
。
¥The remove()
method on documents and models has been removed.
Use deleteOne()
or deleteMany()
instead.
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()
钩子默认被视为查询中间件。因此对于中间件,请执行以下操作:
¥Keep in mind that deleteOne()
hooks are treated as query middleware by default.
So for middleware, please do the following:
// Replace this:
schema.pre('remove', function() {
/* ... */
});
// With this:
schema.pre('deleteOne', { document: true, query: false }, function() {
/* ... */
});
放弃回调支持
¥Dropped callback support
以下函数不再接受回调。他们总是返回 promise。
¥The following functions no longer accept callbacks. They always return promises.
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。如果你需要重构遗留代码库的帮助,请使用 ChatGPT 这个工具从掌握 JS 回调到异步等待。
¥If you are using the above functions with callbacks, we recommend switching to async/await, or promises if async functions don't work for you. If you need help refactoring a legacy codebase, this tool from Mastering JS callbacks to async await using ChatGPT.
// 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()
¥Removed update()
Model.update()
、Query.prototype.update()
和 Document.prototype.update()
已被删除。请改用 updateOne()
。
¥Model.update()
, Query.prototype.update()
, and Document.prototype.update()
have been removed.
Use updateOne()
instead.
// Before
await Model.update(filter, update);
await doc.update(update);
// After
await Model.updateOne(filter, update);
await doc.updateOne(update);
ObjectId 需要 new
¥ObjectId requires new
在 Mongoose 6 及更早版本中,你可以定义一个新的 ObjectId,而不使用 new
关键字:
¥In Mongoose 6 and older, you could define a new ObjectId without using the new
keyword:
// 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
关键字。
¥In Mongoose 7, ObjectId
is now a JavaScript class, so you need to use the new
keyword.
// Works in Mongoose 6 and Mongoose 7
const oid = new mongoose.Types.ObjectId('0'.repeat(24));
id
Setter
从 Mongoose 7.4 开始,Mongoose 的内置 id
虚拟(将文档的 _id
存储为字符串)具有一个 setter,允许通过 id
修改文档的 _id
属性。
¥Starting in Mongoose 7.4, Mongoose's built-in id
virtual (which stores the document's _id
as a string) has a setter which allows modifying the document's _id
property via id
.
const doc = await TestModel.findOne();
doc.id = '000000000000000000000000';
doc._id; // ObjectId('000000000000000000000000')
如果你创建的 new TestModel(obj)
中 obj
同时包含 id
和 _id
,或者你使用 doc.set()
,这可能会导致令人惊讶的行为
¥This can cause surprising behavior if you create a new TestModel(obj)
where obj
contains both an id
and an _id
, or if you use 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
setter 后来在 Mongoose 8 中被删除 是由于兼容性问题。
¥The id
setter was later removed in Mongoose 8 due to compatibility issues.
鉴别器模式默认使用基本模式选项
¥Discriminator schemas use base schema options by default
当你使用 Model.discriminator()
时,Mongoose 现在将默认使用鉴别器基本结构的选项。这意味着你不需要显式设置子结构选项来匹配基本结构。
¥When you use Model.discriminator()
, Mongoose will now use the discriminator base schema's options by default.
This means you don't need to explicitly set child schema options to match the base schema's.
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()
签名
¥Removed castForQueryWrapper
, updated castForQuery()
signature
Mongoose 现在总是使用 3 个参数调用 SchemaType castForQuery()
方法:$conditional
、value
和 context
。如果你已经实现了定义其自己的 castForQuery()
方法的自定义结构类型,则需要按如下方式更新该方法。
¥Mongoose now always calls SchemaType castForQuery()
method with 3 arguments: $conditional
, value
, and context
.
If you've implemented a custom schema type that defines its own castForQuery()
method, you need to update the method as follows.
// 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()
中的架构选项
¥Copy Schema options in Schema.prototype.add()
Mongoose 现在在将一个结构添加到另一个结构时复制用户定义的结构选项。例如,下面的 childSchema
将获得 baseSchema
的 id
和 toJSON
选项。
¥Mongoose now copies user defined schema options when adding one schema to another.
For example, childSchema
below will get baseSchema
's id
and toJSON
options.
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()
的情况。
¥This applies both when creating a new schema using an array of schemas, as well as when calling add()
as follows.
childSchema.add(new Schema({}, { toObject: { virtuals: true } }));
childSchema.options.toObject; // { virtuals: true } in Mongoose 7. undefined in Mongoose 6.
ObjectId bsontype 现在有小写 d
¥ObjectId bsontype now has lowercase d
ObjectIds 上的内部 _bsontype
属性等于 Mongoose 7 中的 'ObjectId'
,而不是 Mongoose 6 中的 'ObjectID'
。
¥The internal _bsontype
property on ObjectIds is equal to 'ObjectId'
in Mongoose 7, as opposed to 'ObjectID'
in Mongoose 6.
const oid = new mongoose.Types.ObjectId();
oid._bsontype; // 'ObjectId' in Mongoose 7, 'ObjectID' in older versions of Mongoose
请更新任何使用 _bsontype
检查对象是否为 ObjectId 的地方。这也可能会影响使用 Mongoose 的库。
¥Please update any places where you use _bsontype
to check if an object is an ObjectId.
This may also affect libraries that use Mongoose.
删除了 mapReduce
¥Removed mapReduce
MongoDB 不再支持 mapReduce
,因此 Mongoose 7 不再有 Model.mapReduce()
功能。使用聚合框架作为 mapReduce()
的替代品。
¥MongoDB no longer supports mapReduce
, so Mongoose 7 no longer has a Model.mapReduce()
function.
Use the aggregation framework as a replacement for 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 的支持
¥Removed Support for custom promise libraries
Mongoose 7 不再支持插入自定义 Promise 库。因此,以下内容不再使 Mongoose 在 Mongoose 7 中返回 Bluebird promise。
¥Mongoose 7 no longer supports plugging in custom promise libraries. So the following no longer makes Mongoose return Bluebird promises in Mongoose 7.
const mongoose = require('mongoose');
// No-op on Mongoose 7
mongoose.Promise = require('bluebird');
如果你想使用 Bluebird 实现全局作用域内的所有 promise,你可以执行以下操作:
¥If you want to use Bluebird for all promises globally, you can do the following:
global.Promise = require('bluebird');
TypeScript 特定的更改
¥TypeScript-specific Changes
删除了 LeanDocument
并支持 extends Document
¥Removed LeanDocument
and support for extends Document
Mongoose 7 不再导出 LeanDocument
类型,也不再支持将 extends Document
的文档类型传递到 Model<>
。
¥Mongoose 7 no longer exports a LeanDocument
type, and no longer supports passing a document type that extends Document
into 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
的新参数
¥New Parameters for HydratedDocument
Mongoose 的 HydratedDocument
类型将原始文档接口转换为水合 Mongoose 文档的类型,包括虚拟、方法等。在 Mongoose 7 中,HydratedDocument
的通用参数已更改。在 Mongoose 6 中,通用参数为:
¥Mongoose's HydratedDocument
type transforms a raw document interface into the type of the hydrated Mongoose document, including virtuals, methods, etc.
In Mongoose 7, the generic parameters to HydratedDocument
have changed.
In Mongoose 6, the generic parameters were:
type HydratedDocument<
DocType,
TMethodsAndOverrides = {},
TVirtuals = {}
> = Document<unknown, any, DocType> &
Require_id<DocType> &
TMethodsAndOverrides &
TVirtuals;
在 Mongoose 7 中,新类型如下。
¥In Mongoose 7, the new type is as follows.
type HydratedDocument<
DocType,
TOverrides = {},
TQueryHelpers = {}
> = Document<unknown, TQueryHelpers, DocType> &
Require_id<DocType> &
TOverrides;
在 Mongoose 7 中,第一个参数是原始文档接口,第二个参数是任何特定于文档的覆盖(通常是虚函数和方法),第三个参数是与文档模型关联的任何查询辅助程序。
¥In Mongoose 7, the first parameter is the raw document interface, the 2nd parameter is any document-specific overrides (usually virtuals and methods), and the 3rd parameter is any query helpers associated with the document's model.
主要区别在于,在 Mongoose 6 中,第三个通用参数是文档的虚拟参数。在 Mongoose 7 中,第三个通用参数是文档的查询助手。
¥The key difference is that, in Mongoose 6, the 3rd generic param was the document's virtuals. In Mongoose 7, the 3rd generic param is the document's query helpers.
// Mongoose 6 version:
type UserDocument = HydratedDocument<TUser, TUserMethods, TUserVirtuals>;
// Mongoose 7:
type UserDocument = HydratedDocument<TUser, TUserMethods & TUserVirtuals, TUserQueryHelpers>;