MongoDB的聚合操作主要是對數(shù)據(jù)的批量處理箕宙。一般都是將記錄按條件分組之后進(jìn)行一系列求最大值,最小值逾苫,平均值的簡單操作卿城,也可以對記錄進(jìn)行數(shù)據(jù)統(tǒng)計(jì),數(shù)據(jù)挖掘的復(fù)雜操作铅搓。聚合操作的輸入是集中的文檔瑟押,輸出可以是一個文檔也可以是多個文檔。
MongoDB 提供了三種強(qiáng)大的聚合操作:
Pipeline查詢速度快于MapReduce星掰,但是MapReduce的強(qiáng)大之處在于能夠在多臺Server上并行執(zhí)行復(fù)雜的聚合邏輯多望。MongoDB不允許Pipeline的單個聚合操作占用過多的系統(tǒng)內(nèi)存嫩舟,如果一個聚合操作消耗20%以上的內(nèi)存,那么MongoDB直接停止操作怀偷,并向客戶端輸出錯誤消息家厌。
本篇主要講解 MapReduce 編程模型。MapReduce是一種計(jì)算模型椎工,簡單的說就是將大批量的工作(數(shù)據(jù))分解(MAP)執(zhí)行饭于,然后再將結(jié)果合并成最終結(jié)果(REDUCE)。
一维蒙、MapReduce 命令
MapReduce 的基本語法如下:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函數(shù)
function(key,values) {return reduceFunction}, //reduce 函數(shù)
{
out: collection,
query: document,
sort: document,
limit: number,
finalize: <function>,
scope: <document>,
jsMode: <boolean>,
verbose: <boolean>
}
)
使用 MapReduce 要實(shí)現(xiàn)兩個函數(shù) Map 函數(shù)和 Reduce 函數(shù),Map 函數(shù)調(diào)用 emit(key, value), 遍歷 collection 中所有的記錄, 將 key 與 value 傳遞給 Reduce 函數(shù)進(jìn)行處理掰吕。
參數(shù)說明:
- map:是JavaScript 函數(shù),負(fù)責(zé)將每一個輸入文檔轉(zhuǎn)換為零或多個文檔颅痊,通過key進(jìn)行分組畴栖,生成鍵值對序列,作為 reduce 函數(shù)參數(shù)
- reduce:是JavaScript 函數(shù),對map操作的輸出做合并的化簡的操作(將key-values變成key-value八千,也就是把values數(shù)組變成一個單一的值value)
- out:統(tǒng)計(jì)結(jié)果存放集合 (不指定則使用臨時集合,在客戶端斷開后自動刪除)。
- query: 一個篩選條件燎猛,只有滿足條件的文檔才會調(diào)用map函數(shù)恋捆。(query。limit重绷,sort可以隨意組合)
- sort: 和limit結(jié)合的sort排序參數(shù)(也是在發(fā)往map函數(shù)前給文檔排序)沸停,可以優(yōu)化分組機(jī)制
- limit: 發(fā)往map函數(shù)的文檔數(shù)量的上限(要是沒有l(wèi)imit,單獨(dú)使用sort的用處不大)
- finalize:可以對reduce輸出結(jié)果再一次修改昭卓,跟group的finalize一樣愤钾,不過MapReduce沒有g(shù)roup的4MB文檔的輸出限制
- scope:向map、reduce候醒、finalize導(dǎo)入外部變量
- verbose:是否包括結(jié)果信息中的時間信息能颁,默認(rèn)為fasle
關(guān)于MapReduce的工作流程如下:
在集合 orders 中查找 status:"A" 的數(shù)據(jù),并根據(jù) cust_id 來分組倒淫,并計(jì)算 amount 的總和伙菊。
二、使用示例
對以下結(jié)構(gòu)的文檔進(jìn)行統(tǒng)計(jì)敌土。統(tǒng)計(jì)每個用戶的文章數(shù)量镜硕。
>db.col.find()
{
"_id" : ObjectId("5c09dfcde354b306e46af7f3"),
"bookname" : "Java 8 實(shí)戰(zhàn)",
"author" : "simon",
"status" : "active"
},
{
"_id" : ObjectId("5c09dfdee354b306e46af7f4"),
"bookname" : "MongoDB權(quán)威指南",
"author" : "simon",
"status" : "active"
},
{
"_id" : ObjectId("5c09dfffe354b306e46af7f5"),
"bookname" : "MyBatis 實(shí)戰(zhàn)",
"author" : "simon",
"status" : "disabled"
},
{
"_id" : ObjectId("5c09e016e354b306e46af7f6"),
"bookname" : "MyBatis 從入門到具精通",
"author" : "Aaron",
"status" : "disabled"
},
{
"_id" : ObjectId("5c09e02ce354b306e46af7f7"),
"bookname" : "Spring Boot 2.0",
"author" : "Aaron",
"status" : "active"
},
{
"_id" : ObjectId("5c09e037e354b306e46af7f8"),
"bookname" : "Spring Cloud",
"author" : "Aaron",
"status" : "active"
},
{
"_id" : ObjectId("5c09e038e354b306e46af7f9"),
"bookname" : "Spring Cloud",
"author" : "Aaron",
"status" : "active"
}
將在 col 集合中使用 mapReduce 函數(shù)來選取已發(fā)布的文章(status:"active"),并通過author分組返干,計(jì)算每個用戶的文章數(shù)
>db.col.mapReduce(
function() { emit(this.author,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"total"
}
)
{
"result" : "total",
"timeMillis" : 422.0,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 2,
"output" : 2
},
"ok" : 1.0,
"_o" : {
"result" : "total",
"timeMillis" : 422,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 2,
"output" : 2
},
"ok" : 1.0
},
"_keys" : [
"result",
"timeMillis",
"counts",
"ok"
],
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_name" : "ibase_dev"
},
"_coll" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_name" : "ibase_dev"
},
"_shortName" : "total",
"_fullName" : "ibase_dev.total"
}
}
從結(jié)果中可以看出兴枯,一共有5個文檔服務(wù){status:'active'}
的文檔,在map函數(shù)中生成了5個鍵值對文檔矩欠,最后使用reduce函數(shù)將相同的鍵值分為 2 組财剖。
具體參數(shù)說明:
- result:儲存結(jié)果的collection的名字,這是個臨時集合悠夯,MapReduce的連接關(guān)閉后自動就被刪除了。
- timeMillis:執(zhí)行花費(fèi)的時間峰伙,毫秒為單位
- input:滿足條件被發(fā)送到map函數(shù)的文檔個數(shù)
- emit:在map函數(shù)中emit被調(diào)用的次數(shù)疗疟,也就是所有集合中的數(shù)據(jù)總量
- ouput:結(jié)果集合中的文檔個數(shù)(count對調(diào)試非常有幫助),
out: { inline: 1 }
不會創(chuàng)建集合,結(jié)果在內(nèi)存中 - ok:是否成功瞳氓,成功為1
- err:如果失敗策彤,這里可以有失敗原因,不過從經(jīng)驗(yàn)上來看匣摘,原因比較模糊店诗,作用不大
查看執(zhí)行結(jié)果:
>db.total.find()
{
"_id" : "Aaron",
"value" : 3.0
},
{
"_id" : "simon",
"value" : 2.0
}