文档

¥Documents

Mongoose documents 表示与存储在 MongoDB 中的文档的一对一映射。每个文档都是其 模型 的一个实例。

¥Mongoose documents represent a one-to-one mapping to documents as stored in MongoDB. Each document is an instance of its Model.

文档与模型

¥Documents vs Models

文档模型 是 Mongoose 中不同的类。Model 类是 Document 类的子类。当你使用 模型构造函数 时,你会创建一个新文档。

¥Document and Model are distinct classes in Mongoose. The Model class is a subclass of the Document class. When you use the Model constructor, you create a new document.

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 中,"document" 通常表示模型的实例。你不必在不通过模型的情况下创建 Document 类的实例。

¥In Mongoose, a "document" generally means an instance of a model. You should not have to create an instance of the Document class without going through a model.

检索中

¥Retrieving

当你使用 findOne() 等模型函数从 MongoDB 加载文档时,你会得到一个 Mongoose 文档。

¥When you load documents from MongoDB using model functions like findOne(), you get a Mongoose document back.

const doc = await MyModel.findOne();

doc instanceof MyModel; // true
doc instanceof mongoose.Model; // true
doc instanceof mongoose.Document; // true

使用 save() 更新

¥Updating Using save()

Mongoose 文档跟踪更改。你可以使用普通 JavaScript 赋值来修改文档,Mongoose 会将其转换为 MongoDB 更新运算符

¥Mongoose documents track changes. You can modify a document using vanilla JavaScript assignments and Mongoose will convert it into MongoDB update operators.

doc.name = 'foo';

// Mongoose sends an `updateOne({ _id: doc._id }, { $set: { name: 'foo' } })`
// to MongoDB.
await doc.save();

save() 方法返回一个 promise。如果 save() 成功,则 promise 将解析为已保存的文档。

¥The save() method returns a promise. If save() succeeds, the promise resolves to the document that was saved.

doc.save().then(savedDoc => {
  savedDoc === doc; // true
});

如果没有找到对应 _id 的文档,Mongoose 会报一个 DocumentNotFoundError

¥If the document with the corresponding _id is not found, Mongoose will report a 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

设置嵌套属性

¥Setting Nested Properties

Mongoose 文档有一个 set() 函数,你可以使用它来安全地设置深度嵌套的属性。

¥Mongoose documents have a set() function that you can use to safely set deeply nested properties.

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 的 可选链接运算符 ?.

¥Mongoose documents also have a get() function that lets you safely read deeply nested properties. get() lets you avoid having to explicitly check for nullish values, similar to JavaScript's optional chaining operator ?..

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 文档一起使用。但是,使用 空值合并赋值 ??= 创建 Mongoose 文档的嵌套路径时要小心。

¥You can use optional chaining ?. and nullish coalescing ?? with Mongoose documents. However, be careful when using nullish coalescing assignments ??= to create nested paths with Mongoose documents.

// 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.

使用查询更新

¥Updating Using Queries

save() 函数通常是使用 Mongoose 更新文档的正确方法。通过 save(),你可以获得完整的 validation中间件

¥The save() function is generally the right way to update a document with Mongoose. With save(), you get full validation and middleware.

对于 save() 不够灵活的情况,Mongoose 允许你通过铸造、中间件有限的验证 创建自己的 MongoDB 更新

¥For cases when save() isn't flexible enough, Mongoose lets you create your own MongoDB updates with casting, middleware, and limited validation.

// Update all documents in the `mymodels` collection
await MyModel.updateMany({}, { $set: { name: 'foo' } });

注意,update()updateMany()findOneAndUpdate() 等不执行 save() 中间件。如果你需要保存中间件和完整验证,请首先查询文档,然后对其进行 save()

¥Note that update(), updateMany(), findOneAndUpdate(), etc. do not execute save() middleware. If you need save middleware and full validation, first query for the document and then save() it.

验证

¥Validating

文档在保存之前经过转换和验证。Mongoose 首先将值转换为指定类型,然后验证它们。在内部,Mongoose 在保存之前调用文档的 validate() 方法

¥Documents are casted and validated before they are saved. Mongoose first casts values to the specified type and then validates them. Internally, Mongoose calls the document's validate() method before saving.

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 默认情况下不会对查询函数参数运行验证。你需要为 Mongoose 设置 runValidators: true 才能验证。

¥Mongoose also supports limited validation on updates using the runValidators option. Mongoose casts parameters to query functions like findOne(), updateOne() by default. However, Mongoose does not run validation on query function parameters by default. You need to set runValidators: true for Mongoose to validate.

// 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 });

请阅读 validation 指南了解更多详细信息。

¥Read the validation guide for more details.

覆盖

¥Overwriting

有两种不同的方法可以覆盖文档(替换文档中的所有键)。一种方法是使用 Document#overwrite() 功能,然后使用 save()

¥There are 2 different ways to overwrite a document (replacing all keys in the document). One way is to use the Document#overwrite() function followed by 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()

¥The other way is to use Model.replaceOne().

// Sets `name` and unsets all other properties
await Person.replaceOne({ _id }, { name: 'Jean-Luc Picard' });

下一步

¥Next Up

现在我们已经介绍了文档,让我们看一下 子文档

¥Now that we've covered Documents, let's take a look at Subdocuments.