在MongoDB中,有兩種方式計算聚合:Pipeline 和 MapReduce。Pipeline查詢速度快於MapReduce,可是MapReduce的強大之處在於可以在多臺Server上並行執行復雜的聚合邏輯。MongoDB不容許Pipeline的單個聚合操做佔用過多的系統內存,若是一個聚合操做消耗20%以上的內存,那麼MongoDB直接中止操做,並向客戶端輸出錯誤消息。html
Pipeline 方式使用db.collection.aggregate()函數進行聚合運算,運算速度較快,操做簡單,可是,Pipeline方式有兩個限制:單個聚合操做消耗的內存不能超過20%,聚合操做返回的結果集必須限制在16MB之內。mongodb
建立示例數據,在集合 c5中插入1000條doc,每一個doc中有三個field:idx,name 和 age。bash
for(i=0;i<10000;i++) { db.c5.insert({"idx":i,name:"user "+i,age:i%90}); };
1 、使用$match 管道符過濾collection中doc,使符合條件的doc進入pipeline,可以減小聚合操做消耗的內存,提升聚合的效率。ide
db.c5.aggregate({$match:{age:{$lte:25}}});
2 、使用$project 管道符,使用doc中的部分field進入下級pipeline函數
db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,idx:1,_id:0}} );
project 管道符的做用是選擇字段,重命名字段,派生字段。spa
在$project 管道符中,field:1/0,表示選擇/不選擇 field;將無用的字段從pipeline中過濾掉,可以減小聚合操做對內存的消耗。code
3 、對字段重命名,產生新的字段htm
引用符$,格式是:"$field",表示引用doc中 field 的值,若是要引用內嵌 doc中的字段,使用 "$field1.filed2",表示引用內嵌文檔field1中的字段:field2的值。blog
示例,新建一個field:preIdx,其值和idx 字段的值是相同的。排序
db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":"$idx",idx:1,"_id":0}} );
4.派生字段
在$project中,對字段進行計算,根據doc中的字段值和表達式,派生一個新的字段。
示例,preIdx是根據當前doc的idx 減1 獲得的
db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project: { age:1, "preIdx":{$subtract:["$idx",1]}, idx:1, "_id":0} } );
在$project 執行算術運算的操做符:+($add),*($multiply),/($divide),%($mod),-($subtract)。
對於字符數據,$substr:[expr,start,length]用於求子字符串;
$concat:[expr1,expr2,,,exprn],用於將表達式鏈接在一塊兒;
$toLower:expr 和 $toUpper:expr用於返回expr的小寫或大寫形式。
5.分組操做
使用$group將doc按照特定的字段的值進行分組,$group將分組字段的值相同的doc做爲一個分組進行聚合計算。若是沒有$group 管道符,那麼全部doc做爲一個分組。對每個分組,都能根據業務邏輯須要計算特定的聚合值。
分組操做和排序操做都是非流式的運算符,
流式運算符是指:只要有新doc進入,就能夠對doc進行處理,
非流式運算符是指:必須等收到全部的文檔以後,才能對文檔進行處理。
分組運算符的處理方式是等接收到全部的doc以後,才能對doc進行分組,而後將各個分組發送給pipeline的下一個運算符進行處理。
//至關於 select age,count(*) where age <=25 group by age; db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":"$age",count:{$sum:1}}} ); //對於分組後的數量處理 //至關於 select age,count(*) where age <=25 group by age having count(*) >= 112; db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":"$age",count:{$sum:1}}}, {$match:{count:{$gte:112}}} //分組後的數量大於等於112的 );
若是分組字段有多個,按照 age 和 age2 進行分組,這樣作僅僅是爲了演示,在實際的產品環境中,可使用更多的字段用來分組。
db.c5.aggregate( {$match:{age:{$lte:25}}}, {$project:{age:1,"preIdx":{$subtract:["$idx",1]},idx:1,"_id":0}} , {$group:{"_id":{age:"$age",age2:"$age"},count:{$sum:1}}} )