深刻浅出mongoose

深刻浅出mongoose

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

初入mongoose

install mongoose

I’ve said that. 使用mongoose你须要有 nodeJS和mongodb数据库. 这两个东西, 前端宝宝们应该很清楚了》 下载mongoose:前端

npm install mongoose --save

connect mongoose

下载好数据库以后,咱们 来碰碰运气, 看你能不能链接上database. 首先,打开你的mongodb;node

mongod;  //这里我已经将mongodb放在环境变量中了 

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

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

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

understand mongoose

mongoose实际上,能够说是Oracle和mongodb的一个混合产物,但归根接地仍是mongodb的. 这里我要祭出,我珍藏好久的对比图. 熟悉其余数据库的同窗应该能很快明白的.mongodb

Oracle MongoDB Mongoose
数据库实例(database instance) MongoDB实例 Mongoose
模式(schema) 数据库(database) mongoose
表(table) 集合(collection) 模板(Schema)+模型(Model)
行(row) 文档(document) 实例(instance)
rowid _id _id
Join DBRef DBRef

经过上面的阐述,咱们大概能知道了在Mongoose里面有哪几个基本概念.数据库

  • Schema: 至关于一个数据库的模板. Model能够经过mongoose.model 集成其基本属性内容. 固然也能够选择不继承.
  • Model: 基本文档数据的父类,经过集成Schema定义的基本方法和属性获得相关的内容.
  • instance: 这就是实实在在的数据了. 经过 new Model()初始化获得.

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

mongoose

咱们先看一个demo吧:api

'use strict'; const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/test'); const con = mongoose.connection; con.on('error', console.error.bind(console, '链接数据库失败')); con.once('open',()=>{ //定义一个schema let Schema = mongoose.Schema({ category:String, name:String }); Schema.methods.eat = function(){ console.log("I've eatten one "+this.name); } //继承一个schema let Model = mongoose.model("fruit",Schema); //生成一个document let apple = new Model({ category:'apple', name:'apple' }); //存放数据 apple.save((err,apple)=>{ if(err) return console.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.

// from mongoose author var mongoose = require('mongoose'); var Schema = mongoose.Schema; var blogSchema = new Schema({ 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 设置索引

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

//在field 设置 var animalSchema = new Schema({ name: String, type: String, tags: { type: [String], index: true } }); //在Schema.index中设置. animalSchema.index({ name: 1, type: -1 }); //1 表示正序, -1 表示逆序 

实际上,二者效果是同样的. 看每一个人的喜爱了. 不过推荐直接在Schema level中设置, 这样分开可以增长可读性. 不过,官方给出了一个建议, 由于在建立字段时, 数据库会自动根据自动排序(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 new Schema({..}, { autoIndex: false }); //懒癌不推荐 

另外, Schema 另外一大特点就是其methods. 咱们能够经过定义其methods,访问到实际上的全部内容.

定义Schema.methods

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

// 定义一个schema var freshSchema = new Schema({ name: String, type: String }); // 添加一个fn. animalSchema.methods.findSimilarTypes = function (cb) { //这里的this指的是具体document上的this return this.model('Animal').find({ type: this.type }, cb); } // 实际上,咱们能够经过schema绑定上,数据库操做的全部方法. // 该method其实是绑定在 实例的 doc上的 

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

实例Model

这里一样很简单,只须要 mongoose.model() 便可.

//生成,model 类. 实际上就至关于咱们的一个collection var Animal = mongoose.model('Animal', animalSchema); var dog = new Animal({ type: 'dog' }); 

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

绑定Model方法

一样很简单,使用 statics 便可.

// 给model添加一个findByName方法 animalSchema.statics.findByName = function (name, cb) { //这里的this 指的就是Model return this.find({ name: new RegExp(name, 'i') }, cb); } var Animal = mongoose.model('Animal', animalSchema); Animal.findByName('fido', function (err, animals) { console.log(animals); }); 

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

//schema基本内容 var personSchema = new Schema({ name: { first: String, last: String } }); // 生成Model var Person = mongoose.model('Person', personSchema); //如今咱们有个需求,即,须要将first和last结合输出. //一种方法是,使用methods来实现 //schema 添加方法 personSchema.methods.getName = function(){ return this.first+" "+this.last; } // 生成一个doc var bad = new Person({ name: { first: 'jimmy', last: 'Gay' } }); //调用 bad.getName(); 

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

//schema 添加虚拟属性 personSchema.virtual('fullName').get(function(){ return this.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参数 在 new Schema([options]) 中,咱们须要设置一些相关的参数.

  • safe: 用来设置安全模式. 实际上,就是定义入库时数据的写入限制. 好比写入时限等.
//使用安全模式. 表示在写入操做时,若是发生错误,也须要返回信息. var safe = true; new Schema({ .. }, { safe: safe }); // 自定义安全模式. w为写入的大小范围. wtimeout设置写入时限. 若是超出10s则返回error var safe = { w: "majority", wtimeout: 10000 }; new Schema({ .. }, { safe: safe }); 
  • toObject: 用来表示在提取数据的时候, 把documents 内容转化为Object内容输出. 通常而言只须要设置getters为true便可.
schema.set('toObject', { getters: true }); var M = mongoose.model('Person', schema); var m = new M({ name: 'Max Headroom' }); //实际打印出来的就是一个Object类型 console.log(m); // { _id: 504e0cd7dd992d9be2f20b6f, name: 'Max Headroom is my name' } 
  • toJSON: 该是和toObject同样的使用. 一般用来把 documents 转化为Object. 可是, 须要显示使用toJSON()方法,不然,不会起做用. 实际上,没什么卵用…

看一下总结图谱: schema

看完schema以后,咱们须要了解一下 model的内容.

Model

实际上,Model才是操做数据库最直接的一块内容. 咱们全部的CRUD就是围绕着Model 展开的. ok. 还记得,咱们是怎样建立一个model的吗?

model的建立

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

//from mongoosejs var schema = new mongoose.Schema({ name: 'string', size: 'string' }); var Tank = mongoose.model('Tank', schema); 

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

ok, 咱们如今已经有了一个基本的Model. 但并无什么x用. 接下来, 正式到了 dry goods(干货) 时间.

model 的子文档操做 这个就厉害了. 原本mongodb是没有关系的. 可是, mongoose提供了children字段. 让咱们可以轻松的在表间创建关系. 如今,咱们来建立一个子域:

var childSchema = new Schema({ name: 'string' }); var parentSchema = new Schema({ children: [childSchema] //指明sub-doc的schema }); //在建立中指明doc var Parent = mongoose.model('Parent', parentSchema); var parent = new Parent({ children: [{ name: 'Matt' }, { name: 'Sarah' }] }) parent.children[0].name = 'Matthew'; parent.save(callback); 

如今, 咱们就已经建立了3个table. 一个parent 包含了 两个child 另外,若是咱们想要查询指定的doc。 则可使用 id()方法.

var doc = parent.children.id(id); 

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

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

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

var doc = parent.children.id(id).remove(); 

若是你忘记添加子文档的话,能够在外围添加, 可是字段必须在Schema中指定

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

model的CRUD操做

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

var Tank = mongoose.model('Tank', yourSchema); var small = new Tank({ size: 'small' }); //使用实例建立 small.save(function (err) { if (err) return handleError(err); // saved! }) //使用Model类建立 Tank.create({ size: 'small' }, function (err, small) { if (err) return handleError(err); // saved! }) 

上面已经完美的介绍建立的方法了. 另外,官方给出一个提醒: 因为mongoose, 会自身链接数据库并断开. 若是你手动链接, 则建立model的方式须要改变.

// 本身并无打开链接: //注意,这里只是链接,并无建立connection mongoose.connect('mongodb://localhost:27017/test'); //手动建立链接: var connection = mongoose.createConnection('mongodb://localhost:27017/test'); var Tank = connection.model('Tank', yourSchema); 

而后, 下面的API仍是同样的. 实际上,咱们通常经常使用的写法为:

const mongoose = require('mongoose'); const Schema = mongoose.Schema; //设置链接位置 mongoose.connect('mongodb://localhost:27017/test'); var schema = new mongoose.Schema({ name: 'string', size: 'string' }); var Tank = mongoose.model('Tank', schema); var small = new Tank({ size: 'small' }); //使用实例建立 small.save(function (err) { if (err) return handleError(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, or where 在mongodb中, query返回的数据格式通常都是为JSON的. 这点须要注意.

事实上,在mongoose中,query数据 提供了两种方式.

  • callback: 使用回调函数, 即, query会当即执行,而后返回到回调函数中.
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) { if (err) return handleError(err); // get data }) 
  • 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给的参考: query Helper fn 这里,咱们简要的了解下,基本的快捷函数.

name effect
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. mongoose remove 操做

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

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

另外,咱们能够直接在Model上调用. 由于remove也是Schema定义的statics方法. 并且, remove返回一个Promise对象

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

最后,咱们再看一下 update. 而后mongoose就基本结束了 update操做: 这里,我只说一下API就好. 由于update 比起上面来讲,仍是比较简单的. Model.update(conditions, doc, [options], [callback])

  • conditions: 就是query. 经过query获取到指定doc
  • doc: 就是用来替换doc内容的值.
  • options: 这块须要说一下.
    • safe (boolean) 是否开启安全模式 (default for true)
    • upsert (boolean) 若是没有匹配到内容,是否自动建立 ( default for false)
    • multi (boolean) 若是有多个doc,匹配到,是否一块儿更改 ( default for false)
    • strict (boolean) 使用严格模式(default for false)
    • overwrite (boolean) 匹配到指定doc,是否覆盖 (default for false)
    • runValidators (boolean): 表示是否用来启用验证. 实际上,你首先须要写一个验证. 关于若是书写,验证你们能够参考下文, validate篇(default for false)
Model.update({age:18}, { $set: { name: 'jason borne' }}, {multi:true}, function (err, raw) { if (err) return handleError(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: 表示必填字段.
new Schema({ name: { type:String, required:[true,"name 是必须的"] //第二个参数是错误提示信息 } }) 
  • min,max: 用来给Number类型的数据设置限制.
var breakfastSchema = new Schema({ eggs: { type: Number, min: [6, 'Too few eggs'], max: 12 } }); 
  • enum,match,maxlength,minlength: 这些验证是给string类型的. enum 就是枚举,表示该属性值,只能出席那那些. match是用来匹配正则表达式的. maxlength&minlength 显示字符串的长度.
new Schema({ drink: { type: String, enum: ['Coffee', 'Tea'] }, food:{ type: String, match:/^a/, maxlength:12, minlength:6 } }) 

mongoose提供的helper fn就是这几种, 若是你想定制化验证. 可使用custom validation.

new Schema({ phone: { type: String, validate: { validator: function(data) { return /\d{3}-\d{3}-\d{4}/.test(data); }, message: '{VALUE} is not a valid phone number!' //VALUE表明phone存放的值 }, required: [true, 'User phone number required'] } }) 

另外,还能够额外添加验证.

var toySchema = new Schema({ color: String, name: String }); var validator = function (value) { return /blue|green|white|red|orange|periwinkle/i.test(value); }; toySchema.path('color').validate(validator, 'Color `{VALUE}` not valid', 'Invalid color'); 

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

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

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

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

事实上, 在validate时, 错误的返回信息有如下4个字段: kind, path, value, and message;

  • kind: 用来表示验证设置的第二个参数. 通常不用
phone: {
        type: String, validate: { validator: function(data) { return /\d{3}-\d{3}-\d{4}/.test(data); }, message: '{VALUE} is not a valid phone number!', //VALUE表明phone存放的值 kind: "invalid phone" } }) 
  • path: 就是字段名
  • value: 你设置的错误内容
  • message: 提示错误信息 看一个总体demo吧:
var validator = function (value) { return /blue|green|white|red|orange|periwinkle/i.test(value); }; Toy.schema.path('color').validate(validator, 'Color `{VALUE}` not valid', 'Invalid color'); //设置了message && kind var toy = new Toy({ color: 'grease'}); toy.save(function (err) { // err is our ValidationError object // err.errors.color is a ValidatorError object assert.equal(err.errors.color.message, 'Color `grease` not valid'); //返回message assert.equal(err.errors.color.kind, 'Invalid color'); 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.

var opts = { runValidators: true }; Test.update({}, update, opts, function(error) { //额外开启runValidators的验证 // There will never be a validation error here }); 

咱们来看一下基本总结吧: valdation

population

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

var mongoose = require('mongoose') , Schema = mongoose.Schema var personSchema = Schema({ _id : Number, name : String, age : Number, stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }] }); var storySchema = Schema({ _creator : { type: Schema.Types.ObjectId, ref: 'Person' }, title : String }); 

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

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

const sam = new Person({ name: 'sam', _id: 1, age: 18, stories: [] }); sam.save((err,sam)=>{ if(err) return err; let story = new Story({ _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执行, 串行 var schema = new Schema(..); schema.pre('save', function(next) { // exe some operations 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('%s has been saved', 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触发的时间为:

var schema = new Schema(..); schema.post('save', function (doc) { console.log('this fired after a document was saved'); }); var Model = mongoose.model('Model', schema); var m = new Model(..); m.save(function (err) { console.log('this fires after the `post` hook'); }); 

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

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

不过可使用虚拟属性,进行操做.

 

生活不止眼前的苟且,还有诗和远方。
相关文章
相关标签/搜索