指南
假设以下代码都运行在
let mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
let db = mongoose.connection;
db.on('error', () => {
console.error('链接失败');
});
db.once('open', function () {
//下面讲到的所有代码都在这里运行
});
Schema
定义 schema
在Mongoose中,任何事情都是从Schema(模式)开始的。每一个模式映射到MongoDB集合,并且定义了这个集合中文档的模型。
let Schema = mongoose.Schema;
let blogSchema = new Schema({
title: String,
author: String,
body: String,
comments: [{
body: String,
data: Date
}],
date: {
type: Date, default: Date.now
},
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
});
在blogSchema中,我们定义的每一个key都被关联了一个SchemaType。比如title这个key,对应一个String SchemaType。date对应一个Date SchemaType。当然,这些key也可以对应一个对象,进行更深度的嵌套。
SchemaTypes 有以下几种:
- String
- Number
- Date
- Buffer
- Mixed
- ObjectId
- Array
Schema 不仅定义了你的文档结构和属性,而且也定义了文档的实例方法,静态的模型方法、复合索引和被称为中间件的文档生命周期钩子。
创建模型
要使用我们的模式定义,我们需要将我们的 blogSchema转换为我们可以使用的模型。为此,我们要将blogSchema传入 mongoose.model(modelName,schema):
var Blog = mongoose.model('Blog',blogSchema);
实例方法
模型的实例叫做文档。文档有许多自己的内置实例方法。我们也可以定义我们自己的自己的文档实例方法。
let Schema = mongoose.Schema;
//定义一个schema
let animalSchema = new Schema({
name: String,
type: String
});
//为我们的animalSchema的methods对象分配一个函数。
animalSchema.methods.findSimilarTypes = function (cb) {
console.log(this.model);
return this.model('Animal').find({ type: this.type }, cb);
}
//现在,我们的所有animal实例都有一个findSimilarTypes方法可以使用。
let Animal = mongoose.model('Animal', animalSchema);
let dog = new Animal({
type: 'dog'
});
dog.save(function (err, dog) {
dog.findSimilarTypes(function (err, dogs) {
console.log(dogs);
});
});
静态方法
添加静态方法给一个model也同样简单。继续我们的animalSchema
let Schema = mongoose.Schema;
let animalSchema = new Schema({
name: String,
type: String
});
//给我们animalsChema上的statics对象添加一个方法。
animalSchema.statics.findByName = function(name,cb){
this.find({name:new RegExp(name,'i')},cb);
}
let Animal = mongoose.model('Animal',animalSchema);
Animal.findByName('fido',function(err,target){
console.log(target)
});
索引
MongoDB支持二级索引。 使用mongoose,我们在路径级别或模式级别的模式中定义这些索引。 在创建复合索引时,必须在模式级别定义索引。
建议在生产中禁用此行为,因为索引创建可能会导致显着的性能影响。 通过将模式的autoIndex选项设置为false来禁用该行为。
animalSchema.set('autoIndex', false);
// or
new Schema({..}, { autoIndex: false });
虚拟
虚拟是指可以获取和设置但不会持久保存到MongoDB的文档属性。 getters对于格式化或组合字段很有用,而settings对于将单个值拆分为多个值以用于存储是有用的。
let Schema = mongoose.Schema;
//定义一个Schema
let personSchema = new Schema({
name:{
first:String,
last:String
}
});
//编译成model
let Person = mongoose.model('Person',personSchema);
// 创建一个文档
let bad = new Person({
name:{
first :'Walter',
last:'White'
}
});
假设我们想要记录bad的全名,那么我们可以手动这样处理。
console.log(bad.name.first+' '+bad.name.last)
或者,我们可以在personSchema上定义一个虚拟属性getter,那么我们不需要每次都写出这个字符串连接代码。
personSchema.virtual('name.full').get(function(){
return this.name.first+' '+this.name.last;
});
现在,当我们访问我们的虚拟属性“name.full"时,我们的getter函数就会被调用,并且会将结果返回。
console.log('%s is insane', bad.name.full); // Walter White is insane
请注意,如果结果记录被转换为对象或JSON,默认情况下不会包括虚拟。将virtuals:true传递给toOject()或传递给toJSON()以使它们返回。
我们同样可以通过设置this.name.full来设置this.name.first和this.name.last。例如,如果我们想把bad的name.first和name.last分别改为"Breaking"和“bad",那么只需这样:
bad.name.full = 'Breaking Bad';
Mongoose允许你通过它的虚拟属性setters来做到这一点。
let personSchema = new Schema({
name:{
first:String,
last:String
}
});
let Person = mongoose.model('Person',personSchema);
//虚拟属性getters
personSchema.virtual('name.full').get(function(){
return this.name.first+' '+this.name.last;
});
//虚拟属性setters
personSchema.virtual('name.full').set(function(name){
let split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});
let bad = new Person({
name:{
first :'Walter',
last:'White'
}
});
console.log(bad.name.full); //Walter White
bad.name.full = 'Breaking Bad';
console.log(bad.name.full); // Breaking Bad
Options
Schema 有一些可配置的选项,可以直接传递给构造函数或者直接设置:
new Schema({..},options);
//或者
let schema = new Schema({..});
schema.set(option,value);
有效的选项(options):
- autoIndex
- capped
- collection
- id
- _id
- read
- safe
- shardKey
- strict
- toJSON
- toObject
- versionKey
option:autoIndex
在应用程序启动时,Mongoose为您的Schema中声明的每个索引发送一个ensureIndex命令。 从Mongoose v3开始,默认情况下在后台创建索引。 如果要禁用自动创建功能并在创建索引时手动处理,请将模式autoIndex选项设置为false,并在模型上使用ensureIndexes方法。
let Schema = mongoose.Schema;
let schema = mongoose.Schema({
name: String
}, {
autoIndex: false
});
let Person = mongoose.model('Person', schema);
Person.ensureIndexes(function (err) {
if (err) {
console.error(err);
}
});
注意:不建议在生产环境中运行。 索引创建可能会影响数据库性能,具体取决于负载。 小心使用。
ensureIndex命令不是并行发送的。 这是为了避免MongoError:无法添加索引与后台操作进行中错误。
option: capped
Mongoose支持MongoDB限制集合大小。 要指定底层MongoDB集合的上限,请将capped选项设置为集合的最大大小(以字节为单位)。
let Schema = mongoose.Schema;
let schema = mongoose.Schema({
name: String
}, {
capped:1 //设置大小1字节
});
let Person = mongoose.model('Person', schema);
let man = new Person({
name:'noshower'
});
man.save(function(err,target){
if(err){
console.error(err);
}else{
console.log(target);
}
});
此时man能正常保存在数据库。但是,当name的值设置为很多字时,保存就会报错。
MongoError: object to insert exceeds cappedMaxSize
如果要传递其他选项(如max或auto IndexId),则capped选项也可以设置为对象。在这种情况下,你必须明确地传递其所需的大小选项。
new Schema({..}, { capped: { size: 1024, max: 1000, autoIndexId: true } });
option: collection
Mongoose默认情况下通过将模型名称传递给utils.toCollectionName方法来生成集合名称。 如果您需要为集合使用不同的名称,请设置此选项。
上面我们model的名称是Person,Mongoose自动给我们生成了people结合。
现在我们修改代码如下:
let schema = new Schema({name:String},{collection:'data'});
那么,生成的集合名字就叫"data";
option:id
Mongoose默认为每个schema分配一个id虚拟getter,它返回的文档_id字段会被强制转换为字符串,或者在ObjectIds及其hexString的情况下。 如果不想将id getter添加到模式中,可以在schema构建时禁用它传递此选项。
//默认行为
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
});
let Person = mongoose.model('Page',schema);
let man = new Person({
name:'noshower'
});
console.log(man.id); //5877026e2c52b034810535e2
// 禁用id
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
},{
id:false
});
let Person = mongoose.model('Page',schema);
let man = new Person({
name:'noshower'
});
console.log(man.id); //undefined
option: _id
Mongoose默认为每个模式分配一个_id字段,如果没有传递给Schema构造函数。 类型被设置为一个ObjectId,与MongoDB的默认行为一致。 如果您不想将_id添加到schema中,您可以使用此选项禁用它。
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
});
//默认行为
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshower'
});
console.log(man); //name: 'noshower', _id: 58771d4c27cb0d3869a88885 }
//禁止_id
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
},{
_id:false
});
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshower'
});
console.log(man);//{ name: 'noshower' }
man.save(function(err,target){
if(err){
console.error(err);
}else{
console.log(target);
}
});
//此时不能保存信息
Error: document must have an _id before saving
我们刚刚禁止了_id,但是在保存前,必须要有_id属性。那么,我们就需要在定义schema时,就定义好_id
let schema = new Schema({
name:String,
_id:Number
},{
_id:false
});
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshower',
_id:1
});
//此时能够正常保存了。
man.save(function(err,target){
if(err){
console.error(err);
}else{
console.log(target);//{ __v: 0, name: 'noshower', _id: 1 }
}
});
option:safe
这个选项被传递给MongoDB和所有操作,并指定是否应该将错误返回给我们的回调,以及调整写入行为。
new Schema({..},{safe:true});
默认情况下,safe设置的是true。 通过设置safe,比如{j:1,w:2,wtimeout:10000}这样的东西,我们可以保证,写入被提交到MongoDB日志(j:1),至少有2个副本(w:2) 如果超过10秒(wtimeout:10000),写入将超时。 错误仍然会传递给我们的回调。
new Schema({..},{j:1,w:2,wtimeout:10000})
option:shardKey
当我们有一个分片的MongoDB架构时,使用shardKey选项。 每个分片集合都有一个分片键,它必须出现在所有插入/更新操作中。 我们只需要将此模式选项设置为相同的shard键,我们就会设置。
请注意,Mongoose不会为您发送shardcollection命令。 您必须自己配置您的碎片。
new Schema({ .. }, { shardKey: { tag: 1, name: 1 }})
option:strict
strict选项(默认情况下已启用)确保传递给模型构造函数的,未在模式中指定的值不会保存到db。
let Schema = mongoose.Schema;
// 只定义了nane
let schema = new Schema({
name:String
});
let Person = mongoose.model('Person',schema);
// 多传了age
let man = new Person({
name:'noshower',
age:22
});
man.save(function(err,target){
if(err){
console.error(err+'xx');
}else{
console.log(target);//age没有被存进去
//{ __v: 0, name: 'noshower', _id: 58772602945ef339db63dc3d }
}
});
//设置strict为false
let schema = new Schema({
name:String
},{
strict:false
});
//此时age也存进去了。
//{ __v: 0,name: 'noshower',age: 22,_id: 5877285c8913073ae8e0146f }
option:toObject
与toObject选项完全相同,但仅在调用文档toJSON方法时才适用。
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
});
schema.path('name').get(function(v){
return v+' is my name'
});
schema.set('toJSON',{getters:true,virtuals:false});
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshwoer'
});
console.log(man);//{ name: 'noshwoer', _id: 5877349b8c8cf73cafa89ada }
console.log(man.toObject());//{ name: 'noshwoer', _id: 5877349b8c8cf73cafa89ada }
console.log(man.toJSON());//{ name: 'noshwoer is my name', _id: 5877349b8c8cf73cafa89ada }
//当一个js对象被字符串化时,就会调用JSON。
console.log(JSON.stringify(man));//{"name":"noshwoer is my name","_id":"5877349b8c8cf73cafa89ada"}
option: toObject
文档有一个toObject方法,将mongoose文档转换为一个纯javascript对象。 此方法接受几个选项。 默认情况下,不是基于每个文档应用这些选项,我们可以在此声明选项,并将其应用于所有这些模式文档。要使所有虚拟显示在您的console.log输出中,请将toObject选项设置为{getters:true}:
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
});
schema.path('name').get(function(v){
return v+' is my name'
});
schema.set('toObject',{getters:true});
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshwoer'
});
console.log(man);//{ name: 'noshwoer is my name', _id: 587736eafce76a3cddadd692, id: '587736eafce76a3cddadd692' }
option:versionKey
当文档第一次被Mongoose创建时,都会被设置versionKey属性。 此键值包含文档的内部版本。 此文档属性的名称是可配置的。 默认值为__v。 如果这与您的应用程序冲突,您可以这样配置:
let Schema = mongoose.Schema;
let schema = new Schema({
name:String
});
let Person = mongoose.model('Person',schema);
let man = new Person({
name:'noshwoer'
});
man.save(function(err,target){
if(err){
console.error(err);
}else{
console.log(target); //{ __v: 0, name: 'noshwoer', _id: 587739f5a090c53d34444e45 }
}
});
// 下面自定义 versionkey
let schema = new Schema({
name:String
},{
versionKey:"_somethingElse"
});
// _v 变成了_somethingElse
// { _somethingElse: 0, name: 'noshwoer', _id: 58773a67f2ec543d46cdac5b }
还可以通过将versionKey设置为false来禁用文档版本控制。 除非你知道你在做什么,否则不要禁用版本控制。
new Schema({..}, { versionKey: false });
现在我们已经介绍了Schemas,让我们来看看SchemaTypes。
SchemaTypes
以下是所有有效的SchemaTypes
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
Dates
如果必须使用内置方法修改日期类型,请在保存之前使用doc.markModified('pathToYourDate')告诉Mongoose关于更改。
let Schema = mongoose.Schema;
let schema = new Schema({
dueDate: Date
});
let Assignment = mongoose.model('Assignment', schema);
let time = new Assignment({
dueDate:new Date()
});
Assignment.findOne(function(err,doc){
doc.dueDate.setMonth(3);
doc.save(function(err,doc){}); //没有保存
});
Assignment.findOne(function (err, doc) {
doc.dueDate.setMonth(2);
doc.markModified('dueDate');
doc.save(function (err, doc) {
//保存了
});
});
Mixed
“任何事情”SchemaType,它的灵活性是在它的难以维护的权衡。 混合可以通过Schema.Types.Mixed或通过传递一个空对象字面量。 以下是等效的:
let Any = new Schema({ any: {} });
let Any = new Schema({ any: Schema.Types.Mixed });
由于它是一个无模式的类型,您可以将值更改为任何您喜欢的,但Mongoose失去自动检测和保存这些更改的能力。 要“告诉”Mongoose混合类型的值已更改,请调用文档的.markModified(路径)方法,将路径传递到刚更改的混合类型。
person.anything = { x: [3, 4, { y: "changed" }]};
person.markModified('anything');
person.save(); // anything will now get saved
ObjectIds
要指定ObjectId的类型,请在声明中使用Schema.Types.ObjectId。
let mongoose = require('mongoose');
let ObjectId = mongoose.Schema.Types.ObjectId;
let Car = new Schema({ driver: ObjectId });
Arrays
提供模式类型或子文档数组的创建。
let ToySchema = new Schema({ name: String });
let ToyBox = new Schema({
toys: [ToySchema],
buffers: [Buffer],
string: [String],
numbers: [Number] // ... etc});
});
注意:指定空数组等效于Mixed。 以下都创建了Mixed的数组:
let Empty1 = new Schema({ any: [] });
let Empty2 = new Schema({ any: Array });
let Empty3 = new Schema({ any: [Schema.Types.Mixed] });
let Empty4 = new Schema({ any: [{}] });
网友评论