從 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
- 預設函式的「文件」參數
- 陣列是 Proxy
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 仍然支援 MongoDB 伺服器版本回溯到 3.0.0。
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 驅動程式 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
}
});
陣列是 Proxy
Mongoose 陣列現在是 ES6 Proxy。您不再需要在直接設定陣列索引後使用 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 中的混合類型。這消除了對 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 將會預設為 false
,針對 autoCreate
和 autoIndex
,因為當連線到次要節點時,createCollection()
和 createIndex()
都會失敗。
不再有 context: 'query'
已移除查詢的 context
選項。現在 Mongoose 始終使用 context = 'query'
。
具有填充路徑的自訂驗證器
Mongoose 6 始終會使用已解除填充的路徑(也就是使用 ID 而非文件本身)來呼叫驗證器。在 Mongoose 5 中,如果路徑已填充,Mongoose 會使用已填充的文件來呼叫驗證器。請參閱 #8042
具有複本集的「已斷開連線」事件
當連線到複本集時,當與主要節點的連線遺失時,連線現在會發出「已斷開連線」的訊號。在 Mongoose 5 中,連線僅在遺失與複本集所有成員的連線時才會發出「已斷開連線」的訊號。
但是,Mongoose 6 在連線斷開時不會緩衝命令。因此,即使 Mongoose 連線處於斷開連線狀態,您仍然可以使用 readPreference = 'secondary'
成功執行查詢等命令。
移除 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 現在會將 valueOf()
函數新增至 ObjectIds。這表示您現在可以使用 ==
將 ObjectId 與字串進行比較。
const a = ObjectId('6143b55ac9a762738b15d4f0');
a == '6143b55ac9a762738b15d4f0'; // true
不可變的 createdAt
如果您設定 timestamps: true
,Mongoose 現在會將 createdAt
屬性設為 immutable
(不可變的)。請參閱 gh-10139
移除驗證器 isAsync
isAsync
不再是 validate
的選項。請改用 async function
(非同步函式)。
移除 safe
safe
不再是 schemas(結構描述)、queries(查詢)或 save()
的選項。請改用 writeConcern
。
SchemaType set
參數
Mongoose 現在呼叫 setter 函式時,會將 priorValue
作為第二個參數,而不是 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()
預設會使用最上層 schema 的 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 }));
或者定義 inline(內聯)的 child
schema(子結構描述)(僅限 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
。
以下舊版類型已被移除
ModelUpdateOptions
DocumentQuery
HookSyncCallback
HookAsyncCallback
HookErrorCallback
HookNextFunction
HookDoneFunction
SchemaTypeOpts
ConnectionOptions
Mongoose 6 會推斷 virtual getters(虛擬 getter)和 setters(虛擬 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 Node 驅動程式將永遠嘗試重試任何操作,直到達到 serverSelectionTimeoutMS
,即使 MongoDB 停機很長一段時間也是如此。因此,它永遠不會用完重試次數或嘗試重新連線到 MongoDB。