您的位置:首页 > 编程语言 > Go语言

【转载】深入浅出mongoose

2018-01-11 16:32 211 查看


深入浅出mongoose

原文在此:http://blog.csdn.net/zhalcie2011/article/details/71750915,由于不知道什么原因,原文配色不对,白字白底,基本没有可读性,重新编辑转帖方便自己阅读。

mongoose是nodeJS提供连接mongodb的一个库.此外还有mongoskin,mongodb(mongodb官方出品).本人,还是比较青睐mongoose的,因为他遵循的是一种,模板式方法,能够对你输入的数据进行自动处理.有兴趣的同学可以去Mongoose官网看看.


初入mongoose


installmongoose

I’vesaidthat.使用mongoose你需要有nodeJS和mongodb数据库.这两个东西,前端宝宝们应该很清楚了》下载mongoose:
npminstallmongoose--save



connectmongoose

下载好数据库之后,我们来碰碰运气,看你能不能连接上database.首先,打开你的mongodb;
mongod;//这里我已经将mongodb放在环境变量中了


数据库成功打开后:在js文件中写入:

'usestrict';

constmongoose=require('mongoose');

mongoose.connect('mongodb://localhost:27017/test');
constcon=mongoose.connection;
con.on('error',console.error.bind(console,'连接数据库失败'));
con.once('open',()=>{
//成功连接
})




(其实,懂得童鞋,你只要copy就行了.)OK,运气好的同学,此刻嘴角扬起45°的微笑.运气差的同学,出门左转google.简单连接mongoose后,我们来看一看mongoose的基本构造吧.


understandmongoose

mongoose实际上,可以说是Oracle和mongodb的一个混合产物,但归根接地还是mongodb的.这里我要祭出,我珍藏很久的对比图.熟悉其他数据库的同学应该能很快明白的.
OracleMongoDBMongoose
数据库实例(databaseinstance)MongoDB实例Mongoose
模式(schema)数据库(database)mongoose
表(table)集合(collection)模板(Schema)+模型(Model)
行(row)文档(document)实例(instance)
rowid_id_id
JoinDBRefDBRef
通过上面的阐述,我们大概能知道了在Mongoose里面有哪几个基本概念.
Schema: 相当于一个数据库的模板.Model可以通过mongoose.model集成其基本属性内容.当然也可以选择不继承.
Model: 基本文档数据的父类,通过集成Schema定义的基本方法和属性得到相关的内容.
instance: 这就是实实在在的数据了.通过newModel()初始化得到.

他们各自间是怎样的关系呢?下图可以清晰的说明,以上3中实际上就是一个继承一个得到最后的数据.



我们先看一个demo吧:

'usestrict';

constmongoose=require('mongoose');

mongoose.connect('mongodb://localhost:27017/test');
constcon=mongoose.connection;
con.on('error',console.error.bind(console,'连接数据库失败'));
con.once('open',()=>{
//定义一个schema
letSchema=mongoose.Schema({
category:String,
name:String
});
Schema.methods.eat=function(){
console.log("I'veeattenone"+this.name);
}
//继承一个schema
letModel=mongoose.model("fruit",Schema);
//生成一个document
letapple=newModel({
category:'apple',
name:'apple'
});
//存放数据
apple.save((err,apple)=>{
if(err)returnconsole.log(err);
apple.eat();
//查找数据
Model.find({name:'apple'},(err,data)=>{
console.log(data);
})
});
})




到这里,实际上,mongoose我们已经就学会了.剩下就是看一看官方文档的API–CRUD相关操作.如果,大家觉得意犹未尽的话,可以继续看下面的深入浅出.而且,下面会附上实际应用中,mongoose的写法.


深入浅出mongoose

这里,我们根据上面的3个概念深入的展开一下.


Schema

这实际上是,mongoose中最重要的一个theroy.schema是用来定义documents的基本字段和集合的.在mongoose中,提供了Schema的类。我们可以通过实例化他,来实现创建Schema的效果.而不需要每次调用mongoose.Schema()这个丑陋的API.

//frommongooseauthor
varmongoose=require('mongoose');
varSchema=mongoose.Schema;

varblogSchema=newSchema({
title:String,
author:String,
body:String,
comments:[{body:String,date:Date}],
date:{type:Date,default:Date.now},
hidden:Boolean,
meta:{
votes:Number,
favs:Number
}
});




Schema之所以能够定义documents,是因为他可以限制你输入的字段及其类型.mongoose支持的基本类型有:
String
Number
Date
Buffer
Boolean
Mixed
ObjectId
Array

其中,Mixed和ObjectId是mongoose中特有的,ObjectId实际上就是**_id**的一个映射.同样,mongoose里面有着和所有大众数据库一样的东西. 索引 –indexs

mongoose设置索引

这里设置索引分两种,一种设在Schemafiled,另外一种设在Schema.index里.

//在field设置
varanimalSchema=newSchema({
name:String,
type:String,
tags:{type:[String],index:true}
});
//在Schema.index中设置.
animalSchema.index({name:1,type:-1});

//1表示正序,-1表示逆序




实际上,两者效果是一样的.看每个人的喜好了.不过推荐直接在Schemalevel中设置,这样分开能够增加可读性.不过,官方给出了一个建议,因为在创建字段时,数据库会自动根据自动排序(ensureIndex).有可能严重拖慢查询或者创建速度,所以一般而言,我们需要将该option关闭.

mongoose.connect('mongodb://user:pass@localhost:port/database',{config:{autoIndex:false}});//真心推荐
//or
mongoose.createConnection('mongodb://user:pass@localhost:port/database',{config:{autoIndex:false}});//不推荐
//or
animalSchema.set('autoIndex',false);//推荐
//or
newSchema({..},{autoIndex:false});//懒癌不推荐




另外,Schema另一大特色就是其methods.我们可以通过定义其methods,访问到实际上的所有内容.

定义Schema.methods

使用的方法很简单,就是使用 .methods即可.

//定义一个schema
varfreshSchema=newSchema({name:String,type:String});

//添加一个fn.
animalSchema.methods.findSimilarTypes=function(cb){
//这里的this指的是具体document上的this
returnthis.model('Animal').find({type:this.type},cb);
}
//实际上,我们可以通过schema绑定上,数据库操作的所有方法.
//该method实际上是绑定在实例的doc上的




定义完methods和property之后,就到了生成Model的阶段了.

实例Model

这里同样很简单,只需要 mongoose.model() 即可.

//生成,model类.实际上就相当于我们的一个collection
varAnimal=mongoose.model('Animal',animalSchema);
vardog=newAnimal({type:'dog'});




但是,这里有个问题.我们在Schema.methods.fn上定义的方法,只能在 newModel() 得到的实例中才能访问.那如果我们想,直接在Model上调用相关的查询或者删除呢?

绑定Model方法

同样很简单,使用 statics 即可.

//给model添加一个findByName方法
animalSchema.statics.findByName=function(name,cb){
//这里的this指的就是Model
returnthis.find({name:newRegExp(name,'i')},cb);
}

varAnimal=mongoose.model('Animal',animalSchema);
Animal.findByName('fido',function(err,animals){
console.log(animals);
});




Mongoose还有一个superfeatrue--virtualproperty该属性是直接设置在Schema上的.但是,需要注意的是,VR并不会真正的存放在db中.他只是一个提取数据的方法.

//schema基本内容
varpersonSchema=newSchema({
name:{
first:String,
last:String
}
});

//生成Model
varPerson=mongoose.model('Person',personSchema);

//现在我们有个需求,即,需要将first和last结合输出.
//一种方法是,使用methods来实现
//schema添加方法
personSchema.methods.getName=function(){
returnthis.first+""+this.last;
}

//生成一个doc
varbad=newPerson({
name:{first:'jimmy',last:'Gay'}
});

//调用
bad.getName();




但是,像这样,仅仅这是为了获取一个属性,实际上完全可以使用虚拟属性来实现.

//schema添加虚拟属性
personSchema.virtual('fullName').get(function(){
returnthis.first+""+this.last;
})
//调用
bad.fullName;//和上面的方法的结果是完全一致的




而且,经过测试,使用fn实现的返回,比VR要慢几十倍.一下是测试结果:

console.time(1);
bad.getName();
console.timeEnd(1);
console.time(2);
bad.fullName;
console.timeEnd(2);

//结果为:
1:4.323ms;//method
2:0.253ms//VR




最后再补充一下,Schema中初始化的相关参数.

Schema参数 在 newSchema([options]) 中,我们需要设置一些相关的参数.
safe: 用来设置安全模式.实际上,就是定义入库时数据的写入限制.比如写入时限等.

//使用安全模式.表示在写入操作时,如果发生错误,也需要返回信息.
varsafe=true;
newSchema({..},{safe:safe});

//自定义安全模式.w为写入的大小范围.wtimeout设置写入时限.如果超出10s则返回error
varsafe={w:"majority",wtimeout:10000};
newSchema({..},{safe:safe});



toObject: 用来表示在提取数据的时候,把documents内容转化为Object内容输出.一般而言只需要设置getters为true即可.

schema.set('toObject',{getters:true});
varM=mongoose.model('Person',schema);
varm=newM({name:'MaxHeadroom'});
//实际打印出来的就是一个Object类型
console.log(m);//{_id:504e0cd7dd992d9be2f20b6f,name:'MaxHeadroomismyname'}



toJSON: 该是和toObject一样的使用.通常用来把documents转化为Object.但是,需要显示使用toJSON()方法,否则,不会起作用.实际上,没什么卵用…

看一下总结图谱: 


看完schema之后,我们需要了解一下model的内容.


Model

实际上,Model才是操作数据库最直接的一块内容.我们所有的CRUD就是围绕着Model展开的.ok.还记得,我们是怎样创建一个model的吗?

model的创建

model的创建实际上就是方法的copy.将schema上的方法,copy到model上.只是copy的位置不一样,一部分在prototype上,一部分在constructor中.

//frommongoosejs
varschema=newmongoose.Schema({name:'string',size:'string'});
varTank=mongoose.model('Tank',schema);




这里,我们一定要搞清楚一个东西.实际上,mongoose.model里面定义的第一个参数,比如’Tank’,并不是数据库中的,collection.他只是collection的单数形式,实际上在db中的collection是’Tanks’.

ok,我们现在已经有了一个基本的Model.但并没有什么x用.接下来,正式到了drygoods(干货)时间.

model的子文档操作 这个就厉害了.本来mongodb是没有关系的.但是,mongoose提供了children字段.让我们能够轻松的在表间建立关系.现在,我们来创建一个子域:

varchildSchema=newSchema({name:'string'});

varparentSchema=newSchema({
children:[childSchema]//指明sub-doc的schema
});
//在创建中指明doc
varParent=mongoose.model('Parent',parentSchema);
varparent=newParent({children:[{name:'Matt'},{name:'Sarah'}]})
parent.children[0].name='Matthew';
parent.save(callback);




现在,我们就已经创建了3个table.一个parent包含了两个child另外,如果我们想要查询指定的doc。则可以使用id()方法.

vardoc=parent.children.id(id);




子文档的CRUD,实际上就是数组的操作,比如push,unshift,remove,pop,shift等

parent.children.push({name:'Liesl'});




mongoose还给移除提供了另外一个方法–remove:

vardoc=parent.children.id(id).remove();




如果你忘记添加子文档的话,可以在外围添加,但是字段必须在Schema中指定

varnewdoc=parent.children.create({name:'Aaron'});




model的CRUD操作

model的创建 关于model的创建,有两种方法,一种是使用实例创建,另外一种是使用Model类创建.

varTank=mongoose.model('Tank',yourSchema);

varsmall=newTank({size:'small'});
//使用实例创建
small.save(function(err){
if(err)returnhandleError(err);
//saved!
})

//使用Model类创建
Tank.create({size:'small'},function(err,small){
if(err)returnhandleError(err);
//saved!
})




上面已经完美的介绍创建的方法了.另外,官方给出一个提醒:由于mongoose,会自身连接数据库并断开.如果你手动连接,则创建model的方式需要改变.

//自己并没有打开连接:
//注意,这里只是连接,并没有创建connection
mongoose.connect('mongodb://localhost:27017/test');

//手动创建连接:
varconnection=mongoose.createConnection('mongodb://localhost:27017/test');
varTank=connection.model('Tank',yourSchema);




然后,下面的API还是一样的.实际上,我们一般常用的写法为:

constmongoose=require('mongoose');
constSchema=mongoose.Schema;
//设置连接位置
mongoose.connect('mongodb://localhost:27017/test');
varschema=newmongoose.Schema({name:'string',size:'string'});
varTank=mongoose.model('Tank',schema);
varsmall=newTank({size:'small'});
//使用实例创建
small.save(function(err){
if(err)returnhandleError(err);
console.log('创建成功');
})




这样,就不用自己去手动管连接的问题了.如果你,在后面想手动添加字段的话,可以使用.set方法.

//一个key/valye
doc.set(path,value)
//很多key/value
doc.set({
path:value,
path2:{
path:value
}
})




model的query model的查找主要提供了以下的API,给我们进行操作. find,findById,findOne,orwhere 在mongodb中,query返回的数据格式一般都是为JSON的.这点需要注意.

事实上,在mongoose中,query数据提供了两种方式.
callback: 使用回调函数,即,query会立即执行,然后返回到回调函数中.

Person.findOne({'name.last':'Ghost'},'nameoccupation',function(err,person){
if(err)returnhandleError(err);
//getdata
})



query: 使用查询方法,返回的对象.该对象是一个Promise,所以可以使用chain进行调用.最后必须使用exec(cb)传入回调进行处理.cb是一个套路,第一个参数永远是err.第二个就是返回的数据。

Person.
find({
occupation:/host/,
'name.last':'Ghost',
age:{$gt:17,$lt:66},
likes:{$in:['vaporizing','talking']}
}).
limit(10).
sort({occupation:-1}).
select({name:1,occupation:1}).
exec(callback);

//如果没有查询到,则返回[](空数组)
//如果你使用findOne,没有的话则会返回null




童鞋,你觉得我会推荐哪种呢?

上面4个API,3个使用方式都是一样的,另外一个不同的是where.他一样是用来进行query.只是,写法和find系列略有不同.

where简介 where的API为: Model.where(path,[val]) path实际上就是字段,第二个参数.val表示可以用来指定,path=val的数据内容,你也可以不写,交给后面进行筛选.看一下对比demo吧:

User.find({age:{$gte:21,$lte:65}},callback);
//等价于:
User.where('age').gte(21).lte(65).exec(callback);




从上面的query中,我们可以看到有许多fn,比如gte,lte,$gte,$lte.这些是db提供给我们用来查询的快捷函数.我们可以参考,mongoose给的参考: queryHelperfn 这里,我们简要的了解下,基本的快捷函数.
nameeffect
select添加需要显示的字段,需要的字段在字段后加上:1,不需要的加上0;<br/>query.select({a:1,b:0});//显示a字段,隐藏b字段<br/>不能和distinct方法一起使用
distinct用来筛选不重复的值或者字段<br/>distinct(field).//筛选指定不重复字段的数据
$lt,$lte,$gt,$gte.分别对应:<,<=,>,>=.该字段是用在condition中的.如果,你想要链式调用,则需要使用<br/>lt,lte,ge,gte.<br/>eg:<br/>model.find({num:{$gt:12}},cb)<br/>model.where(‘num’).gt(12).exec(cb)
$in查询包含键值的文档,<br/>model.find({name:{$in:[“jimmy”,“sam”]}})//相当于匹配jimmy或者sam
$nin返回不匹配查询条件文档,都是指定数组类型<br/>model.find({name:{$nin:[“jimmy”,“sam”]}})
$ne表示不包含指定值<br/>model.find({name:{$ne:“sam”}})
$or表示或查询<br/>model.find({$or:[{color:‘red’},{status:‘emergency’}]})
$exits表示键值是否存在;<br/>model.find({name:{$exits:true}})
$all通常用来匹配数组里面的键值,匹配多个值(同时具有)<br/>$all:[“apple”,“banana”,“peach”]}
$size用来查询数组的长度值<br/>model.find({name:{$size:3}});匹配name的数组长度为3
$slice用来获取数组字段的内容:<br/>query.slice(‘comments’,5)
ok~这上面就是比较常用的快捷函数.另外还有一些游标集合的处理方法:常用的就3个,limit,skip,sort.
**limit:**用来获取限定长度的内容.
query.limit(20);//只返回前20个内容

skip: 返回,跳过指定doc后的值.
query.skip(2);

sort: 用来设置根据指定字段排序.可以设置为1:升序,-1:降序.
query.sort({name:1,age:-1});


实际上,关于query,我们需要了解的也就差不多了.

我们接下来,来看一下remove. mongooseremove操作

官方提供的API,就是remove.同样,移除的效果,我们可以使用两种方式实现。一是回调函数,二是,链式调用.

Model.find().remove({name:'AnneMurray'}).remove(fn);
//或者直接添加回调
Model.find().remove({name:'AnneMurray'},cb)




另外,我们可以直接在Model上调用.因为remove也是Schema定义的statics方法.而且,remove返回一个Promise对象

product.remove().then(function(product){
...
});
//或者直接传入回调
Tank.remove({size:'large'},function(err){
if(err)returnhandleError(err);
//removed!
});




最后,我们再看一下update.然后mongoose就基本结束了 update操作: 这里,我只说一下API就好.因为update比起上面来说,还是比较简单的. Model.update(conditions,doc,[options],[callback])
conditions: 就是query.通过query获取到指定doc
doc: 就是用来替换doc内容的值.
options: 这块需要说一下.

safe(boolean)是否开启安全模式(defaultfortrue)
upsert(boolean)如果没有匹配到内容,是否自动创建(defaultforfalse)
multi(boolean)如果有多个doc,匹配到,是否一起更改(defaultforfalse)
strict(boolean)使用严格模式(defaultforfalse)
overwrite(boolean)匹配到指定doc,是否覆盖(defaultforfalse)
runValidators(boolean):表示是否用来启用验证.实际上,你首先需要写一个验证.关于如果书写,验证大家可以参考下文,validate篇(defaultforfalse)


Model.update({age:18},{$set:{name:'jasonborne'}},{multi:true},function(err,raw){
if(err)returnhandleError(err);
console.log('raw就是mongodb返回的更改状态的falg',raw);
//比如:{ok:1,nModified:2,n:2}
});




其中的$set是,用来指明更新的字段.另外,mongoose还提供了一个:findByIdAndUpdate(id,doc[,options][,callback]);方法.关于mongoose的更新helper函数.童鞋们可以参考一下.mongoose官方文档.


validation

说完了,mongoose的body之后.我们接着来看一下,官方给mongoose穿上的漂亮的衣服.其中一件,比较吸引人的是–validation.在你save数据之前,你可以对数据进行一些列的validation.来防止某天你傻不拉几的把数据完整性给破坏了.mongoose贴心的提供了几个built-in的验证函数.
required: 表示必填字段.

newSchema({
name:{
type:String,
required:[true,"name是必须的"]//第二个参数是错误提示信息
}
})



min,max: 用来给Number类型的数据设置限制.

varbreakfastSchema=newSchema({
eggs:{
type:Number,
min:[6,'Toofeweggs'],
max:12
}
});



enum,match,maxlength,minlength: 这些验证是给string类型的.enum就是枚举,表示该属性值,只能出席那那些.match是用来匹配正则表达式的.maxlength&minlength显示字符串的长度.

newSchema({
drink:{
type:String,
enum:['Coffee','Tea']
},
food:{
type:String,
match:/^a/,
maxlength:12,
minlength:6
}
})




mongoose提供的helperfn就是这几种,如果你想定制化验证.可以使用customvalidation.

newSchema({
phone:{
type:String,
validate:{
validator:function(data){
return/\d{3}-\d{3}-\d{4}/.test(data);
},
message:'{VALUE}isnotavalidphonenumber!'//VALUE代表phone存放的值
},
required:[true,'Userphonenumberrequired']
}
})




另外,还可以额外添加验证.

vartoySchema=newSchema({
color:String,
name:String
});

varvalidator=function(value){
return/blue|green|white|red|orange|periwinkle/i.test(value);
};
toySchema.path('color').validate(validator,
'Color`{VALUE}`notvalid','Invalidcolor');




现在,我们已经设置了validation.但是你不启用,一样没有什么卵用.实际上,我们也可以把validation当做一个中间件使用.mongoose提供了两种调用方式.一种是内置调用,当你使用.save方法时,他会首先执行一次存储方法.

cat.save(function(error){
//自动执行,validation
});




另外一种是,手动验证–指定validate方法.

//上面已经设置好user的字段内容.
user.validate(function(error){
//error就是验证不通过返回的错误信息
assert.equal(error.errors['phone'].message,
'555.0123isnotavalidphonenumber!');
});
});




事实上,在validate时,错误的返回信息有以下4个字段:kind,path,value,andmessage;
kind: 用来表示验证设置的第二个参数.一般不用

phone:{
type:String,
validate:{
validator:function(data){
return/\d{3}-\d{3}-\d{4}/.test(data);
},
message:'{VALUE}isnotavalidphonenumber!',//VALUE代表phone存放的值
kind:"invalidphone"
}
})



path: 就是字段名
value: 你设置的错误内容
message: 提示错误信息看一个整体demo吧:

varvalidator=function(value){
return/blue|green|white|red|orange|periwinkle/i.test(value);
};
Toy.schema.path('color').validate(validator,
'Color`{VALUE}`notvalid','Invalidcolor');//设置了message&&kind

vartoy=newToy({color:'grease'});

toy.save(function(err){
//errisourValidationErrorobject
//err.errors.colorisaValidatorErrorobject
assert.equal(err.errors.color.message,'Color`grease`notvalid');//返回message
assert.equal(err.errors.color.kind,'Invalidcolor');
assert.equal(err.errors.color.path,'color');
assert.equal(err.errors.color.value,'grease');
assert.equal(err.name,'ValidationError');
//访问color也可以直接上errors["color"]进行访问.
});




在Model.update那一节有个参数–runValidators.还没有详细说.这里,展开一下.实际上,validate一般只会应用在save上,如果你想在update使用的话,需要额外的trick,而runValidators就是这个trick.

varopts={runValidators:true};
Test.update({},update,opts,function(error){//额外开启runValidators的验证
//Therewillneverbeavalidationerrorhere
});




我们来看一下基本总结吧: 



population

originally,mongodb本来就是一门非关系型数据库。但有时候,我们又需要联合其他的table进行数据查找。这时候,一般的做法就是实现两次查询,效率我就呵呵了.此时,mongoose说了一句:麻麻,我已经都把脏活帮你做好了.感动~有木有~这就是mongoose提供的population.用来连接多表数据查询.一般而言,我们只要提供某一个collection的_id,就可以实现完美的联合查询.population用到的关键字是: 
ref
 用来指明外联的数据库的名字.
一般,我们需要在schema中就定义好.


varmongoose=require('mongoose')
,Schema=mongoose.Schema

varpersonSchema=Schema({
_id:Number,
name:String,
age:Number,
stories:[{type:Schema.Types.ObjectId,ref:'Story'}]
});

varstorySchema=Schema({
_creator:{type:Schema.Types.ObjectId,ref:'Person'},
title:String
});




这里就指明了,外联数据表的应用关系personSchema<stories>By_id=>StorystorySchema<_creator>By_id=>Person实际上,就是通过_id的相互索引即可.这里需要说明的是,_id应该是某个具体model的id.

我们来看一下,接下来应该如何利用population实现外联查询.

constsam=newPerson({
name:'sam',
_id:1,
age:18,
stories:[]
});
sam.save((err,sam)=>{
if(err)returnerr;
letstory=newStory({
_creator:sam._id,
title:"喜剧之王"
})
})

Story.findOne({title:"喜剧之王"}).populate('_creator').exec((err,story)=>{
if(err)console.log(err);
console.log(story._creator.name);
})


//使用populate来指定,外联查询的字段,而且该值必须是_id才行


现在es6时代的来临,generator,async/await盛行.也带来另外一种书写方式–middleware.在mongoose也有这个hook,给我们使用.(说实话,有点像AOP)


mongoose&&middleware

mongoose里的中间件,有两个,一个是pre,一个是post.
pre: 在指定方法执行之前绑定。中间件的状态分为parallel和series.
post: 相当于事件监听的绑定

这里需要说明一下,中间件一般仅仅只能限于在几个方法中使用.(但感觉就已经是全部了)
doc方法上:init,validate,save,remove;
model方法上:count,find,findOne,findOneAndRemove,findOneAndUpdate,update


pre

我们来看一下,pre中间件是如何绑定的.

//series执行,串行
varschema=newSchema(..);
schema.pre('save',function(next){
//exesomeoperations
this.model.
next();//这里的next()相当于间执行权给下一个pre
});




在你调用model.save方法时,他会自动执行pre.如果你想并行执行中间件,可以设置为:

schema.pre('save',true,function(next,done){
//并行执行下一个中间件
next();
});




post

相当于绑定啦~post会在指定事件后触发

schema.post('save',function(doc){
//在save完成后触发.
console.log('%shasbeensaved',doc._id);
});




当save方法调用时,便会触发post绑定的save事件.如果你绑定了多个post。则需要指定一下中间件顺序.

schema.post('save',function(doc,next){
setTimeout(function(){
console.log('post1');
next();
},10);
});

schema.post('save',function(doc,next){
console.log('post2');
next();
});




实际上,post触发的时间为:

varschema=newSchema(..);
schema.post('save',function(doc){
console.log('thisfiredafteradocumentwassaved');
});

varModel=mongoose.model('Model',schema);

varm=newModel(..);
m.save(function(err){
console.log('thisfiresafterthe`post`hook');
});




另外,在post和find中,是不能直接修改doc上的属性的.即,像下面一样的,没有效果

articleSchema.post('find',function(docs){
docs[1].date=1
})
docs[1].date的值还是不变



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: