Schemas架构

If you haven't yet done so, please take a minute to read the quickstart to get an idea of how Mongoose works. 如果您还没有这样做,请花一分钟时间阅读快速入门,了解Mongoose是如何工作的。If you are migrating from 6.x to 7.x please take a moment to read the migration guide.如果您正在从6x迁移到7x,请花点时间阅读迁移指南

Defining your schema定义架构

Everything in Mongoose starts with a Schema. Mongoose中的一切都是从一个Schema开始的。Each schema maps to a MongoDB collection and defines the shape of the documents within that collection.每个架构映射到一个MongoDB集合,并定义该集合中文档的形状。

import mongoose from 'mongoose';
const { Schema } = mongoose;

const blogSchema = new Schema({
title: String, // String is shorthand for {type: String}字符串是{type: String}的简写
author: String,
body: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});

If you want to add additional keys later, use the Schema#add method.如果以后要添加其他密钥,请使用Schema#add方法。

Each key in our code blogSchema defines a property in our documents which will be cast to its associated SchemaType. 代码blogSchema中的每个键都定义了文档中的一个属性,该属性将被强制转换为其关联的SchemaTypeFor example, we've defined a property title which will be cast to the String SchemaType and property date which will be cast to a Date SchemaType.例如,我们定义了一个属性title,它将被强制转换为String架构类型,而属性日期将被强制转变为Date架构类型。

Notice above that if a property only requires a type, it can be specified using a shorthand notation (contrast the title property above with the date property).请注意,如果一个属性只需要一个类型,那么可以使用简写符号来指定它(将上面的title属性与date属性进行对比)。

Keys may also be assigned nested objects containing further key/type definitions like the meta property above. 还可以为键分配嵌套对象,该嵌套对象包含进一步的键/类型定义,如上面的meta属性。This will happen whenever a key's value is a POJO that doesn't have a type property.只要键的值是没有type属性的POJO,就会发生这种情况。

In these cases, Mongoose only creates actual schema paths for leaves in the tree. 在这些情况下,Mongoose只为树中的叶子创建实际的架构路径。(like meta.votes and meta.favs above), and the branches do not have actual paths. (如上面的meta.votesmeta.favs),并且分支没有实际路径。 A side-effect of this is that meta above cannot have its own validation. 这样做的一个副作用是上面的meta不能有自己的验证。If validation is needed up the tree, a path needs to be created up the tree - see the Subdocuments section for more information on how to do this. 如果需要向上验证,则需要在树上创建一个路径——有关如何执行此操作的更多信息,请参阅子文档部分。Also read the Mixed subsection of the SchemaTypes guide for some gotchas.还要阅读SchemaTypes指南中的Mixed小节,了解一些技巧。

The permitted SchemaTypes are:允许的架构类型包括:

Read more about SchemaTypes here.请在此处阅读有关架构类型的更多信息。

Schemas not only define the structure of your document and casting of properties, they also define document instance methods, static Model methods, compound indexes, and document lifecycle hooks called middleware.架构不仅定义了文档的结构和属性的转换,还定义了文档实例方法静态模型方法复合索引和称为中间件的文档生命周期挂钩。

Creating a model创建模型

To use our schema definition, we need to convert our blogSchema into a Model we can work with. 要使用架构定义,我们需要将blogSchema转换为一个可以使用的模型To do so, we pass it into mongoose.model(modelName, schema):要做到这一点,我们将其传递到mongoose.model(modelName, schema)中:

const Blog = mongoose.model('Blog', blogSchema);
// ready to go!

Ids

By default, Mongoose adds an _id property to your schemas.默认情况下,Mongoose会向您的架构添加一个_id属性。

const schema = new Schema();

schema.path('_id'); // ObjectId { ... }

When you create a new document with the automatically added _id property, Mongoose creates a new _id of type ObjectId to your document.当您创建一个具有自动添加的_id属性的新文档时,Mongoose会为您的文档创建一个类型为ObjectId的新_id

const Model = mongoose.model('Test', schema);

const doc = new Model();
doc._id instanceof mongoose.Types.ObjectId; // true

You can also overwrite Mongoose's default _id with your own _id. 您也可以用自己的_id覆盖Mongoose的默认_idJust be careful: Mongoose will refuse to save a document that doesn't have an _id, so you're responsible for setting _id if you define your own _id path.请注意:Mongoose将拒绝保存没有_id的文档,因此如果您定义了自己的_id路径,则需要负责设置_id

const schema = new Schema({ _id: Number });
const Model = mongoose.model('Test', schema);

const doc = new Model();
await doc.save(); // Throws "document must have an _id before saving"Throws“文档在保存之前必须有一个_id”

doc._id = 1;
await doc.save(); // works生效了

Instance methods实例方法

Instances of Models are documents. 模型的实例是文档Documents have many of their own built-in instance methods. 文档有许多自己的内置实例方法We may also define our own custom document instance methods.我们还可以定义自己的自定义文档实例方法。

// define a schema定义架构
const animalSchema = new Schema({ name: String, type: String },
{
// Assign a function to the "methods" object of our animalSchema through schema options.通过架构选项为animalSchema的“methods”对象分配一个函数。
// By following this approach, there is no need to create a separate TS type to define the type of the instance functions.通过遵循这种方法,不需要创建单独的TS类型来定义实例函数的类型。
methods: {
findSimilarTypes(cb) {
return mongoose.model('Animal').find({ type: this.type }, cb);
}
}
});

// Or, assign a function to the "methods" object of our animalSchema或者,为animalSchema的“methods”对象分配一个函数
animalSchema.methods.findSimilarTypes = function(cb) {
return mongoose.model('Animal').find({ type: this.type }, cb);
};

Now all of our animal instances have a findSimilarTypes method available to them.现在,我们所有的animal实例都有一个findSimilarTypes方法可供使用。

const Animal = mongoose.model('Animal', animalSchema);
const dog = new Animal({ type: 'dog' });

dog.findSimilarTypes((err, dogs) => {
console.log(dogs); // woof棒棒哒
});
  • Overwriting a default mongoose document method may lead to unpredictable results. 重写默认的mongoose文档方法可能会导致不可预测的结果。See this for more details.有关更多详细信息,请参阅此出
  • The example above uses the Schema.methods object directly to save an instance method. 上面的示例直接使用Schema.methods对象来保存实例方法。You can also use the Schema.method() helper as described here.您也可以使用这里描述的Schema.method()帮助程序。
  • Do not declare methods using ES6 arrow functions (=>). 不要使用ES6箭头函数(=>)声明方法。Arrow functions explicitly prevent binding this, so your method will not have access to the document and the above examples will not work.箭头函数明确禁止绑定this,因此您的方法将无法访问文档,上述示例也将不起作用。

Statics静态方法

You can also add static functions to your model. There are three equivalent ways to add a static:您也可以将静态函数添加到模型中。有三种等效的方法可以添加静态:

  • Add a function property to the second argument of the schema-constructor (statics)将函数属性添加到架构构造函数的第二个参数(statics
  • Add a function property to 将函数属性添加到schema.statics
  • Call the Schema#static() function调用Schema#static()函数

// define a schema定义架构
const animalSchema = new Schema({ name: String, type: String },
{
// Assign a function to the "statics" object of our animalSchema through schema options.通过架构选项为animalSchema的“statics”对象分配一个函数。
// By following this approach, there is no need to create a separate TS type to define the type of the statics functions.通过遵循这种方法,不需要创建单独的TS类型来定义静态函数的类型。
statics: {
findByName(name) {
return this.find({ name: new RegExp(name, 'i') });
}
}
});

// Or, Assign a function to the "statics" object of our animalSchema或者,为animalSchema的“statics”对象指定一个函数
animalSchema.statics.findByName = function(name) {
return this.find({ name: new RegExp(name, 'i') });
};
// Or, equivalently, you can call `animalSchema.static()`.或者,等效地,您可以调用`animalSchema.static()`。
animalSchema.static('findByBreed', function(breed) { return this.find({ breed }); });

const Animal = mongoose.model('Animal', animalSchema);
let animals = await Animal.findByName('fido');
animals = animals.concat(await Animal.findByBreed('Poodle'));

Do not declare statics using ES6 arrow functions (=>). 不要使用ES6箭头函数(=>)声明statics。Arrow functions explicitly prevent binding this, so the above examples will not work because of the value of this.箭头函数明确禁止绑定this,因此由于this的值,上面的示例将不起作用。

Query Helpers查询帮助程序

You can also add query helper functions, which are like instance methods but for mongoose queries. 您还可以添加查询助手函数,这些函数类似于实例方法,但用于mongoose查询。Query helper methods let you extend mongoose's chainable query builder API.查询助手方法允许您扩展猫鼬的可连掇查询生成器API


// define a schema定义架构
const animalSchema = new Schema({ name: String, type: String },
{
// Assign a function to the "query" object of our animalSchema through schema options.通过架构选项为animalSchema的“query”对象分配一个函数。
// By following this approach, there is no need to create a separate TS type to define the type of the query functions.通过遵循这种方法,不需要创建单独的TS类型来定义查询函数的类型。
query: {
byName(name) {
return this.where({ name: new RegExp(name, 'i') });
}
}
});

// Or, Assign a function to the "query" object of our animalSchema或者,为animalSchema的“query”对象分配一个函数
animalSchema.query.byName = function(name) {
return this.where({ name: new RegExp(name, 'i') });
};

const Animal = mongoose.model('Animal', animalSchema);

Animal.find().byName('fido').exec((err, animals) => {
console.log(animals);
});

Animal.findOne().byName('fido').exec((err, animal) => {
console.log(animal);
});

Indexes索引

MongoDB supports secondary indexes. MongoDB支持二级索引With mongoose, we define these indexes within our Schema at the path level or the schema level. 使用mongoose,我们在路径级别或架构级别的Schema中定义这些索引。Defining indexes at the schema level is necessary when creating compound indexes.在创建复合索引时,有必要在架构级别定义索引。

const animalSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true } // path level
});

animalSchema.index({ name: 1, type: -1 }); // schema level

See SchemaType#index() for other index options.有关其他索引选项,请参阅SchemaType#index()

When your application starts up, Mongoose automatically calls createIndex for each defined index in your schema. 当您的应用程序启动时,Mongoose会自动为您的架构中的每个定义的索引调用createIndexMongoose will call createIndex for each index sequentially, and emit an 'index' event on the model when all the createIndex calls succeeded or when there was an error. Mongoose将依次为每个索引调用createIndex,并在所有createIndex调用成功或出现错误时在模型上发出“index”事件。While nice for development, it is recommended this behavior be disabled in production since index creation can cause a significant performance impact. 虽然这对开发很好,但建议在生产中禁用此行为,因为索引创建可能会对性能产生重大影响Disable the behavior by setting the autoIndex option of your schema to false, or globally on the connection by setting the option autoIndex to false.通过将架构的autoIndex选项设置为false来禁用该行为,或者通过将选项autoIndex设置为false在连接上全局禁用该行为。

mongoose.connect('mongodb://user:pass@127.0.0.1:port/database', { autoIndex: false });
// or
mongoose.createConnection('mongodb://user:pass@127.0.0.1:port/database', { autoIndex: false });
// or
mongoose.set('autoIndex', false);
// or
animalSchema.set('autoIndex', false);
// or
new Schema({ /* ... */ }, { autoIndex: false });

Mongoose will emit an index event on the model when indexes are done building or an error occurred.当索引构建完成或发生错误时,Mongoose将在模型上发出一个index事件。

// Will cause an error because mongodb has an _id index by default that is not sparse将导致错误,因为mongodb默认情况下有一个不稀疏的_id索引
animalSchema.index({ _id: 1 }, { sparse: true });
const Animal = mongoose.model('Animal', animalSchema);

Animal.on('index', error => {
// "_id index cannot be sparse索引不能稀疏"
console.log(error.message);
});

See also the Model#ensureIndexes method.另请参阅Model#ensureIndexes方法。

Virtuals虚拟机

Virtuals are document properties that you can get and set but that do not get persisted to MongoDB. 是可以获取和设置的文档属性,但不会持久化到MongoDB。The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage.getter可用于格式化或组合字段,而setter可用于将单个值分解为多个值以进行存储。

// define a schema定义架构
const personSchema = new Schema({
name: {
first: String,
last: String
}
});

// compile our model编译模型
const Person = mongoose.model('Person', personSchema);

// create a document
const axl = new Person({
name: { first: 'Axl', last: 'Rose' }
});

Suppose you want to print out the person's full name. 假设你想打印出这个人的全名。You could do it yourself:你可以自己做:

console.log(axl.name.first + ' ' + axl.name.last); // Axl Rose

But concatenating the first and last name every time can get cumbersome. 但是,每次串联名字和姓氏可能会变得很麻烦。And what if you want to do some extra processing on the name, like removing diacritics? 如果你想对这个名字做一些额外的处理,比如去掉变音符号,该怎么办?A virtual property getter lets you define a fullName property that won't get persisted to MongoDB.虚拟属性getter允许您定义一个不会持久化到MongoDB的fullName属性。

// That can be done either by adding it to schema options:这可以通过将其添加到架构选项中来完成:
const personSchema = new Schema({
name: {
first: String,
last: String
}
}, {
virtuals: {
fullName: {
get() {
return this.name.first + ' ' + this.name.last;
}
}
}
});

// Or by using the virtual method as following:或者使用以下虚拟方法:
personSchema.virtual('fullName').get(function() {
return this.name.first + ' ' + this.name.last;
});

Now, mongoose will call your getter function every time you access the fullName property:现在,每次访问fullName属性时,mongoose都会调用getter函数:

console.log(axl.fullName); // Axl Rose

If you use toJSON() or toObject() mongoose will not include virtuals by default. 如果使用toJSON()toObject(),则默认情况下mongose将不包括虚拟对象。This includes the output of calling JSON.stringify() on a Mongoose document, because JSON.stringify() calls toJSON(). 这包括在Mongoose文档上调用JSON.stringify()的输出,因为JSON.stringify()调用到JSON()Pass { virtuals: true } to either toObject() or toJSON().{ virtuals: true }传递给toObject()toJSON()

You can also add a custom setter to your virtual that will let you set both first name and last name via the fullName virtual.您还可以向虚拟机添加一个自定义setter,它将允许您通过fullName虚拟机设置名字和姓氏。

// Again that can be done either by adding it to schema options:同样,这可以通过将其添加到架构选项中来完成:
const personSchema = new Schema({
name: {
first: String,
last: String
}
}, {
virtuals: {
fullName: {
get() {
return this.name.first + ' ' + this.name.last;
},
set(v) {
this.name.first = v.substr(0, v.indexOf(' '));
this.name.last = v.substr(v.indexOf(' ') + 1);
}
}
}
});

// Or by using the virtual method as following:或者使用以下虚拟方法:
personSchema.virtual('fullName').
get(function() {
return this.name.first + ' ' + this.name.last;
}).
set(function(v) {
this.name.first = v.substr(0, v.indexOf(' '));
this.name.last = v.substr(v.indexOf(' ') + 1);
});

axl.fullName = 'William Rose'; // Now `axl.name.first` is "William"现在`axl.name.first`是“William”

Virtual property setters are applied before other validation. 在进行其他验证之前应用虚拟属性设置器。So the example above would still work even if the first and last name fields were required.因此,即使需要名字和姓氏字段,上面的例子仍然有效。

Only non-virtual properties work as part of queries and for field selection. Since virtuals are not stored in MongoDB, you can't query with them.只有非虚拟属性才能作为查询和字段选择的一部分。由于虚拟机不存储在MongoDB中,因此您无法使用它们进行查询。

You can learn more about virtuals here.你可以在这里了解更多关于虚拟人的知识。

Aliases别名

Aliases are a particular type of virtual where the getter and setter seamlessly get and set another property. 别名是一种特殊类型的虚拟,getter和setter可以无缝地获取和设置另一个属性。This is handy for saving network bandwidth, so you can convert a short property name stored in the database into a longer name for code readability.这对于节省网络带宽很方便,因此您可以将存储在数据库中的短属性名称转换为较长的名称,以提高代码的可读性。

const personSchema = new Schema({
n: {
type: String,
// Now accessing `name` will get you the value of `n`, and setting `name` will set the value of `n`现在访问`name`将获得`n`的值,而设置`name`将设置`n`的值
alias: 'name'
}
});

// Setting `name` will propagate to `n`设置`name`将传播到`n`
const person = new Person({ name: 'Val' });
console.log(person); // { n: 'Val' }
console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }
console.log(person.name); // "Val"

person.name = 'Not Val';
console.log(person); // { n: 'Not Val' }

You can also declare aliases on nested paths. 您也可以在嵌套路径上声明别名。It is easier to use nested schemas and subdocuments, but you can also declare nested path aliases inline as long as you use the full nested path nested.myProp as the alias.使用嵌套的架构和子文档更容易,但也可以内联声明嵌套的路径别名,只要使用完整的嵌套路径nested.myProp作为别名即可。

const childSchema = new Schema({
n: {
type: String,
alias: 'name'
}
}, { _id: false });

const parentSchema = new Schema({
// If in a child schema, alias doesn't need to include the full nested path如果在子架构中,则别名不需要包括完整的嵌套路径
c: childSchema,
name: {
f: {
type: String,
// Alias needs to include the full nested path if declared inline如果声明为内联,则Alias需要包括完整的嵌套路径
alias: 'name.first'
}
}
});

Options选项

Schemas have a few configurable options which can be passed to the constructor or to the set method:架构有一些可配置的选项,这些选项可以传递给构造函数或set方法:

new Schema({ /* ... */ }, options);

// or

const schema = new Schema({ /* ... */ });
schema.set(option, value);

Valid options:有效选项:

option:选项:autoIndex

By default, Mongoose's init() function creates all the indexes defined in your model's schema by calling Model.createIndexes() after you successfully connect to MongoDB. 默认情况下,Mongoose的init()函数在成功连接到MongoDB后通过调用Model.createIndexes()来创建模型架构中定义的所有索引。Creating indexes automatically is great for development and test environments. 自动创建索引对于开发和测试环境非常有用。But index builds can also create significant load on your production database. 但是索引构建也会给生产数据库带来很大的负载。If you want to manage indexes carefully in production, you can set autoIndex to false.如果您想在生产中谨慎地管理索引,可以将autoIndex设置为false

const schema = new Schema({ /* ... */ }, { autoIndex: false });
const Clock = mongoose.model('Clock', schema);
Clock.ensureIndexes(callback);

The autoIndex option is set to true by default. 默认情况下,autoIndex选项设置为trueYou can change this default by setting mongoose.set('autoIndex', false);您可以通过设置mongoose.set('autoIndex', false);来更改此默认值;

option:选项:autoCreate

Before Mongoose builds indexes, it calls Model.createCollection() to create the underlying collection in MongoDB by default. 在Mongoose构建索引之前,默认情况下,它会调用Model.createCollection()在MongoDB中创建底层集合。Calling createCollection() sets the collection's default collation based on the collation option and establishes the collection as a capped collection if you set the capped schema option.调用createCollection()将基于collation选项设置集合的默认排序规则,并在设置了capped架构选项的情况下将集合建立为封顶集合。

You can disable this behavior by setting autoCreate to false using mongoose.set('autoCreate', false). 您可以通过使用mongoose.set('autoCreate', false)autoCreate设置为false来禁用此行为。Like autoIndex, autoCreate is helpful for development and test environments, but you may want to disable it for production to avoid unnecessary database calls.autoIndex一样,autoCreate对开发和测试环境很有帮助,但您可能希望在生产环境中禁用它,以避免不必要的数据库调用。

Unfortunately, createCollection() cannot change an existing collection. 很遗憾,createCollection()无法更改现有集合。For example, if you add capped: { size: 1024 } to your schema and the existing collection is not capped, createCollection() will not overwrite the existing collection. 例如,如果将capped: { size: 1024 }添加到架构中,而现有集合没有封顶,那么createCollection()将不会覆盖现有集合。That is because the MongoDB server does not allow changing a collection's options without dropping the collection first.这是因为MongoDB服务器不允许在不首先删除集合的情况下更改集合的选项。

const schema = new Schema({ name: String }, {
autoCreate: false,
capped: { size: 1024 }
});
const Test = mongoose.model('Test', schema);

// No-op if collection already exists, even if the collection is not capped.如果集合已经存在,即使集合没有上限,也不执行操作。
// This means that `capped` won't be applied if the 'tests' collection already exists.这意味着,如果“tests”集合已经存在,则不会应用`capped`。
await Test.createCollection();

option:选项:bufferCommands

By default, mongoose buffers commands when the connection goes down until the driver manages to reconnect. 默认情况下,mongoose会在连接中断时缓冲命令,直到驱动程序能够重新连接为止。To disable buffering, set bufferCommands to false.要禁用缓冲,请将bufferCommands设置为false

const schema = new Schema({ /* ... */ }, { bufferCommands: false });

The schema bufferCommands option overrides the global bufferCommands option.架构bufferCommands选项将覆盖全局bufferCommand选项。

mongoose.set('bufferCommands', true);
// Schema option below overrides the above, if the schema option is set.如果设置了架构选项,则下面的架构选项将覆盖上面的选项。
const schema = new Schema({ /* ... */ }, { bufferCommands: false });

option:选项:bufferTimeoutMS

If bufferCommands is on, this option sets the maximum amount of time Mongoose buffering will wait before throwing an error. 如果bufferCommands处于启用状态,则此选项设置Mongoose缓冲在抛出错误之前等待的最长时间。If not specified, Mongoose will use 10000 (10 seconds).如果未指定,Mongoose将使用10000(10秒)。

// If an operation is buffered for more than 1 second, throw an error.如果一个操作的缓冲时间超过1秒,则抛出一个错误。
const schema = new Schema({ /* ... */ }, { bufferTimeoutMS: 1000 });

option:选项:capped

Mongoose supports MongoDBs capped collections. Mongoose支持MongoDB封顶的集合。To specify the underlying MongoDB collection be capped, set the capped option to the maximum size of the collection in bytes.要指定要封顶的底层MongoDB集合,请将capped选项设置为集合的最大大小(以字节为单位)。

new Schema({ /* ... */ }, { capped: 1024 });

The capped option may also be set to an object if you want to pass additional options like max. 如果要传递其他选项(如最大值),也可以将capped选项设置为对象。In this case you must explicitly pass the size option, which is required.在这种情况下,必须显式传递size选项,这是必需的。

new Schema({ /* ... */ }, { capped: { size: 1024, max: 1000, autoIndexId: true } });

option:选项:collection

Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName method. 默认情况下,Mongoose通过将模型名称传递给utils.toCollectionName方法来生成集合名称。This method pluralizes the name. Set this option if you need a different name for your collection.此方法将名称复数化。如果您的集合需要其他名称,请设置此选项。

const dataSchema = new Schema({ /* ... */ }, { collection: 'data' });

option:选项:discriminatorKey

When you define a discriminator, Mongoose adds a path to your schema that stores which discriminator a document is an instance of. 当您定义鉴别器时,Mongoose会向您的架构添加一个路径,用于存储文档是哪个鉴别器的实例。By default, Mongoose adds an __t path, but you can set discriminatorKey to overwrite this default.默认情况下,Mongoose会添加一个__t路径,但您可以设置discriminatorKey来覆盖此默认路径。

const baseSchema = new Schema({}, { discriminatorKey: 'type' });
const BaseModel = mongoose.model('Test', baseSchema);

const personSchema = new Schema({ name: String });
const PersonModel = BaseModel.discriminator('Person', personSchema);

const doc = new PersonModel({ name: 'James T. Kirk' });
// Without `discriminatorKey`, Mongoose would store the discriminator如果没有`discriminatorKey`,Mongoose将存储鉴别器
// key in `__t` instead of `type`
doc.type; // 'Person'

option:选项:id

Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. Mongoose默认情况下为每个架构分配一个id虚拟getter,该getter将文档的_id字段强制转换为字符串,或者在ObjectId的情况下,返回其hexString。If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.如果你不想在你的架构中添加一个id getter,你可以在架构构建时通过传递这个选项来禁用它。

// default behavior默认行为
const schema = new Schema({ name: String });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p.id); // '50341373e894ad16347efe01'

// disabled id
const schema = new Schema({ name: String }, { id: false });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p.id); // undefined

option:选项:_id

Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor. 默认情况下,如果没有将_id字段传递给Schema构造函数,Mongoose会为每个架构分配一个_id字段。The type assigned is an ObjectId to coincide with MongoDB's default behavior. 分配的类型是一个ObjectId,与MongoDB的默认行为一致。If you don't want an _id added to your schema at all, you may disable it using this option.如果您根本不想在架构中添加_id,可以使用此选项禁用它。

You can only use this option on subdocuments. 您只能对子文档使用此选项。Mongoose can't save a document without knowing its id, so you will get an error if you try to save a document without an _id.Mongoose无法在不知道id的情况下保存文档,因此如果您试图保存没有_id的文档,则会出现错误。

// default behavior默认行为
const schema = new Schema({ name: String });
const Page = mongoose.model('Page', schema);
const p = new Page({ name: 'mongodb.org' });
console.log(p); // { _id: '50341373e894ad16347efe01', name: 'mongodb.org' }

// disabled禁用的 _id
const childSchema = new Schema({ name: String }, { _id: false });
const parentSchema = new Schema({ children: [childSchema] });

const Model = mongoose.model('Model', parentSchema);

Model.create({ children: [{ name: 'Luke' }] }, (error, doc) => {
// doc.children[0]._id will be 将为undefined
});

option:选项:minimize

Mongoose will, by default, "minimize" schemas by removing empty objects.默认情况下,Mongoose将通过删除空对象来“最小化”架构。

const schema = new Schema({ name: String, inventory: {} });
const Character = mongoose.model('Character', schema);

// will store `inventory` field if it is not empty如果不为空,将存储`inventory`字段
const frodo = new Character({ name: 'Frodo', inventory: { ringOfPower: 1 } });
await frodo.save();
let doc = await Character.findOne({ name: 'Frodo' }).lean();
doc.inventory; // { ringOfPower: 1 }

// will not store `inventory` field if it is empty如果`inventory`字段为空,则不会存储该字段
const sam = new Character({ name: 'Sam', inventory: {} });
await sam.save();
doc = await Character.findOne({ name: 'Sam' }).lean();
doc.inventory; // undefined

This behavior can be overridden by setting minimize option to false. 可以通过将minimize选项设置为false来覆盖此行为。It will then store empty objects.然后它将存储空对象。

const schema = new Schema({ name: String, inventory: {} }, { minimize: false });
const Character = mongoose.model('Character', schema);

// will store `inventory` if empty如果`inventory`为空,将存储`inventory`
const sam = new Character({ name: 'Sam', inventory: {} });
await sam.save();
doc = await Character.findOne({ name: 'Sam' }).lean();
doc.inventory; // {}

To check whether an object is empty, you can use the $isEmpty() helper:要检查对象是否为空,可以使用$isEmpty()助手:

const sam = new Character({ name: 'Sam', inventory: {} });
sam.$isEmpty('inventory'); // true

sam.inventory.barrowBlade = 1;
sam.$isEmpty('inventory'); // false

option:选项:read

Allows setting query#read options at the schema level, providing us a way to apply default ReadPreferences to all queries derived from a model.允许在架构级别设置query#read选项,为我们提供了一种将默认ReadPreferences应用于从模型派生的所有查询的方法。

const schema = new Schema({ /* ... */ }, { read: 'primary' });            // also aliased as 'p'
const schema = new Schema({ /* ... */ }, { read: 'primaryPreferred' }); // aliased as 'pp'
const schema = new Schema({ /* ... */ }, { read: 'secondary' }); // aliased as 's'
const schema = new Schema({ /* ... */ }, { read: 'secondaryPreferred' }); // aliased as 'sp'
const schema = new Schema({ /* ... */ }, { read: 'nearest' }); // aliased as 'n'

The alias of each pref is also permitted so instead of having to type out 'secondaryPreferred' and getting the spelling wrong, we can simply pass 'sp'.每个pref的别名也是允许的,所以我们可以简单地传递“sp”,而不必键入“secondary Preferred”并拼写错误。

The read option also allows us to specify tag sets. 读取选项还允许我们指定标记集These tell the driver from which members of the replica-set it should attempt to read. 这些信息告诉驱动程序应该尝试从复制集的哪些成员中读取。Read more about tag sets here and here.此处此处阅读有关标记集的更多信息。

NOTE: you may also specify the driver read preference strategy option when connecting:注意:连接时,您也可以指定驱动程序读取首选strategy选项:

// pings the replset members periodically to track network latency定期ping复制集成员以跟踪网络延迟
const options = { replset: { strategy: 'ping' } };
mongoose.connect(uri, options);

const schema = new Schema({ /* ... */ }, { read: ['nearest', { disk: 'ssd' }] });
mongoose.model('JellyBean', schema);

option:选项:writeConcern

Allows setting write concern at the schema level.允许在架构级别设置写入关注

const schema = new Schema({ name: String }, {
writeConcern: {
w: 'majority',
j: true,
wtimeout: 1000
}
});

option:选项:shardKey

The shardKey option is used when we have a sharded MongoDB architecture. 当我们有一个分片的MongoDB架构时,会使用shardKey选项。Each sharded collection is given a shard key which must be present in all insert/update operations. 每个分片集合都有一个分片密钥,该密钥必须存在于所有插入/更新操作中。We just need to set this schema option to the same shard key and we’ll be all set.我们只需要将这个架构选项设置为相同的分片键,我们就可以完成所有设置了。

new Schema({ /* ... */ }, { shardKey: { tag: 1, name: 1 } });

Note that Mongoose does not send the shardcollection command for you. 请注意,Mongoose不会为您发送shardcollection命令。You must configure your shards yourself.您必须自己配置碎片。

option:选项:strict

The strict option, (enabled by default), ensures that values passed to our model constructor that were not specified in our schema do not get saved to the db.严格选项(默认启用)确保传递给模型构造函数的值不会被保存到数据库中,而这些值没有在架构中指定。

const thingSchema = new Schema({ /* ... */ })
const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing({ iAmNotInTheSchema: true });
thing.save(); // iAmNotInTheSchema is not saved to the dbiAmNotInTheSchema未保存到数据库

// set to false..
const thingSchema = new Schema({ /* ... */ }, { strict: false });
const thing = new Thing({ iAmNotInTheSchema: true });
thing.save(); // iAmNotInTheSchema is now saved to the db!!现在已保存到数据库!!

This also affects the use of doc.set() to set a property value.这也会影响使用doc.set()来设置属性值。

const thingSchema = new Schema({ /* ... */ });
const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing;
thing.set('iAmNotInTheSchema', true);
thing.save(); // iAmNotInTheSchema is not saved to the dbiAmNotInTheSchema未保存到数据库

This value can be overridden at the model instance level by passing a second boolean argument:通过传递第二个布尔参数,可以在模型实例级别重写此值:

const Thing = mongoose.model('Thing');
const thing = new Thing(doc, true); // enables strict mode启用严格架构
const thing = new Thing(doc, false); // disables strict mode禁用严格架构

The strict option may also be set to "throw" which will cause errors to be produced instead of dropping the bad data.strict选项也可以设置为"throw",这将导致产生错误,而不是丢弃坏数据。

NOTE: Any key/val set on the instance that does not exist in your schema is always ignored, regardless of schema option.注意:无论架构选项如何,实例上不存在的任何key/val集都将始终被忽略。

const thingSchema = new Schema({ /* ... */ });
const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing;
thing.iAmNotInTheSchema = true;
thing.save(); // iAmNotInTheSchema is never saved to the db从未保存到数据库

option:选项:strictQuery

Mongoose supports a separate strictQuery option to avoid strict mode for query filters. Mongoose支持单独的strictQuery选项,以避免查询筛选器的严格架构。This is because empty query filters cause Mongoose to return all documents in the model, which can cause issues.这是因为空的查询筛选器会导致Mongoose返回模型中的所有文档,这可能会导致问题。

const mySchema = new Schema({ field: Number }, { strict: true });
const MyModel = mongoose.model('Test', mySchema);
// Mongoose will filter out `notInSchema: 1` because `strict: true`, meaning this query will returnMongoose将筛选出`notInSchema: 1`,因为`strict: true`,这意味着此查询将返回
// _all_ documents in the 'tests' collection“tests”集合中的_all_文档
MyModel.find({ notInSchema: 1 });

The strict option does apply to updates. strict选项确实适用于更新。The strictQuery option is just for query filters.strictQuery选项仅用于查询筛选器。

// Mongoose will strip out `notInSchema` from the update if `strict` is not `false`如果`strict`不是`false`,Mongoose将从更新中删除`notInSchema`
MyModel.updateMany({}, { $set: { notInSchema: 1 } });

Mongoose has a separate strictQuery option to toggle strict mode for the filter parameter to queries.Mongoose有一个单独的strictQuery选项,用于将filter参数的严格架构切换到查询。

const mySchema = new Schema({ field: Number }, {
strict: true,
strictQuery: false // Turn off strict mode for query filters关闭查询筛选器的严格架构
});
const MyModel = mongoose.model('Test', mySchema);
// Mongoose will not strip out `notInSchema: 1` because `strictQuery` is falseMongoose不会删除`notInSchema:1`,因为`strictQuery`为false
MyModel.find({ notInSchema: 1 });

In general, we do not recommend passing user-defined objects as query filters:通常,我们不建议将用户定义的对象作为查询筛选器传递:

// Don't do this!不要这样做!
const docs = await MyModel.find(req.query);

// Do this instead:请这样做:
const docs = await MyModel.find({ name: req.query.name, age: req.query.age }).setOptions({ sanitizeFilter: true });

In Mongoose 7, strictQuery is false by default. 在Mongoose 7中,strictQuery默认为falseHowever, you can override this behavior globally:但是,您可以全局覆盖此行为:

// Set `strictQuery` to `true` to omit unknown fields in queries.将`strictQuery`设置为`true`可省略查询中的未知字段。
mongoose.set('strictQuery', true);

option:选项:toJSON

Exactly the same as the toObject option but only applies when the document's toJSON method is called.toObject选项完全相同,但仅在调用文档的toJSON方法时适用。

const schema = new Schema({ name: String });
schema.path('name').get(function(v) {
return v + ' is my name';
});
schema.set('toJSON', { getters: true, virtuals: false });
const M = mongoose.model('Person', schema);
const m = new M({ name: 'Max Headroom' });
console.log(m.toObject()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom' }
console.log(m.toJSON()); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }
// since we know toJSON is called whenever a js object is stringified:因为我们知道只要对js对象进行字符串化,就会调用toJSON:
console.log(JSON.stringify(m)); // { "_id": "504e0cd7dd992d9be2f20b6f", "name": "Max Headroom is my name" }

To see all available toJSON/toObject options, read this.要查看所有可用的toJSON/toObject选项,请阅读本文

option:选项:toObject

Documents have a toObject method which converts the mongoose document into a plain JavaScript object. 文档有一个toObject方法,它将mongoose文档转换为一个普通的JavaScript对象。This method accepts a few options. 此方法接受一些选项。Instead of applying these options on a per-document basis, we may declare the options at the schema level and have them applied to all of the schema's documents by default.我们可以在架构级别声明这些选项,并在默认情况下将其应用于架构的所有文档,而不是在每个文档的基础上应用这些选项。

To have all virtuals show up in your console.log output, set the toObject option to { getters: true }:要使所有虚拟机都显示在console.log输出中,请将toObject选项设置为{ getters: true }

const schema = new Schema({ name: String });
schema.path('name').get(function(v) {
return v + ' is my name';
});
schema.set('toObject', { getters: true });
const M = mongoose.model('Person', schema);
const m = new M({ name: 'Max Headroom' });
console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' }

To see all available toObject options, read this.要查看所有可用的toObject选项,请阅读本文

option:选项:typeKey

By default, if you have an object with key 'type' in your schema, mongoose will interpret it as a type declaration.默认情况下,如果您的架构中有一个键为“type”的对象,mongoose会将其解释为类型声明。

// Mongoose interprets this as 'loc is a String'Mongoose将其解释为“loc是一个字符串”
const schema = new Schema({ loc: { type: String, coordinates: [Number] } });

However, for applications like geoJSON, the 'type' property is important. 然而,对于像geoJSON这样的应用程序,“type”属性很重要。If you want to control which key mongoose uses to find type declarations, set the 'typeKey' schema option.如果您想控制mongoose使用哪个密钥来查找类型声明,请设置“typeKey”架构选项。

const schema = new Schema({
// Mongoose interprets this as 'loc is an object with 2 keys, type and coordinates'Mongoose将其解释为“loc是一个有2个键、类型和坐标的对象”
loc: { type: String, coordinates: [Number] },
// Mongoose interprets this as 'name is a String'Mongoose将其解释为“name is a String”
name: { $type: String }
}, { typeKey: '$type' }); // A '$type' key means this object is a type declaration“$type”键表示此对象是类型声明

option:选项:validateBeforeSave

By default, documents are automatically validated before they are saved to the database. 默认情况下,文档在保存到数据库之前会自动进行验证。This is to prevent saving an invalid document. 这是为了防止保存无效文档。If you want to handle validation manually, and be able to save objects which don't pass validation, you can set validateBeforeSave to false.如果您想手动处理验证,并且能够保存未通过验证的对象,可以将validateBeforeSave设置为false

const schema = new Schema({ name: String });
schema.set('validateBeforeSave', false);
schema.path('name').validate(function(value) {
return value != null;
});
const M = mongoose.model('Person', schema);
const m = new M({ name: null });
m.validate(function(err) {
console.log(err); // Will tell you that null is not allowed.会告诉你不允许为null。
});
m.save(); // Succeeds despite being invalid尽管无效,但仍成功

option:选项:versionKey

The versionKey is a property set on each document when first created by Mongoose. versionKey是Mongoose首次创建时在每个文档上设置的属性。This keys value contains the internal revision of the document. 此键值包含文档的内部修订The versionKey option is a string that represents the path to use for versioning. versionKey选项是一个字符串,表示用于版本控制的路径。The default is __v. 默认值为__vIf this conflicts with your application you can configure as such:如果这与您的应用程序冲突,您可以这样配置:

const schema = new Schema({ name: 'string' });
const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'mongoose v3' });
await thing.save(); // { __v: 0, name: 'mongoose v3' }

// customized versionKey自定义版本密钥
new Schema({ /* ... */ }, { versionKey: '_somethingElse' })
const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'mongoose v3' });
thing.save(); // { _somethingElse: 0, name: 'mongoose v3' }

Note that Mongoose's default versioning is not a full optimistic concurrency solution. 请注意,Mongoose的默认版本控制并不是一个完全乐观的并发解决方案。Mongoose's default versioning only operates on arrays as shown below.Mongoose的默认版本控制仅在数组上运行,如下所示。

// 2 copies of the same document同一份文件的2份副本
const doc1 = await Model.findOne({ _id });
const doc2 = await Model.findOne({ _id });

// Delete first 3 comments from `doc1`从`doc1`中删除前3条注释
doc1.comments.splice(0, 3);
await doc1.save();

// The below `save()` will throw a VersionError, because you're trying to modify the comment at index 1, and the above `splice()` removed that comment.下面的`save()`将抛出VersionError,因为您正试图修改索引1处的注释,而上面的`splice()`删除了该注释。
doc2.set('comments.1.body', 'new comment');
await doc2.save();

If you need optimistic concurrency support for save(), you can set the optimisticConcurrency option如果需要对save()提供乐观并发支持,可以设置optimisticConcurrency选项

Document versioning can also be disabled by setting the versionKey to false. 还可以通过将versionKey设置为false来禁用文档版本控制。DO NOT disable versioning unless you know what you are doing.除非你知道自己在做什么,否则不要禁用版本控制。

new Schema({ /* ... */ }, { versionKey: false });
const Thing = mongoose.model('Thing', schema);
const thing = new Thing({ name: 'no versioning please' });
thing.save(); // { name: 'no versioning please' }

Mongoose only updates the version key when you use save(). Mongoose只在使用save()时更新版本密钥。If you use update(), findOneAndUpdate(), etc. Mongoose will not update the version key. 如果您使用update()findOneAndUpdate()等,Mongoose将不会更新版本密钥。As a workaround, you can use the below middleware.作为一种变通方法,您可以使用以下中间件。

schema.pre('findOneAndUpdate', function() {
const update = this.getUpdate();
if (update.__v != null) {
delete update.__v;
}
const keys = ['$set', '$setOnInsert'];
for (const key of keys) {
if (update[key] != null && update[key].__v != null) {
delete update[key].__v;
if (Object.keys(update[key]).length === 0) {
delete update[key];
}
}
}
update.$inc = update.$inc || {};
update.$inc.__v = 1;
});

option:选项:optimisticConcurrency

Optimistic concurrency is a strategy to ensure the document you're updating didn't change between when you loaded it using find() or findOne(), and when you update it using save().乐观并发是一种策略,可以确保您正在更新的文档在使用find()findOne()加载时和使用save()更新时不会发生变化。

For example, suppose you have a House model that contains a list of photos, and a status that represents whether this house shows up in searches. 例如,假设您有一个House模型,其中包含一个photos列表,以及一个表示此房子是否显示在搜索中的statusSuppose that a house that has status 'APPROVED' must have at least two photos. 假设一栋状态为'APPROVED'的房子必须至少有两张照片。You might implement the logic of approving a house document as shown below:您可以实现批准房屋文件的逻辑,如下所示:

async function markApproved(id) {
const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

house.status = 'APPROVED';
await house.save();
}

The markApproved() function looks right in isolation, but there might be a potential issue: what if another function removes the house's photos between the findOne() call and the save() call? markApproved()函数看起来是孤立的,但可能存在一个潜在的问题:如果另一个函数在findOne()调用和save()调用之间删除房子的照片怎么办?For example, the below code will succeed:例如,以下代码将成功:

const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });
house2.photos = [];
await house2.save();

// Marks the house as 'APPROVED' even though it has 0 photos!将房子标记为“已批准”,即使它有0张照片!
house.status = 'APPROVED';
await house.save();

If you set the optimisticConcurrency option on the House model's schema, the above script will throw an error.如果您在House模型的架构上设置了optimisticConcurrency选项,那么上面的脚本将抛出一个错误。

const House = mongoose.model('House', Schema({
status: String,
photos: [String]
}, { optimisticConcurrency: true }));

const house = await House.findOne({ _id });
if (house.photos.length < 2) {
throw new Error('House must have at least two photos!');
}

const house2 = await House.findOne({ _id });
house2.photos = [];
await house2.save();

// Throws 'VersionError: No matching document found for id "..." version 0'引发“VersionError:找不到id“…”版本0的匹配文档”
house.status = 'APPROVED';
await house.save();

option:选项:collation

Sets a default collation for every query and aggregation. 为每个查询和聚合设置默认排序规则Here's a beginner-friendly overview of collations.这是一个初学者友好的排序规则概述

const schema = new Schema({
name: String
}, { collation: { locale: 'en_US', strength: 1 } });

const MyModel = db.model('MyModel', schema);

MyModel.create([{ name: 'val' }, { name: 'Val' }]).
then(() => {
return MyModel.find({ name: 'val' });
}).
then((docs) => {
// `docs` will contain both docs, because 将包含两个文档,因为`strength: 1` means
// MongoDB will ignore case when matching.MongoDB在匹配时将忽略大小写。
});

option:选项:timeseries

If you set the timeseries option on a schema, Mongoose will create a timeseries collection for any model that you create from that schema.如果您在一个架构上设置timeseries选项,Mongoose将为您从该架构创建的任何模型创建一个时间序列集合

const schema = Schema({ name: String, timestamp: Date, metadata: Object }, {
timeseries: {
timeField: 'timestamp',
metaField: 'metadata',
granularity: 'hours'
},
autoCreate: false,
expireAfterSeconds: 86400
});

// `Test` collection will be a timeseries collection集合将是时间序列集合
const Test = db.model('Test', schema);

option:选项:skipVersioning

skipVersioning allows excluding paths from versioning (i.e., the internal revision will not be incremented even if these paths are updated). 允许从版本控制中排除路径(即,即使更新了这些路径,内部修订也不会增加)。DO NOT do this unless you know what you're doing. 除非你知道自己在做什么,否则不要这样做。For subdocuments, include this on the parent document using the fully qualified path.对于子文档,请使用完全限定路径将其包含在父文档中。

new Schema({ /* ... */ }, { skipVersioning: { dontVersionMe: true } });
thing.dontVersionMe.push('hey');
thing.save(); // version is not incremented

option:选项:timestamps

The timestamps option tells Mongoose to assign createdAt and updatedAt fields to your schema. timestamps选项告诉Mongoose将createdAtupdatedAt字段分配给您的架构。The type assigned is Date.指定的类型为Date

By default, the names of the fields are createdAt and updatedAt. 默认情况下,字段的名称为createdAtupdatedAtCustomize the field names by setting timestamps.createdAt and timestamps.updatedAt.通过设置timestamps.createdAttimestamps.updatedAt来自定义字段名称。

The way timestamps works under the hood is:timestamps在底层的工作方式是:

  • If you create a new document, mongoose simply sets createdAt, and updatedAt to the time of creation.如果您创建了一个新文档,mongoose只需将createdAtupdatedAt设置为创建时间。
  • If you update a document, mongoose will add updatedAt to the $set object.如果您更新文档,mongoose会将updatedAt添加到$set对象中。
  • If you set upsert: true on an update operation, mongoose will use $setOnInsert operator to add createdAt to the document in case the upsert operation resulted into a new inserted document.如果在更新操作中设置了upstart: true,mongoose将使用$setOnInsert运算符将createdAt添加到文档中,以防upstart操作导致新插入的文档。
const thingSchema = new Schema({ /* ... */ }, { timestamps: { createdAt: 'created_at' } });
const Thing = mongoose.model('Thing', thingSchema);
const thing = new Thing();
await thing.save(); // `created_at` & `updatedAt` will be included

// With updates, Mongoose will add `updatedAt` to `$set`通过更新,Mongoose将`updatedAt`添加到`$set`中
await Thing.updateOne({}, { $set: { name: 'Test' } });

// If you set upsert: true, Mongoose will add `created_at` to `$setOnInsert` as well如果设置了upstart: true,Mongoose也会将`created_at`添加到`$setOnInsert`中
await Thing.findOneAndUpdate({}, { $set: { name: 'Test2' } });

// Mongoose also adds timestamps to bulkWrite() operationsMongoose还为bulkWrite()操作添加了时间戳
// See 请参阅https://mongoosejs.com/docs/api/model.html#model_Model-bulkWrite
await Thing.bulkWrite([
{
insertOne: {
document: {
name: 'Jean-Luc Picard',
ship: 'USS Stargazer'
// Mongoose will add Mongoose将添加`created_at` and `updatedAt`
}
}
},
{
updateOne: {
filter: { name: 'Jean-Luc Picard' },
update: {
$set: {
ship: 'USS Enterprise'
// Mongoose will add 将添加`updatedAt`
}
}
}
}
]);

By default, Mongoose uses new Date() to get the current time. 默认情况下,Mongoose使用new Date()来获取当前时间。If you want to overwrite the function Mongoose uses to get the current time, you can set the timestamps.currentTime option. 如果你想覆盖Mongoose用来获取当前时间的函数,你可以设置timestamps.currentTime选项。Mongoose will call the timestamps.currentTime function whenever it needs to get the current time.每当需要获取当前时间时,Mongoose都会调用timestamps.currentTime函数。

const schema = Schema({
createdAt: Number,
updatedAt: Number,
name: String
}, {
// Make Mongoose use Unix time让Mongoose使用Unix时间 (seconds since Jan 1, 1970)
timestamps: { currentTime: () => Math.floor(Date.now() / 1000) }
});

option:选项:pluginTags

Mongoose supports defining global plugins, plugins that apply to all schemas.Mongoose支持定义全局插件,即适用于所有架构的插件。

// Add a `meta` property to all schemas将`meta`属性添加到所有架构
mongoose.plugin(function myPlugin(schema) {
schema.add({ meta: {} });
});

Sometimes, you may only want to apply a given plugin to some schemas. 有时,您可能只想将给定的插件应用于某些架构。In that case, you can add pluginTags to a schema:在这种情况下,您可以将pluginTags添加到架构中:

const schema1 = new Schema({
name: String
}, { pluginTags: ['useMetaPlugin'] });

const schema2 = new Schema({
name: String
});

If you call plugin() with a tags option, Mongoose will only apply that plugin to schemas that have a matching entry in pluginTags.如果您使用tags选项调用plugin(),Mongoose将只将该插件应用于pluginTags中有匹配条目的架构。

// Add a `meta` property to all schemas将`meta`属性添加到所有架构
mongoose.plugin(function myPlugin(schema) {
schema.add({ meta: {} });
}, { tags: ['useMetaPlugin'] });

option:选项:selectPopulatedPaths

By default, Mongoose will automatically select() any populated paths for you, unless you explicitly exclude them.默认情况下,Mongoose会自动为您select()任何已填充的路径,除非您明确排除它们。

const bookSchema = new Schema({
title: 'String',
author: { type: 'ObjectId', ref: 'Person' }
});
const Book = mongoose.model('Book', bookSchema);

// By default, Mongoose will add `author` to the below `select()`.默认情况下,Mongoose会将`author`添加到下面的`select()`中。
await Book.find().select('title').populate('author');

// In other words, the below query is equivalent to the above换句话说,下面的查询等同于上面的查询
await Book.find().select('title author').populate('author');

To opt out of selecting populated fields by default, set selectPopulatedPaths to false in your schema.要在默认情况下选择不选择已填充的字段,请在架构中将selectPopulatedPaths设置为false

const bookSchema = new Schema({
title: 'String',
author: { type: 'ObjectId', ref: 'Person' }
}, { selectPopulatedPaths: false });
const Book = mongoose.model('Book', bookSchema);

// Because `selectPopulatedPaths` is false, the below doc will **not** contain an `author` property.由于`selectPopulatedPaths`为false,因此下面的文档**不会**包含author属性。
const doc = await Book.findOne().select('title').populate('author');

option:选项:storeSubdocValidationError

For legacy reasons, when there is a validation error in subpath of a single nested schema, Mongoose will record that there was a validation error in the single nested schema path as well. 由于遗留的原因,当单个嵌套架构的子路径中存在验证错误时,Mongoose将记录单个嵌套架构路径中也存在验证错误。For example:例如:

const childSchema = new Schema({ name: { type: String, required: true } });
const parentSchema = new Schema({ child: childSchema });

const Parent = mongoose.model('Parent', parentSchema);

// Will contain an error for both 'child.name' _and_ 'child'将对“childname”和“child”包含错误
new Parent({ child: {} }).validateSync().errors;

Set the storeSubdocValidationError to false on the child schema to make Mongoose only reports the parent error.在子架构上将storeSubdocValidationError设置为false,使Mongoose只报告父错误。

const childSchema = new Schema({
name: { type: String, required: true }
}, { storeSubdocValidationError: false }); // <-- set on the child schema
const parentSchema = new Schema({ child: childSchema });

const Parent = mongoose.model('Parent', parentSchema);

// Will only contain an error for 'child.name'将只包含“child.name”的错误
new Parent({ child: {} }).validateSync().errors;

With ES6 Classes使用ES6类

Schemas have a loadClass() method that you can use to create a Mongoose schema from an ES6 class:架构有一个loadClass()方法,可以用来从ES6类创建Mongoose架构:

Here's an example of using loadClass() to create a schema from an ES6 class:下面是一个使用loadClass()从ES6类创建架构的示例:

class MyClass {
myMethod() { return 42; }
static myStatic() { return 42; }
get myVirtual() { return 42; }
}

const schema = new mongoose.Schema();
schema.loadClass(MyClass);

console.log(schema.methods); // { myMethod: [Function: myMethod] }
console.log(schema.statics); // { myStatic: [Function: myStatic] }
console.log(schema.virtuals); // { myVirtual: VirtualType { ... } }

Pluggable可插拔

Schemas are also pluggable which allows us to package up reusable features into plugins that can be shared with the community or just between your projects.架构也是可插拔的,这使我们能够将可重复使用的功能打包到插件中,这些插件可以与社区共享,也可以在您的项目之间共享。

Further Reading进一步阅读

Here's an alternative introduction to Mongoose schemas.下面是Mongoose架构的另一个介绍

To get the most out of MongoDB, you need to learn the basics of MongoDB schema design. 为了充分利用MongoDB,您需要学习MongoDB架构设计的基础知识。SQL schema design (third normal form) was designed to minimize storage costs, whereas MongoDB schema design is about making common queries as fast as possible. SQL架构设计(第三种正常形式)是为了最大限度地降低存储成本,而MongoDB架构设计是为了尽可能快地进行常见查询。The 6 Rules of Thumb for MongoDB Schema Design blog series is an excellent resource for learning the basic rules for making your queries fast.MongoDB Schema Design的6条拇指规则博客系列是学习快速查询基本规则的绝佳资源。

Users looking to master MongoDB schema design in Node.js should look into The Little MongoDB Schema Design Book by Christian Kvalheim, the original author of the MongoDB Node.js driver. 想要在Node.js中掌握MongoDB模式设计的用户应该看看MongoDB Node.js驱动程序的原作者Christian Kvalheim的《小MongoDB架构设计书》。This book shows you how to implement performant schemas for a laundry list of use cases, including e-commerce, wikis, and appointment bookings.这本书向您展示了如何为一系列用例实现性能架构,包括电子商务、Wiki和预约。

Next Up下一步

Now that we've covered Schemas, let's take a look at SchemaTypes.现在我们已经介绍了Schemas,让我们来看看架构类型