深刻淺出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 的值仍是不變 

不過可使用虛擬屬性,進行操做.

 

生活不止眼前的苟且,還有詩和遠方。
相關文章
相關標籤/搜索