MongoDB Map Reduce
Map-Reduceは計算モデルであり、簡単に言うと、大量の作業(データ)を分解(MAP)して実行し、その後、結果を最終結果(REDUCE)にマージするものです。
MongoDBが提供するMap-Reduceは非常に柔軟で、大規模なデータ分析にもかなり実用的です。
MapReduce コマンド
以下はMapReduceの基本構文です:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number
}
)
MapReduceを使用するには、Map関数とReduce関数の2つの関数を実装する必要があります。Map関数はemit(key, value)を呼び出し、collection内のすべてのレコードを走査して、keyとvalueをReduce関数に渡して処理します。
Map関数はemit(key, value)を呼び出してキーと値のペアを返す必要があります。
パラメータの説明:
- map:マッピング関数(キーと値のペアのシーケンスを生成し、reduce関数のパラメータとします)。
- reduce統計関数。reduce関数のタスクは、key-valuesをkey-valueに変換すること、つまりvalues配列を単一の値valueに変換することです。
- out統計結果を格納するコレクション(指定しない場合は一時コレクションが使用され、クライアントの切断後に自動的に削除されます)。
- queryフィルタ条件。条件を満たすドキュメントのみがmap関数を呼び出します(query、limit、sortは自由に組み合わせられます)。
- sortlimitと組み合わせるsortソートパラメータ(map関数に送信する前にドキュメントをソートします)。グループ化の仕組みを最適化できます。
- limitmap関数に送信されるドキュメント数の上限(limitがない場合、sortを単独で使用してもあまり役に立ちません)。
以下の例では、コレクションorders内でstatus:"A"のデータを検索し、cust_idに基づいてグループ化し、amountの合計を計算します。
MapReduce を使用
次のドキュメント構造は、ユーザーの記事を格納するものです。ドキュメントにはユーザーのuser_nameと記事のstatusフィールドが格納されています:
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "mark",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "サンプル",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "サンプル",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "サンプル,最全的技术文档。",
"user_name": "サンプル",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
次に、postsコレクションでmapReduce関数を使用して、公開済みの記事(status:"active")を選択し、user_nameでグループ化して、各ユーザーの記事数を計算します:
>db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"post_total"
}
)
上記のmapReduceの出力結果は次のとおりです:
{
"result" : "post_total",
"timeMillis" : 23,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 1,
"output" : 2
},
"ok" : 1
}
結果は、クエリ条件(status:"active")を満たすドキュメントが合計5件あり、map関数で5つのキーと値のペアのドキュメントが生成され、最後にreduce関数で同じキーと値が2つのグループに分けられたことを示しています。
具体的なパラメータの説明:
- result:結果を格納するcollectionの名前。これは一時コレクションであり、MapReduceの接続が閉じられると自動的に削除されます。
- timeMillis:実行にかかった時間(ミリ秒単位)
- input:条件を満たしてmap関数に送信されたドキュメントの数
- emit:map関数内でemitが呼び出された回数、つまりすべてのコレクション内のデータ総量
- output:結果コレクション内のドキュメントの数(countはデバッグに非常に役立ちます)
- ok:成功したかどうか。成功の場合は1
- err:失敗した場合、ここに失敗の理由がありますが、経験上、理由は曖昧で、あまり役に立ちません。
find演算子を使用してmapReduceのクエリ結果を表示します:
> var map=function() { emit(this.user_name,1); }
> var reduce=function(key, values) {return Array.sum(values)}
> var options={query:{status:"active"},out:"post_total"}
> db.posts.mapReduce(map,reduce,options)
{ "result" : "post_total", "ok" : 1 }
> db.post_total.find();
上記のクエリは次の結果を表示します:
{ "_id" : "mark", "value" : 4 }
{ "_id" : "サンプル", "value" : 1 }
同様の方法で、MapReduceを使用して大規模で複雑な集計クエリを構築できます。
Map関数とReduce関数はJavaScriptを使用して実装できるため、MapReduceの使用は非常に柔軟で強力です。
その他の拡張