文件
Mongoose 文件代表 MongoDB 中儲存的文件的一對一映射。每個文件都是其模型的實例。
文件 vs 模型
文件和模型是 Mongoose 中不同的類別。模型類別是文件類別的子類別。當您使用模型建構子時,您會建立一個新的文件。
const MyModel = mongoose.model('Test', new Schema({ name: String }));
const doc = new MyModel();
doc instanceof MyModel; // true
doc instanceof mongoose.Model; // true
doc instanceof mongoose.Document; // true
在 Mongoose 中,「文件」通常是指模型的實例。您不應該在未透過模型的情況下建立文件類別的實例。
擷取
當您使用模型函式(例如 findOne()
)從 MongoDB 載入文件時,您會得到一個 Mongoose 文件。
const doc = await MyModel.findOne();
doc instanceof MyModel; // true
doc instanceof mongoose.Model; // true
doc instanceof mongoose.Document; // true
使用 save()
更新
Mongoose 文件會追蹤變更。您可以使用純 JavaScript 指定來修改文件,Mongoose 會將其轉換為 MongoDB 更新運算子。
doc.name = 'foo';
// Mongoose sends an `updateOne({ _id: doc._id }, { $set: { name: 'foo' } })`
// to MongoDB.
await doc.save();
save()
方法會回傳 Promise。如果 save()
成功,則 Promise 會解析為已儲存的文件。
doc.save().then(savedDoc => {
savedDoc === doc; // true
});
如果找不到具有對應 _id
的文件,Mongoose 將報告 DocumentNotFoundError
const doc = await MyModel.findOne();
// Delete the document so Mongoose won't be able to save changes
await MyModel.deleteOne({ _id: doc._id });
doc.name = 'foo';
await doc.save(); // Throws DocumentNotFoundError
設定巢狀屬性
Mongoose 文件具有 set()
函式,您可以使用該函式安全地設定深層巢狀屬性。
const schema = new Schema({
nested: {
subdoc: new Schema({
name: String
})
}
});
const TestModel = mongoose.model('Test', schema);
const doc = new TestModel();
doc.set('nested.subdoc.name', 'John Smith');
doc.nested.subdoc.name; // 'John Smith'
Mongoose 文件也具有 get()
函式,可讓您安全地讀取深層巢狀屬性。get()
可讓您避免必須明確檢查 nullish 值,類似於 JavaScript 的選用鏈結運算子 ?.
。
const doc2 = new TestModel();
doc2.get('nested.subdoc.name'); // undefined
doc2.nested?.subdoc?.name; // undefined
doc2.set('nested.subdoc.name', 'Will Smith');
doc2.get('nested.subdoc.name'); // 'Will Smith'
您可以在 Mongoose 文件中使用選用鏈結 ?.
和 nullish 合併 ??
。但是,使用 nullish 合併指定 ??=
建立 Mongoose 文件的巢狀路徑時請小心。
// The following works fine
const doc3 = new TestModel();
doc3.nested.subdoc ??= {};
doc3.nested.subdoc.name = 'John Smythe';
// The following does **NOT** work.
// Do not use the following pattern with Mongoose documents.
const doc4 = new TestModel();
(doc4.nested.subdoc ??= {}).name = 'Charlie Smith';
doc.nested.subdoc; // Empty object
doc.nested.subdoc.name; // undefined.
使用查詢更新
save()
函式通常是用於使用 Mongoose 更新文件的正確方法。使用 save()
,您可以獲得完整的驗證和中介軟體。
對於 save()
不夠彈性的情況,Mongoose 可讓您建立自己的 MongoDB 更新,並具有轉換、中介軟體和有限的驗證。
// Update all documents in the `mymodels` collection
await MyModel.updateMany({}, { $set: { name: 'foo' } });
請注意,update()
、updateMany()
、findOneAndUpdate()
等不會執行 save()
中介軟體。如果您需要儲存中介軟體和完整驗證,請先查詢文件,然後 save()
它。
驗證
文件會在儲存之前進行轉換和驗證。Mongoose 會先將值轉換為指定的類型,然後驗證它們。在內部,Mongoose 會在儲存之前呼叫文件的 validate()
方法。
const schema = new Schema({ name: String, age: { type: Number, min: 0 } });
const Person = mongoose.model('Person', schema);
const p = new Person({ name: 'foo', age: 'bar' });
// Cast to Number failed for value "bar" at path "age"
await p.validate();
const p2 = new Person({ name: 'foo', age: -1 });
// Path `age` (-1) is less than minimum allowed value (0).
await p2.validate();
Mongoose 也支援使用 runValidators
選項在更新時進行有限的驗證。Mongoose 會預設轉換查詢函式的參數(例如 findOne()
、updateOne()
)。但是,Mongoose 預設不對查詢函式參數執行驗證。您需要設定 runValidators: true
,Mongoose 才會進行驗證。
// Cast to number failed for value "bar" at path "age"
await Person.updateOne({}, { age: 'bar' });
// Path `age` (-1) is less than minimum allowed value (0).
await Person.updateOne({}, { age: -1 }, { runValidators: true });
請閱讀驗證指南以了解更多詳細資訊。
覆寫
有 2 種不同的方法可以覆寫文件(取代文件中的所有索引鍵)。一種方法是使用 Document#overwrite()
函式,然後再使用 save()
。
const doc = await Person.findOne({ _id });
// Sets `name` and unsets all other properties
doc.overwrite({ name: 'Jean-Luc Picard' });
await doc.save();
另一種方法是使用 Model.replaceOne()
。
// Sets `name` and unsets all other properties
await Person.replaceOne({ _id }, { name: 'Jean-Luc Picard' });
接下來
既然我們已經介紹了文件,讓我們來看看子文件。