在MongoDB中使用MapReduce
MapReduce是聚合工具的明星。Count、distinct、group能做的上述事情,MapReduce都能做。它是一個能輕松并行化到多個服務器的聚合方法。它會拆分問題,再將各個部分發送到不同的機器上,讓每臺機器都完成一部分。當所有的機器都完成的時候,再把結果匯集起來形成最終完整的結果。
MapReduce的步驟。 Map->Shuffle->Reduce
Map:將操作映射到集合中的每個文檔。這個操作要么“無作為”,要么產生“一些鍵和幾個值”。
Shuffle: 將Map的結果按照鍵進行分組,并將產生的鍵值組成列表放到對應的鍵中。
Reduce: 將每個鍵對應的列表中的值化簡成一個單值。 這個值被返回,然后接著Shuffle,直到每個鍵的列表中只有一個值為止,這個值就是最終的結果。
MapReduce的函數類似這樣:
db.runCommand({“mapReduce” : ”集合名” , ”map” : “map函數” , ”reduce” : ”reduce函數”});
不同語言有對應的接口:比如java中,可以直接調用executeCommad()方法,或者在集成SpringMongo后,調用mapReduce()接口。
調用示例(SpringMongoDB方式):
searchMongoTemplate.mapReduce(query, collection, map, reduce,
option.outputCollection(out), Map.class);
searchMongoTemplate 是一個MongoOperations對象;
query是一個Query的對象, mapReduce操作只對查詢結果進行;
map類似于"classpath:mapReduce/mapDayDetail.js" ,是map的js文件存放地址,或者可以直接將map的js文件讀成String;
reduce的意義類似map,對應reduce的js文件;
out: 是輸出文件的集合名稱
Map.class: 輸出的對象類型,可以是自定義對象
Map.js與Reduce.js舉例說明:
Map.js
function() {
var renewTime = this.renewTime;
var date = new Date(renewTime);
var time = date.getFullYear()*10000+(date.getMonth()+1)*100+date.getDate();
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"level" : this.productLevel,
"time" : time
}, {
'count' : 1
});
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"node" : this.nodeName,
"time" : time
}, {
'count' : 1
});
emit({
"year" : date.getFullYear(),
"month" : date.getMonth() + 1,
"day" : date.getDate(),
"satellite" : this.satelliteName,
"time" : time
}, {
'count' : 1
});
};
Map.js的腳本作用于輸入的每一個文檔,this指代當前文檔,用this.屬性名的方式獲取文檔中的屬性,用emit方式返回鍵值對結果。本例中,對于每個文檔,返回三個結果,emit函數中,第一個參數是key,第二個參數是value.
Reduce.js
function(key, emits) {
var result = {
"count" : 0
};
for (var i in emits) {
result.count += emits[i].count;
}
return result;
}
傳遞給reduce的參數可以是map的結果,也可以是reduce自己的結果。所以reduce必須能被反復調用,reduce返回的結果必須能作為reduce第二個參數中的一個元素。Reduce中只需要返回value即可,返回的key默認是傳進來的key.