標籤:
MongDB的MapReduce相當於MySQL中的“group by”,所以在MongoDB上使用Map/Reduce進行並行“統計”很容易。
使用MapReduce要實現兩個函數Map函數和Reduce函數,Map函數調用emit(key,value),遍曆collection中的所有記錄,將key和value傳遞給Reduce函數進行處理。Map函數和Reduce函數可以使用JS來實現,可以通過db.runCommand或mapReduce命令來執行一個MapReduce操作。
樣本shell
db.runCommand({ mapreduce : <collection>,map : <mapfunction>,reduce : <reducefunction>[, query : <query filter object>][, sort : <sorts the input objects using this key. Useful for optimization, like sorting by theemit key for fewer reduces>][, limit : <number of objects to return from collection>][, out : <see output options below>][, keeptemp: <true|false>][, finalize : <finalizefunction>][, scope : <object where fields go into javascript global scope >][, verbose : true]});View Code
參數說明:
mapreduce: 要操作的目的地組合。
map: 映射函數 (產生索引值對序列,作為 reduce 函數參數)。
reduce: 統計函數。
query: 目標記錄過濾。
sort: 目標記錄排序。
limit: 限制目標記錄數量。
out: 統計結果存放集合 (不指定則使用臨時集合,在用戶端斷開後自動刪除)。
keeptemp: 是否保留臨時集合。
finalize: 最終處理函數 (對 reduce 返回結果進行最終整理後存入結果集合)。
scope: 向 map、reduce、finalize 匯入外部變數。
verbose: 顯示詳細的時間統計資訊。
下面我們準備資料以備後面樣本所需
> db.students.insert({classid:1, age:14, name:‘Tom‘})> db.students.insert({classid:1, age:12, name:‘Jacky‘})> db.students.insert({classid:2, age:16, name:‘Lily‘})> db.students.insert({classid:2, age:9, name:‘Tony‘})> db.students.insert({classid:2, age:19, name:‘Harry‘})> db.students.insert({classid:2, age:13, name:‘Vincent‘})> db.students.insert({classid:1, age:14, name:‘Bill‘})> db.students.insert({classid:2, age:17, name:‘Bruce‘})>View Code
現在我們示範如何統計1班和2班的學生數量
Map 函數必須調用 emit(key, value) 返回索引值對,使用 this 訪問當前待處理的 Document。
這裡this一定不能忘了!!!
> m = function() { emit(this.classid, 1) }function () {emit(this.classid, 1);}>
value 可以使用 JSON Object 傳遞 (支援多個屬性值)。例如:
emit(this.classid, {count:1})
Reduce 函數接收的參數類似 Group 效果,將 Map 返回的索引值序列組合成 { key, [value1,value2, value3, value...] } 傳遞給 reduce。
> r = function(key, values) {... var x = 0;... values.forEach(function(v) { x += v });... return x;... }function (key, values) {var x = 0;values.forEach(function (v) {x += v;});return x;}>
Reduce 函數對這些 values 進行 "統計" 操作,返回結果可以使用 JSON Object。
結果如下:
> res = db.runCommand({... mapreduce:"students",... map:m,... reduce:r,... out:"students_res"... });{"result" : "students_res","timeMillis" : 1587,"counts" : {"input" : 8,"emit" : 8,"output" : 2},"ok" : 1}> db.students_res.find(){ "_id" : 1, "value" : 3 }{ "_id" : 2, "value" : 5 }>View Code
mapReduce() 將結果儲存在 "students_res" 表中。
利用 finalize() 我們可以對 reduce() 的結果做進一步處理。
> f = function(key, value) { return {classid:key, count:value}; }function (key, value) {return {classid:key, count:value};}>
我們再重新計算一次,看看返回的結果:
> res = db.runCommand({... mapreduce:"students",... map:m,... reduce:r,... out:"students_res",... finalize:f... });{"result" : "students_res","timeMillis" : 804,"counts" : {"input" : 8,"emit" : 8,"output" : 2},"ok" : 1}> db.students_res.find(){ "_id" : 1, "value" : { "classid" : 1, "count" : 3 } }{ "_id" : 2, "value" : { "classid" : 2, "count" : 5 } }>View Code
列名變與 “classid”和”count”了,這樣的列表更容易理解。
我們還可以添加更多的控制細節。
> res = db.runCommand({... mapreduce:"students",... map:m,... reduce:r,... out:"students_res",... finalize:f,... query:{age:{$lt:10}}... });{"result" : "students_res","timeMillis" : 358,"counts" : {"input" : 1,"emit" : 1,"output" : 1},"ok" : 1}> db.students_res.find();{ "_id" : 2, "value" : { "classid" : 2, "count" : 1 } }>View Code
可以看到先進行了過濾,只取age<10 的資料,然後再進行統計,所以就沒有1 班的統計資料了。
MongoDB整理筆記のMapReduce