Mongodb中的聚合架構
1 .Mongodb 中的管道操作符之簡單操作,
Mongo 中的資料查詢語句db.collection.find({},{})中的第一個大括号是用來控制過濾條件的,
第二個大括号是控制key的顯示的。如果想要對查詢出來的結果集中的字段重指令,求平均值,分組等操作
的時候這個find就實作不了。
要想對查詢出來的結果集做進一步處理,mongo提供一種實作的方式,聚合架構(aggreagate) 。
下面我們來進一步讨論這個aggreagate 怎麼使用。。
在使用aggregate實作聚合操作之前,我們首先來認識下幾個常用的聚合操作符。
$project::可以對結果集中的鍵 重指令,控制鍵是否顯示,對列進行計算。
$match: 過濾結果集
$group:分組,聚合,求和,平均數,等
$skip: 在顯示結果的時候跳過前幾行
$sort:對即将顯示的結果集排序
$limit:控制結果集的大小
(1).$project::可以對結果集中的鍵 重指令,控制鍵是否顯示。如果在操作中,
執行個體一:
> db.test.find({},{_id:0}).limit(2);
{ "id" : 1, "iname" : "ToneyDeng", "iage" : 21, "iadd" : "ShengZheng", "mark" : "" }
{ "id" : 2, "iname" : "Jack", "iage" : 24, "iadd" : "ShangHai", "mark" : "" }
> db.test.aggregate({$project:{did:"$id",dname:"$iname",dage:"$iage",daddr:"$iadd",_id:0}},{$limit:2});
{
"result" : [
{
"did" : 1,
"dname" : "ToneyDeng",
"dage" : 21,
"daddr" : "ShengZheng"
},
"did" : 2,
"dname" : "Jack",
"dage" : 24,
"daddr" : "ShangHai"
}
],
"ok" : 1
}
在以上執行個體中,隐藏了_id,然後将其他所有的key的名字重新設定了下。
這裡需要重點注意的是在對key重指令後,如果該key下面有索引,将使查詢無法使用到索引。
是以盡量在查詢後進行重指令。
(2)$match: 這個函數的功能其實和find({})差不多,主要過濾結果集的。
執行個體二:
> db.test.find({iage:{$lt:25}},{}).limit(2);
{ "_id" : ObjectId("54c1fe77570ca4579ca978e5"), "id" : 1, "iname" : "ToneyDeng", "iage" : 21, "iadd" : "ShengZheng", "mark" : "" }
{ "_id" : ObjectId("54c1febb570ca4579ca978e6"), "id" : 2, "iname" : "Jack", "iage" : 24, "iadd" : "ShangHai", "mark" : "" }
> db.test.aggregate({$match:{iage:{$lt:25}}},{$limit:2})
"_id" : ObjectId("54c1fe77570ca4579ca978e5"),
"id" : 1,
"iname" : "ToneyDeng",
"iage" : 21,
"iadd" : "ShengZheng",
"mark" : ""
"_id" : ObjectId("54c1febb570ca4579ca978e6"),
"id" : 2,
"iname" : "Jack",
"iage" : 24,
"iadd" : "ShangHai",
>
以上兩個查詢的結果集一樣隻是展示的方式不一樣而已。
在進行聚合group之前最好執行match,減少資料量,提高工作效率。另外如果過濾的key上有索引,
那麼查詢會走索引。
(3). $group:分組,聚合,求和,平均數,等
執行個體三:
> db.test.aggregate({$group:{_id:"$iname",ct:{$sum:1},avg:{$avg:"$iage"}}},{$limit:3});
"_id" : "Tom",
"ct" : 3,
"avg" : 25.666666666666668
"_id" : "Owen",
"ct" : 2,
"avg" : 26
"_id" : "Smith",
"ct" : 1,
"avg" : 27
以上是一個簡單的$group操作,這裡需要重點解釋一下幾個字段
_id:分組字段,該字段的值必須為”$key” ,并且_id是固定的不能随意改變,該清單示需要的分組的字段,可以為單字段分組,也可以多字段分組(後面示範)。
ct:表示總的記錄數,
avg:平均值。
(4).$skip: 在顯示結果的時候跳過前幾行
執行個體四:
> db.test.find({}).sort({id:1});
{ "_id" : ObjectId("54c1ff22570ca4579ca978e7"), "id" : 3, "iname" : "Smith", "iage" : 27, "iadd" : "ShangHai", "mark" : "" }
{ "_id" : ObjectId("54c1ff36570ca4579ca978e8"), "id" : 4, "iname" : "Owen", "iage" : 27, "iadd" : "BeiJing", "mark" : "" }
{ "_id" : ObjectId("54c1ff44570ca4579ca978e9"), "id" : 5, "iname" : "Owen", "iage" : 25, "iadd" : "BeiJing", "mark" : "" }
{ "_id" : ObjectId("54c1ffc2570ca4579ca978ea"), "id" : 6, "iname" : "Tom", "iage" : 25, "iadd" : "WuHan", "mark" : "" }
{ "_id" : ObjectId("54c1ffd3570ca4579ca978eb"), "id" : 7, "iname" : "Tom", "iage" : 25, "iadd" : "BeiJing", "mark" : "" }
{ "_id" : ObjectId("54c2005e570ca4579ca978ec"), "id" : 8, "iname" : "Tom", "iage" : 27, "iadd" : "GuangZhou", "mark" : "nihao" }
{ "_id" : ObjectId("54c20076570ca4579ca978ed"), "id" : 9, "iname" : "Jack", "iage" : 23, "iadd" : "GuangZhou", "mark" : "Yahoo" }
> db.test.aggregate({$sort:{id:1}},{$skip:6});
"_id" : ObjectId("54c1ffd3570ca4579ca978eb"),
"id" : 7,
"iname" : "Tom",
"iage" : 25,
"iadd" : "BeiJing",
"_id" : ObjectId("54c2005e570ca4579ca978ec"),
"id" : 8,
"iage" : 27,
"iadd" : "GuangZhou",
"mark" : "nihao"
"_id" : ObjectId("54c20076570ca4579ca978ed"),
"id" : 9,
"iage" : 23,
"mark" : "Yahoo"
通過上面的兩個結果集的對比可以發現結果二成功跳過了前6行。
(5)$sort:對即将顯示的結果集排序 , $limit:控制結果集的大小
關于這兩個管道操作符的的使用方法比較簡單,在執行個體四和執行個體三中有使用到。
2.Mongodb 中的管道操作符之複雜操作,
執行個體五:分組後,重命名字段。
> db.test.aggregate({$group:{_id:"$iname",count:{$sum:1},avg:{$avg:"$iage"}}},{$project:{id:"$_id",ct:"$count",ag:"$avg",_id:0}})
"id" : "Tom",
"ag" : 25.666666666666668
"id" : "Owen",
"ag" : 26
"id" : "Smith",
"ag" : 27
"id" : "Jack",
"ag" : 23.5
"id" : "ToneyDeng",
"ag" : 21
執行個體六:重命名字段再分組。
> db.test.aggregate({$project:{_id:0,aid:"$id",aname:"$iname",age:"$iage"}},{$group:{_id:"$aname",ct:{$sum:1},ag:{$avg:"$age"}}});
"_id" : "Jack",
"_id" : "ToneyDeng",
執行個體七:過濾再分組.
> db.test.aggregate({$match:{id:{$lt:6}}},{$group:{_id:"$iname",ct:{$sum:1},ag:{$avg:"$iage"}}});
"ag" : 24
執行個體八:分組再過濾
> db.test.aggregate({$group:{_id:"$iname",ct:{$sum:1},ag:{$avg:"$iage"}}},{$match:{ct:{$lte:2}}});
總結: 這幾個聚合操作符之間沒有嚴格的使用順序,隻要按照一定的規則搭配使用,一般的聚合都可以實作。
問題一:在聚合架構中,如何統計結果集的總數。
問題二:在聚合架構中,如何檢視執行計劃,
3.Mongodb 中的管道表達式之常用表達式。
管道表達式
project:部分
#數學表達式
$add:一個或多個表達式相加
$subtract:接收兩個表達式,傳回兩表達式的之差
$multiply:接收一個或多個表達式,傳回它們相乘的結果集
$divide:接收兩個表達式,傳回第一個除以第二個表達式的商
$mod:接收兩個表達式,傳回第一個表達式除以第二個表達式的餘數(模型)
#日期表達式
$year:年份
$month:月份
$week:一年中的第幾周
$dayOfMonth:本月的第幾天
$dayOfWeek:一個星期的第幾天
$dayOfYear:一年中的第幾天
$hour:擷取小時部分
$minute:取得分鐘數
$second:取得秒數
#字元串表達式
$substr:字元串截取函數,相當于mysql中substr
$concat:字元串連接配接函數
$toLower:将字元串值變為小寫
$toUpper:将字元串值變為大寫
#邏輯表達式
$cpm:接收兩個表達式[exp1,exp2],如果exp1<exp2,就傳回-1,如果exp1=exp2,就傳回1,如果exp1>exp2就傳回1
$cond:[bool,exp1,exp2],如果bool為true就傳回exp1,否則傳回exp2
$ifnull:[exp1,exp2],如果exp1為null,就傳回exp2
$strcasecmp:比較兩個字元串的大小
$eq/$ne/$gt/$gte/$lt/$lte:以此為(等于/不等于/大于/大于或等于/小于/小于或等于)
$and:并且
$or:或
$not:否
$in:相當于mysql中 in
$ne:不等于。
$exists:列是存在。
(1)$add:一個或多個表達式相加
> db.test.aggregate({$project:{iage:{$add:["$iage",1]},_id:0}},{$limit:3});
"iage" : 22
"iage" : 25
"iage" : 28
(2).$subtract:接收兩個表達式,傳回兩表達式的之差
> db.test.aggregate({$project:{iage:{$add:["$iage",1]},subtr:{$subtract:["$iage",20]},_id:0}},{$limit:3});
"iage" : 22,
"subtr" : 1
"subtr" : 4
"iage" : 28,
"subtr" : 7
(3).$multiply:接收一個或多個表達式,傳回它們相乘的結果集
> db.test.aggregate({$project:{multi:{$multiply:["$iage",2]},_id:0}},{$limit:3});
"multi" : 42
"multi" : 48
"multi" : 54
(4).$divide:接收兩個表達式,傳回第一個除以第二個表達式的商
> db.test.aggregate({$project:{divide:{$divide:["$iage",11]},_id:0}},{$limit:3});
"divide" : 1.9090909090909092
"divide" : 2.1818181818181817
"divide" : 2.4545454545454546
(5).$mod:接收兩個表達式,傳回第一個表達式除以第二個表達式的餘數(模型)
> db.test.aggregate({$project:{iage:1,mod:{$mod:["$iage",20]},_id:0}},{$limit:3});
"mod" : 1
"mod" : 4
"mod" : 7
這個表達式很特别,可以在find,和match中使用但是好像是有點問題.
> db.test.find({id:{$mod:[21,4]}}).limit(3);
> db.test.find({id:{$mod:[7,4]}}).limit(3);
以上兩個查詢中,在正常情況下,查詢出來的id應為,1,3 不應該為4,。
(6).#日期表達式部分.
$year:年份
用法很單一,這裡就用$year為例。
> db.test.aggregate({$project:{_id:0,iname:1,year:{$year:[new Date()]}}},{$limit:2});
"year" : 2015
(7).#字元串表達式
> db.test.aggregate({$project:{_id:0,id:1,iname:1,substr:{$substr:["$iname",0,4]}}},{$limit:2});
"substr" : "Tone"
"substr" : "Jack"
(8).$concat:字元串連接配接函數
> db.test.aggregate({$project:{_id:0,id:1,iname:1,iadd:1,concat:{$concat:["$iname"," ","$iadd"]}}},{$limit:2});
"concat" : "ToneyDeng ShengZheng"
"concat" : "Jack ShangHai"
(9).$toLower:将字元串值變為小寫 ,$toUpper:将字元串值變為大寫
$toLower和$toUpper的使用方法一樣,
> db.test.aggregate({$project:{_id:0,iname:1,toLower:{$toLower:["$iname"]}}},{$limit:2});
"toLower" : "toneydeng"
"toLower" : "jack"
(10)。#邏輯表達式
$cpm:接收兩個表達式[exp1,exp2],如果exp1<exp2,就傳回-1,如果exp1=exp2,就傳回1,如果exp1>exp2就傳回
> db.test.aggregate({$project:{_id:0,iname:1,iadd:1,cmp:{$cmp:["$iname","$iadd"]}}},{$limit:2});
"cmp" : 1
"cmp" : -1
(11).$strcasecmp:比較兩個字元串的大小(這個具體還不知道是怎麼回事!,不過文法沒有問題。)
> db.test.aggregate({$project:{_id:0,iname:1,iadd:1,strcasecmp:{$strcasecmp:["w","w"]}}},{$limit:2});
"strcasecmp" : 0
(12).$cond:[bool,exp1,exp2],如果bool為true就傳回exp1,否則傳回exp2
> db.test.aggregate({$project:{_id:0,iname:1, con:{$cond:[false,"$iage","$iadd"]}}},{$limit:2});
"con" : "ShengZheng"
"con" : "ShangHai"
(14) 運算表達式,這幾個表達式可以在find和match中用來過濾文檔數量
$exists:某個鍵是否存在。
相當于 select * from tableName where iname <>”ToneyDeng” and iage<25
> db.test.aggregate({$match:{$and:[{iname:{$ne:"ToneyDeng"}},{iage:{$lt:25}}]}});
相當于:select * from tableName where id not in (1,3,5,6,7,8) or iname<>”ToneyDeng” and iage<25
> db.test.aggregate({$match: {$or:[{id:{$not:{$in:[1,3,5,6,7,8]}}} ,{$and:[{iname:{$ne:"ToneyDeng"}},{iage:{$lt:25}}]}]}});
"_id" : ObjectId("54c1ff36570ca4579ca978e8"),
"id" : 4,
"iname" : "Owen",
鍵iname 是否存在
> db.test.aggregate({$match:{iname:{$exists:true}}},{$limit:2});
(15).group:聚合操作符
#算術操作符
$sum:求和
$avg:平均數
$max:最大值
$min:最小值
$first:第一個值
$last:最後一個值
> db.test.aggregate({$group:{_id:"$iname",count:{$sum:1},sum:{$sum:"$iage"},avg:{$avg:"$iage"},max:{$max:"$iage"},min:{$min:"$iage"}}},{$limit:2});
"count" : 3,
"sum" : 77,
"avg" : 25.666666666666668,
"max" : 27,
"min" : 25
"count" : 2,
"sum" : 52,
"avg" : 26,
得到分組中的第一個值和最後一個值
> db.test.aggregate({$group:{_id:"$iname",ct:{$sum:1},first:{$first:"$iage"},min:{$min:"$iage"},last:{$last:"$iage"}}},{$limit:2});
"first" : 25,
"min" : 25,
"last" : 27
"first" : 27,
"last" : 25
4.#數組操作符
$addTOSet:expx ,如果目前數組中不包含expx就把它添加到數組中。
$push:expx,不管expx是什麼值,都expx 都添加到數組中。
5.mongo聚合操作操作還可以通過以下方式實作,
不過似乎是要寫js腳本,這個就有點複雜了。
MapReduce
db.runCommand():執行資料庫指令
參考連結:
http://www.cnblogs.com/shanyou/p/3494854.html