文档
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()
允许你避免显式检查空值,类似于 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 文档中使用可选链 ?.
和空值合并 ??
。但是,在使用 空值合并赋值 ??=
创建嵌套路径时要小心。
// 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 });
阅读 验证 指南以了解更多详情。
覆盖
有两种不同的方法可以覆盖文档(替换文档中的所有键)。一种方法是使用 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' });
下一步
现在我们已经介绍了文档,让我们来看看 子文档。