MongoDB除了基本的查詢功能,還提供了很多強大的聚合工具,其中簡單的可計算集合中的文檔個數,
復雜的可利用MapReduce做復雜數據分析.
1.count
count返回集合中的文檔數量
db.refactor.count()
不管集合有多大,都能很快的返回文檔數量.
可以傳遞查詢,MongoDB會計算查詢結果的數量
db.refactor.count({"username":"refactor"})
但是增加查詢條件會使count變慢.
2.distinct
distinct用來找出給定鍵的所有不同值.使用時必須指定集合和鍵.
如:
db.runCommand({"distinct":"refactor","key":"username"})
3.group
group先選定分組所依據的鍵,MongoDB將會將集合依據選定鍵值的不同分成若干組.然后可以通過聚合每一組內的文檔,
產生一個結果文檔.
如:
db.runCommand(
{
"group":
{
"ns":"refactor",
"key":{"username":true},
"initial":{"count":0},
"$reduce":function(doc,prev)
{
prev.count++;
},
"condition":{"age":{"$gt":40}}
}
}
)
"ns":"refactor",
指定要進行分組的集合
"key":{"username":true},
指定文檔分組的依據,這里是username鍵,所有username鍵的值相等的被划分到一組,true為返回鍵username的值
"initial":{"count":0},
每一組reduce函數調用的初始個數.每一組的所有成員都會使用這個累加器.
"$reduce":function(doc,prev){...}
每個文檔都對應的調用一次.系統會傳遞兩個參數:當前文檔和累加器文檔.
"condition":{"age":{"$gt":40}}
這個age的值大於40的條件
4.使用完成器
完成器用於精簡從數據庫傳到用戶的數據.group命令的輸出一定要能放在單個數據庫相應中.
"finalize"附帶一個函數,在數組結果傳遞到客戶端之前被調用一次.
db.runCommand(
{
"group":
{
"ns":"refactor",
"key":{"username":true},
"initial":{"count":0},
"$reduce":function(doc,prev)
{
prev.count++;
},
"finalize":function(doc)
{
doc.num=doc.count;
delete doc.count;
}
}
}
)
finalize能修改傳遞的參數也能返回新值.
5.將數組作為鍵使用
有些時候分組所依據的條件很復雜,不僅是一個鍵.比如要使用group計算每個類別有多篇博客文章.由於有很多作者,
給文章分類時可能不規律的使用了大小寫.所以,如果要是按類別名來分組,最后"MongoDB"和"mongodb"就是不同的組.
為了消除這種大小寫的影響,就要定義一個函數來確定文檔所依據的鍵.
定義分組要用到$keyf
db.runCommand(
{
"group":
{
"ns":"refactor",
"$keyf":function(doc){return {"username":doc.username.toLowerCase()}},
"initial":{"count":0},
"$reduce":function(doc,prev)
{
prev.count++;
}
}
}
)
6.MapReduce
count,distinct,group能做的事情MapReduce都能做.它是一個可以輕松並行化到多個服務器的聚合方法.它會
拆分問題,再將各個部分發送到不同機器上,讓每台機器完成一部分.當所有機器都完成時候,再把結果匯集起來形成
最終完整的結果.
MapReduce需要幾個步驟:
1.映射,將操作映射到集合中的每個文檔.這個操作要么什么都不做,要么 產生一個鍵和n個值.
2.洗牌,按照鍵分組,並將產生的鍵值組成列表放到對應鍵中.
3.化簡,把列表中的值 化簡 成一個單值,這個值被返回.
4.重新洗牌,直到每個鍵的列表只有一個值為止,這個值就是最終結果.
MapReduce的速度比group慢,group也很慢.在應用程序中,最好不要用MapReduce,可以在后台運行MapReduce
創建一個保存結果的集合,可以對這個集合進行實時查詢.
找出集合中的所有鍵
MongoDB沒有模式,所以並不知曉每個文檔有多少個鍵.通常找到集合的所有鍵的做好方式是用MapReduce.
在映射階段,想得到文檔中的每個鍵.map函數使用emit 返回要處理的值.emit會給MapReduce一個鍵和一個值.
這里用emit將文檔某個鍵的記數(count)返回({count:1}).我們為每個鍵單獨記數,所以為文檔中的每一個鍵調用一次emit,
this是當前文檔的引用:
map=function(){
for(var key in this)
{
emit(key,{count:1})
}
};
這樣返回了許許多多的{count:1}文檔,每一個都與集合中的一個鍵相關.這種有一個或多個{count:1}文檔組成的數組,
會傳遞給reduce函數.reduce函數有兩個參數,一個是key,也就是emit返回的第一個值,另一個參數是數組,由一個或者多個
對應鍵的{count:1}文檔組成.
reduce=function(key,emits){
total=0;
for(var i in emits){
total+=emits[i].count;
}
return {count:total};
}
reduce要能被反復被調用,不論是映射環節還是前一個化簡環節.reduce返回的文檔必須能作為reduce的
第二個參數的一個元素.如x鍵映射到了3個文檔{"count":1,id:1},{"count":1,id:2},{"count":1,id:3}
其中id鍵用於區別.MongoDB可能這樣調用reduce:
>r1=reduce("x",[{"count":1,id:1},{"count":1,id:2}])
{count:2}
>r2=reduce("x",[{"count":1,id:3}])
{count:1}
>reduce("x",[r1,r2])
{count:3}
reduce應該能處理emit文檔和其他reduce結果的各種集合.
如:
mr=db.runCommand(
{
"mapreduce":"refactor",
"map":map,
"reduce":reduce,
"out":{inline:1}
}
)
或:
db.refactor.mapReduce(map,reduce,{out:{inline:1}})
"timeMillis" : 5,//操作花費的時間
"counts" : {
"input" : 10,//發往到map函數的文檔個數
"emit" : 40,//在map函數中emit被調用的次數
"reduce" : 4,//在map函數中reduce被調用的次數
"output" : 4//結果集合中創建的文檔數量.
},
1.mapreduce是根據map函數里調用的emit函數的第一個參數來進行分組的
2.僅當根據分組鍵分組后一個鍵匹配多個文檔,才會將key和文檔集合交由reduce函數處理
注意MongoDB 1.8版本以上,必須指明 out 參數
否則會報如下錯誤:
"assertion" : "'out' has to be a string or an object",
"assertionCode" : 13606,
MapReduce中的其他鍵
mapreduce,map,reduce這三個鍵是必須的,MapReduce命令還有其他的可選鍵
finalize:函數
將reduce的結果發送給這個鍵,這是處理過程的最后一步
keeptemp:布爾值
連接關閉時,臨時結果是否保存
output:字符串
結果集合的名字,設定該項則隱含着keeptemp:true
query:文檔
會在發往map函數前,先用指定條件過濾文檔
sort:文檔
會在發往map函數前先給文檔排序
limit:整數
發往map函數文檔的最大數量
scope:文檔
javascript代碼中要用到的變量
verbose:布爾值
是否產生更加信息的服務器日志