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定义架构Creating a model创建模型- Ids
Instance methods实例方法Statics静态方法Query Helpers查询帮助程序Indexes索引Virtuals虚拟机Aliases别名Options选项With ES6 Classes使用ES6类Pluggable可插拔Further Reading进一步阅读
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
中的每个键都定义了文档中的一个属性,该属性将被强制转换为其关联的SchemaType。For 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.votes
和meta.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 默认情况下,Mongoose会向您的架构添加一个_id
property to your schemas._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的默认_id
。Just be careful: Mongoose will refuse to save a document that doesn't have an 请注意:Mongoose将拒绝保存没有_id
, so you're responsible for setting _id
if you define your own _id
path._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()
functionSchema#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 使用mongoose,我们在路径级别或架构级别的Schema
at the path level or the schema
level. 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会自动为您的架构中的每个定义的索引调用createIndex。Mongoose will call Mongoose将依次为每个索引调用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. 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 当索引构建完成或发生错误时,Mongoose将在模型上发出一个index
event on the model when indexes are done building or an error occurred.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 虚拟属性getter允许您定义一个不会持久化到MongoDB的fullName
property that won't get persisted to 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 这包括在Mongoose文档上调用JSON.stringify()
calls toJSON()
. 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 您还可以向虚拟机添加一个自定义setter,它将允许您通过fullName
virtual.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:有效选项:
- autoIndex
- autoCreate
- bufferCommands
- bufferTimeoutMS
- capped
- collection
- discriminatorKey
- id
- _id
- minimize
- read
- writeConcern
- shardKey
- statics
- strict
- strictQuery
- toJSON
- toObject
- typeKey
- validateBeforeSave
- versionKey
- optimisticConcurrency
- collation
- timeseries
- selectPopulatedPaths
- skipVersioning
- timestamps
- storeSubdocValidationError
- methods
- query
option:选项:autoIndex
By default, Mongoose's 默认情况下,Mongoose的init()
function creates all the indexes defined in your model's schema by calling Model.createIndexes()
after you successfully connect to MongoDB. 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
选项设置为true
。You can change this default by setting 您可以通过设置mongoose.set('autoIndex', false);
mongoose.set('autoIndex', false);
来更改此默认值;
option:选项:autoCreate
Before Mongoose builds indexes, it calls 在Mongoose构建索引之前,默认情况下,它会调用Model.createCollection()
to create the underlying collection in MongoDB by default. 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 要指定要封顶的底层MongoDB集合,请将capped
, set the capped
option to the maximum size of the collection in bytes.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 默认情况下,Mongoose通过将模型名称传递给utils.toCollectionName
method. 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 默认情况下,Mongoose会添加一个__t
path, but you can set discriminatorKey
to overwrite this default.__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 Mongoose默认情况下为每个架构分配一个id
virtual getter by default which returns the document's _id
field cast to a string, or in the case of ObjectIds, its hexString. 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 Mongoose无法在不知道id的情况下保存文档,因此如果您试图保存没有_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 当我们有一个分片的MongoDB架构时,会使用shardKey
option is used when we have a sharded MongoDB architecture. 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 请注意,Mongoose不会为您发送shardcollection
command for you. 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 Mongoose支持单独的strictQuery
option to avoid strict mode for query filters. 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 Mongoose有一个单独的strictQuery
option to toggle strict mode for the filter
parameter to queries.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, 在Mongoose 7中,strictQuery
is false
by default. strictQuery
默认为false
。However, 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
. __v
。If 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
optionsave()
提供乐观并发支持,可以设置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
列表,以及一个表示此房子是否显示在搜索中的status
。Suppose 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将createdAt
和updatedAt
字段分配给您的架构。The type assigned is Date.指定的类型为Date
。
By default, the names of the fields are 默认情况下,字段的名称为createdAt
and updatedAt
. createdAt
和updatedAt
。Customize the field names by setting 通过设置timestamps.createdAt
and timestamps.updatedAt
.timestamps.createdAt
和timestamps.updatedAt
来自定义字段名称。
The way timestamps
works under the hood is:timestamps
在底层的工作方式是:
If you create a new document, mongoose simply sets如果您创建了一个新文档,mongoose只需将createdAt
, andupdatedAt
to the time of creation.createdAt
和updatedAt
设置为创建时间。If you update a document, mongoose will add如果您更新文档,mongoose会将updatedAt
to the$set
object.updatedAt
添加到$set
对象中。If you set如果在更新操作中设置了upsert: true
on an update operation, mongoose will use $setOnInsert operator to addcreatedAt
to the document in case theupsert
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 默认情况下,Mongoose使用new Date()
to get the current time. new Date()
来获取当前时间。If you want to overwrite the function Mongoose uses to get the current time, you can set the 如果你想覆盖Mongoose用来获取当前时间的函数,你可以设置timestamps.currentTime
option. timestamps.currentTime
选项。Mongoose will call the 每当需要获取当前时间时,Mongoose都会调用timestamps.currentTime
function whenever it needs to get the current time.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 默认情况下,Mongoose会自动为您select()
any populated paths for you, unless you explicitly exclude them.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架构:
- ES6 class methods
become成为Mongoose methods - ES6 class statics
become成为Mongoose statics - ES6 getters and setters
become成为Mongoose virtuals
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
,让我们来看看架构类型。