從 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,或者如果 async 函式不適用於您,則使用 Promise。如果您需要協助重構舊版程式碼,可以使用 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
ObjectIds 的內部 _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');
如果您想為所有 Promise 全域使用 Bluebird,可以執行以下操作
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>;