從 4.x 遷移至 5.x
請注意:我們計劃於 2024 年 3 月 1 日停止對 Mongoose 5 的支援。請參閱我們的版本支援指南。
從 Mongoose 4.x 遷移至 Mongoose 5.x 時,您應該注意幾個向後不相容的變更。
如果您仍在使用 Mongoose 3.x,請閱讀Mongoose 3.x 至 4.x 的遷移指南。
- 版本需求
- 查詢中間件
mongoose.connect()
的 Promise 和回呼- 連線邏輯和
useMongoClient
- Setter 順序
- 檢查路徑是否已填充
remove()
和deleteX()
的傳回值- 聚合游標
- geoNear
- 連線字串需要 URI 編碼
- 包含特定字元的密碼
- Domain sockets (網域套接字)
toObject()
選項- Aggregate 參數
- 布林值轉換
- 查詢轉換
- Post Save Hooks 取得流程控制
$pushAll
運算符- 始終使用正向鍵順序
- 在查詢上執行 setter
- 預編譯的瀏覽器套件
- 儲存錯誤
- Init hook 簽名
numAffected
和save()
remove()
和防抖getPromiseConstructor()
- 從 Pre Hooks 傳遞參數
- 陣列的
required
驗證器 - debug 輸出預設為 stdout 而不是 stderr
- 覆寫篩選器屬性
bulkWrite()
結果- 嚴格的 SSL 驗證
版本需求
Mongoose 現在需要 Node.js >= 4.0.0 和 MongoDB >= 3.0.0。MongoDB 2.6 和 Node.js < 4 均已於 2016 年終止生命週期。
查詢中間件
查詢中間件現在會在您呼叫 mongoose.model()
或 db.model()
時編譯。如果您在呼叫 mongoose.model()
後新增查詢中間件,該中間件將不會被呼叫。
const schema = new Schema({ name: String });
const MyModel = mongoose.model('Test', schema);
schema.pre('find', () => { console.log('find!'); });
MyModel.find().exec(function() {
// In mongoose 4.x, the above `.find()` will print "find!"
// In mongoose 5.x, "find!" will **not** be printed.
// Call `pre('find')` **before** calling `mongoose.model()` to make the middleware apply.
});
mongoose.connect()
的 Promise 和回呼
如果未指定回呼,mongoose.connect()
和 mongoose.disconnect()
現在會傳回 promise,否則傳回 null
。它不會傳回 Mongoose 單例。
// Worked in mongoose 4. Does **not** work in mongoose 5, `mongoose.connect()`
// now returns a promise consistently. This is to avoid the horrible things
// we've done to allow mongoose to be a thenable that resolves to itself.
mongoose.connect('mongodb://127.0.0.1:27017/test').model('Test', new Schema({}));
// Do this instead
mongoose.connect('mongodb://127.0.0.1:27017/test');
mongoose.model('Test', new Schema({}));
連線邏輯和 useMongoClient
useMongoClient
選項已在 Mongoose 5 中移除,現在始終為 true
。因此,如果 useMongoClient
選項關閉,Mongoose 5 不再支援 Mongoose 4.x 中有效的 mongoose.connect()
的數個函式簽名。以下是一些在 Mongoose 5.x 中不起作用的 mongoose.connect()
呼叫範例。
mongoose.connect('127.0.0.1', 27017);
mongoose.connect('127.0.0.1', 'mydb', 27017);
mongoose.connect('mongodb://host1:27017,mongodb://host2:27017');
在 Mongoose 5.x 中,如果指定,mongoose.connect()
和 mongoose.createConnection()
的第一個參數必須是 MongoDB 連線字串。連線字串和選項會傳遞給 MongoDB Node.js 驅動程式的 MongoClient.connect()
函式。Mongoose 不會修改連線字串,儘管 mongoose.connect()
和 mongoose.createConnection()
除了 MongoDB 驅動程式支援的選項外,還支援一些其他選項。
Setter 順序
Setter 在 4.x 中以相反順序執行
const schema = new Schema({ name: String });
schema.path('name').
set(() => console.log('This will print 2nd')).
set(() => console.log('This will print first'));
在 5.x 中,setter 會按照宣告的順序執行。
const schema = new Schema({ name: String });
schema.path('name').
set(() => console.log('This will print first')).
set(() => console.log('This will print 2nd'));
檢查路徑是否已填充
Mongoose 5.1.0 為 ObjectIds 引入了 _id
getter,讓您可以取得 ObjectId,而無論路徑是否已填充。
const blogPostSchema = new Schema({
title: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Author'
}
});
const BlogPost = mongoose.model('BlogPost', blogPostSchema);
await BlogPost.create({ title: 'test', author: author._id });
const blogPost = await BlogPost.findOne();
console.log(blogPost.author); // '5b207f84e8061d1d2711b421'
// New in Mongoose 5.1.0: this will print '5b207f84e8061d1d2711b421' as well
console.log(blogPost.author._id);
await blogPost.populate('author');
console.log(blogPost.author._id); // '5b207f84e8061d1d2711b421'
因此,檢查 blogPost.author._id
是否存在 不再是檢查 author
是否已填充的方法。請使用 blogPost.populated('author') != null
或 blogPost.author instanceof mongoose.Types.ObjectId
來檢查是否已填充 author
。
請注意,您可以呼叫 mongoose.set('objectIdGetter', false)
來變更此行為。
remove()
和 deleteX()
的傳回值
deleteOne()
、deleteMany()
和 remove()
現在解析為結果物件,而不是完整的 驅動程式 WriteOpResult
物件。
// In 4.x, this is how you got the number of documents deleted
MyModel.deleteMany().then(res => console.log(res.result.n));
// In 5.x this is how you get the number of documents deleted
MyModel.deleteMany().then(res => res.n);
聚合游標
4.x 中的 useMongooseAggCursor
選項現在始終開啟。這是 Mongoose 5 中聚合游標的新語法
// When you call `.cursor()`, `.exec()` will now return a mongoose aggregation
// cursor.
const cursor = MyModel.aggregate([{ $match: { name: 'Val' } }]).cursor().exec();
// No need to `await` on the cursor or wait for a promise to resolve
cursor.eachAsync(doc => console.log(doc));
// Can also pass options to `cursor()`
const cursorWithOptions = MyModel.
aggregate([{ $match: { name: 'Val' } }]).
cursor({ batchSize: 10 }).
exec();
geoNear
由於 MongoDB 驅動程式不再支援它,因此 Model.geoNear()
已被移除
連線字串需要 URI 編碼
由於 MongoDB 驅動程式中的變更,連線字串必須進行 URI 編碼。
如果沒有,連線可能會因非法字元訊息而失敗。
包含特定字元的密碼
請參閱受影響字元的完整列表。
如果您的應用程式使用了許多不同的連線字串,您的測試案例可能會通過,但生產密碼會失敗。請將所有連線字串編碼以確保安全。
如果您想繼續使用未編碼的連線字串,最簡單的解決方法是使用 mongodb-uri
模組來剖析連線字串,然後產生正確編碼的版本。您可以使用如下的函式
const uriFormat = require('mongodb-uri');
function encodeMongoURI(urlString) {
if (urlString) {
const parsed = uriFormat.parse(urlString);
urlString = uriFormat.format(parsed);
}
return urlString;
}
// Your un-encoded string.
const mongodbConnectString = 'mongodb://...';
mongoose.connect(encodeMongoURI(mongodbConnectString));
無論現有字串是否已編碼,上述函式都可以安全使用。
Domain sockets (網域套接字)
網域套接字必須進行 URI 編碼。例如
// Works in mongoose 4. Does **not** work in mongoose 5 because of more
// stringent URI parsing.
const host = '/tmp/mongodb-27017.sock';
mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
// Do this instead
const host = encodeURIComponent('/tmp/mongodb-27017.sock');
mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`);
toObject()
選項
toObject()
和 toJSON()
的 options
參數會合併預設值,而不是覆寫它們。
// Note the `toObject` option below
const schema = new Schema({ name: String }, { toObject: { virtuals: true } });
schema.virtual('answer').get(() => 42);
const MyModel = db.model('MyModel', schema);
const doc = new MyModel({ name: 'test' });
// In mongoose 4.x this prints "undefined", because `{ minimize: false }`
// overwrites the entire schema-defined options object.
// In mongoose 5.x this prints "42", because `{ minimize: false }` gets
// merged with the schema-defined options.
console.log(doc.toJSON({ minimize: false }).answer);
Aggregate 參數
aggregate()
不再接受 spread,您必須以陣列形式傳遞您的聚合管道。以下程式碼在 4.x 中有效
MyModel.aggregate({ $match: { isDeleted: false } }, { $skip: 10 }).exec(cb);
上述程式碼在 5.x 中不起作用,您必須將 $match
和 $skip
階段包裝在陣列中。
MyModel.aggregate([{ $match: { isDeleted: false } }, { $skip: 10 }]).exec(cb);
布林值轉換
預設情況下,Mongoose 4 會在沒有錯誤的情況下將任何值強制轉換為布林值。
// Fine in mongoose 4, would save a doc with `boolField = true`
const MyModel = mongoose.model('Test', new Schema({
boolField: Boolean
}));
MyModel.create({ boolField: 'not a boolean' });
Mongoose 5 只會將以下值轉換為 true
true
'true'
1
'1'
'yes'
並將以下值轉換為 false
false
'false'
0
'0'
'no'
所有其他值都會導致 CastError
查詢轉換
update()
、updateOne()
、updateMany()
、replaceOne()
、remove()
、deleteOne()
和 deleteMany()
的轉換會在 exec()
之前不會發生。這使得 hook 和自訂查詢輔助程式更容易修改資料,因為 Mongoose 不會在您的 hook 和查詢輔助程式執行完畢之前重新結構化您傳遞的資料。它也使得在傳遞更新後設定 overwrite
選項成為可能。
// In mongoose 4.x, this becomes `{ $set: { name: 'Baz' } }` despite the `overwrite`
// In mongoose 5.x, this overwrite is respected and the first document with
// `name = 'Bar'` will be replaced with `{ name: 'Baz' }`
User.where({ name: 'Bar' }).update({ name: 'Baz' }).setOptions({ overwrite: true });
Post Save Hooks 取得流程控制
Post hook 現在取得流程控制,這意味著非同步 post save hook 和子文件 post save hook 會在您的 save()
回呼之前執行。
const ChildModelSchema = new mongoose.Schema({
text: {
type: String
}
});
ChildModelSchema.post('save', function(doc) {
// In mongoose 5.x this will print **before** the `console.log()`
// in the `save()` callback. In mongoose 4.x this was reversed.
console.log('Child post save');
});
const ParentModelSchema = new mongoose.Schema({
children: [ChildModelSchema]
});
const Model = mongoose.model('Parent', ParentModelSchema);
const m = new Model({ children: [{ text: 'test' }] });
m.save(function() {
// In mongoose 5.xm this prints **after** the "Child post save" message.
console.log('Save callback');
});
$pushAll
運算符
由於 $pushAll
自 MongoDB 2.4 起已棄用,因此不再支援且不再在內部用於 save()
。請改用 $push
和 $each
。
始終使用正向鍵順序
retainKeyOrder
選項已移除,Mongoose 現在在複製物件時始終保留相同的鍵位置。如果您有依賴相反鍵順序的查詢或索引,您將必須變更它們。
在查詢上執行 setter
Setter 現在預設在查詢上執行,並且舊的 runSettersOnQuery
選項已移除。
const schema = new Schema({
email: { type: String, lowercase: true }
});
const Model = mongoose.model('Test', schema);
Model.find({ email: 'FOO@BAR.BAZ' }); // Converted to `find({ email: 'foo@bar.baz' })`
預編譯的瀏覽器套件
我們不再有針對瀏覽器的 Mongoose 預編譯版本。如果您想在瀏覽器中使用 Mongoose schema,您需要使用 browserify/webpack 建立自己的套件。
儲存錯誤
saveErrorIfNotFound
選項已移除,如果找不到基礎文件,Mongoose 現在始終會從 save()
中傳回錯誤
Init hook 簽名
init
hook 現在完全同步,並且不會收到 next()
作為參數。
Document.prototype.init()
不再將回呼作為參數。它始終是同步的,只是為了舊版原因而有一個回呼。
numAffected
和 save()
doc.save()
不再將 numAffected
作為第三個參數傳遞給其回呼。
remove()
和防抖
doc.remove()
不再進行防抖
getPromiseConstructor()
getPromiseConstructor()
已消失,只需使用 mongoose.Promise
。
從 Pre Hooks 傳遞參數
您無法在 Mongoose 5.x 中使用 next()
將參數傳遞給鏈中的下一個 pre 中間件。在 Mongoose 4 中,pre 中間件中的 next('Test')
會以 'Test' 作為參數呼叫下一個中間件。Mongoose 5.x 已移除對此的支援。
陣列的 required
驗證器
在 Mongoose 5 中,required
驗證器僅驗證值是否為陣列。也就是說,它不會像在 Mongoose 4 中一樣因空陣列而失敗。
debug 輸出預設為 stdout 而不是 stderr
在 Mongoose 5 中,預設 debug 函式使用 console.info()
來顯示訊息,而不是 console.error()
。
覆寫篩選器屬性
在 Mongoose 4.x 中,用物件覆寫作為原始值的篩選器屬性會悄悄失敗。例如,以下程式碼會忽略 where()
,並且等效於 Sport.find({ name: 'baseball' })
Sport.find({ name: 'baseball' }).where({ name: { $ne: 'softball' } });
在 Mongoose 5.x 中,上述程式碼會正確地將 'baseball'
覆寫為 { $ne: 'softball' }
bulkWrite()
結果
Mongoose 5.x 使用 MongoDB Node.js 驅動程式的 3.x 版本。MongoDB 驅動程式 3.x 變更了 bulkWrite()
呼叫結果的格式,因此不再有最上層的 nInserted
、nModified
等屬性。新的結果物件結構在此處說明。
const Model = mongoose.model('Test', new Schema({ name: String }));
const res = await Model.bulkWrite([{ insertOne: { document: { name: 'test' } } }]);
console.log(res);
在 Mongoose 4.x 中,以上程式碼將會印出
BulkWriteResult {
ok: [Getter],
nInserted: [Getter],
nUpserted: [Getter],
nMatched: [Getter],
nModified: [Getter],
nRemoved: [Getter],
getInsertedIds: [Function],
getUpsertedIds: [Function],
getUpsertedIdAt: [Function],
getRawResponse: [Function],
hasWriteErrors: [Function],
getWriteErrorCount: [Function],
getWriteErrorAt: [Function],
getWriteErrors: [Function],
getLastOp: [Function],
getWriteConcernError: [Function],
toJSON: [Function],
toString: [Function],
isOk: [Function],
insertedCount: 1,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: { '0': 5be9a3101638a066702a0d38 },
n: 1 }
在 Mongoose 5.x 中,該腳本將會印出
BulkWriteResult {
result:
{ ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [ [Object] ],
nInserted: 1,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: [],
lastOp: { ts: [Object], t: 1 } },
insertedCount: 1,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: { '0': 5be9a1c87decfc6443dd9f18 },
n: 1 }
嚴格 SSL 驗證
MongoDB Node.js 驅動程式的最新版本預設使用嚴格的 SSL 驗證,如果您使用自我簽署憑證,可能會導致錯誤。
如果這阻礙了您升級,您可以將 tlsInsecure
選項設為 true
。
mongoose.connect(uri, { tlsInsecure: false }); // Opt out of additional SSL validation