MapReduce

Based on 68

MongoDBでのmap/reduceは、バッチでのデータの更新や集計処理で便利に使えます。すべての入力はコレクションから、すべての出力はコレクションへ、という方針はHadoopに少しにています。SQLでGROUP BYを使いたいようなとき、MongoDBではmap/reduceを使うのが正しいです。

MongoDBでは、インデックスと通常のクエリはmap/reduceとは別です。もし、CouchDBの使ったことがあるなら、これはとても大きな違いになります。MongoDBはでは、クエリーとインデックスはMySQLに近いです。 これらの操作については、 クエリーインデックス を参照してください。

概要

バージョン1.1.1以上

map/reduce はデータベース [command] を経由して、呼び出されます。   データバースは一時的なコレクションを出力結果用に作成します。この一時的なコレクションは、クライアントのコネクションが閉じたとき、または明示的にdropされた場合削除されます。また、永続的な出力用のコレクション名を指定することもできます。  mapreduce ファンクションはJavaScriptで書き、サーバ上で実行されます。

コマンドの文法:

db.runCommand( 
{ mapreduce : <collection>, 
map : <map ファンクション名>, 
reduce : <reduce ファンクション名> 
[, query : <クエリーフィルタオブジェクト>] 
[, sort : <クエリをソート。最適化用です>] 
[, limit : <最大何件のオブジェクトを返すか>] 
[, out : <出力先のコレクション名>] 
[, keeptemp: <true|false>] 
[, finalize : <finalize ファンクション>] 
[, scope : <object where fields go into javascript global scope >] 
[, verbose : true] 
 } 
); 
  • keeptemp - true を指定した場合、作成されたコレクションは「一時用」として扱われません。デフォルトはfalseです。  out が指定された場合、コレクションは自動的にパーマネントになります。
  • finalize - 終了時にすべての結果に対し実行されるファンクションです。
  • verbose - 実行時間の統計を出します
  • scope - map/reduce/finalize から呼び出すことのできる変数を渡します example mr5

結果:

{ result : <collection_name>, 
counts : { 
input : <走査されたオブジェクトの数>, 
emit : <emitが呼ばれた回数>, 
output : <出力結果の数> 
} , 
timeMillis : <job_time>, 
ok : <1_if_ok>, 
[, err : <errmsg_if_error>] 
} 

MongoDB shell にはこのためのコマンドヘルパーがあります。

db.collection.mapreduce(mapfunction,reducefunction[,options]);

map, reduce, finalize ファンクションはJavascriptで書きます。

map ファンクション

map ファンクションは、 評価中の現在のオブジェクトを{{this}} 変数で指します。 mapファンクションは、 最低一回は{{emit(key,value)}}を呼ぶ必要があります。また何回でも呼ぶこともできます。

function map(void) -> void 

reduce ファンクション

reduce ファンクションは、キーと配列(array)を受け取ります。受け取った値を減らし(reduce)、結果を返します。

function reduce(key, value_array) -> value 

mapreduceエンジンは、reduceファンクションを繰り返し呼ぶことがあります。そのため、reduceファンクションはidempotentになる必要があります。 これは以下の条件を満たす必要があること、ということです。

for all k,vals : reduce( k, [reduce(k,vals)] ) == reduce(k,vals) 

もし、一回だけ実行する必要がある場合には、finalize ファンクションを作ってください。

注意: 現在のところ、reduceファンクションから配列(array)を返すことはできません。(オブジェクトか数値を返すのが一般的です)

finalize ファンクション

finalize ファンクションは、reduceの後に走ります。 このファンクションはオプションで、多くのmap/reduce処理では必要ではありません。   finalize ファンクションはキーと値を受け取り、finalizeされた結果を返します。

function finalize(key, value) -> final_value 

shard環境

shardな環境では、map/reduceでの操作はすべてのshardで並行に処理されます。

シェルでの例 1

次の例では、 events コレクションが次の構造のオブジェクトを持つことを仮定しています。

{ time : <time>, user_id : <userid>, type : <type>, ... } 

mapreduceを使い、"sale"というタイプのイベントを1つ以上持つユーザを抽出します。

> m = function() { emit(this.user_id, 1); } 
> r = function(k,vals) { return 1; } 
> res = db.events.mapreduce(m, r, { query : {type:'sale'} }); 
> db[res.result].find().limit(2) 
{ "_id" : 8321073716060 , "value" : 1 } 
{ "_id" : 7921232311289 , "value" : 1 } 

もし、ユーザが何回のeventを経験したかという情報も出力したい場合には、reduceファンクションを以下のように書き換えます。

> r = function(k,vals) { 
... var sum=0; 
... for(var i in vals) sum += vals[i]; 
... return sum; 
... } 

注意。ここで、単純に vals.length を返すことはできません。reduceファンクションは複数回呼ばれることがあるからです。

シェルでの例 2

$ ./mongo 
> db.things.insert( { _id : 1, tags : ['dog', 'cat'] } ); 
> db.things.insert( { _id : 2, tags : ['cat'] } ); 
> db.things.insert( { _id : 3, tags : ['mouse', 'cat', 'dog'] } ); 
> db.things.insert( { _id : 4, tags : [] } ); 

> // map function 
> m = function(){ 
... this.tags.forEach( 
... function(z){ 
... emit( z , { count : 1 } ); 
... } 
... ); 
...}; 

> // reduce function 
> r = function( key , values ){ 
... var total = 0; 
... for ( var i=0; i<values.length; i++ ) 
... total += values[i].count; 
... return { count : total }; 
...}; 

> res = db.things.mapreduce(m,r); 
> res 
{"timeMillis.emit" : 9 , "result" : "mr.things.1254430454.3" , 
"numObjects" : 4 , "timeMillis" : 9 , "errmsg" : "" , "ok" : 0} 

> db[res.result].find() 
{"_id" : "cat" , "value" : {"count" : 3}} 
{"_id" : "dog" , "value" : {"count" : 2}} 
{"_id" : "mouse" , "value" : {"count" : 1}} 

> db[res.result].drop() 

その他の例

永続的なコレクションについての注意

たとえ永続的なコレクション名が指定されたときでも、一時的なコレクション名が処理中に使われます。map/reduce が終わったとき、一時的なコレクションが永続的なコレクション名にリネームされます。このため、一時的な不完全なデータについて心配することなく、同じコレクション名を使い定期的にmap/reduce処理を実行することができます。これは、定期的に統計情報をコレクションに出力するときにとても便利です。


Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

IF YOU HAVE A QUESTION, POST IT TO THE USER GROUP.

These pages are fine for comments, but for questions, your best bet will always be the MongoDB User Group.

blog comments powered by Disqus