MongoDBでの集計処理の概要
一般的なNoSQLプロダクトは、
しかし、
- 1. Aggregationフレームワーク
- SQLでいうGroup By句やSum関数を提供します。Mongo Shellからクエリと同じように実施できます。一部の処理
($groupと$sort) はシャーディングに対応しており、 各シャードで処理します。 - 2. MongoDBのMap/
Reduce機能 - Map関数/Reduce関数を独自に定義し、
集計処理を行います。Aggregationフレームワークではできないような、 複雑な集計処理を行うために使用します。シャーディングに対応していますので、 分散処理を実施することが可能です。 - 3. その他集計処理ミドルとの連携
- より大規模に集計処理を行うため、
他の集計処理ミドルとの連携も可能です。今回の記事では、 Hadoopとの連携を紹介します。
それではさっそくAggregationフレームワークを使って、
Aggregationフレームワーク
データの準備
最初に集計するためのデータを準備しましょう。今回はWebサーバーのアクセスログを想定したデータを用います。下記のサンプルデータをMongoDBへ保存します。
> db.httplogs.insert({"url_path":"/", "status":"200"}); > db.httplogs.insert({"url_path":"/", "status":"200"}); > db.httplogs.insert({"url_path":"/", "status":"500"}); > db.httplogs.insert({"url_path":"/", "status":"500"}); > db.httplogs.insert({"url_path":"/top", "status":"200"}); > db.httplogs.insert({"url_path":"/top", "status":"200"}); > db.httplogs.insert({"url_path":"/top", "status":"404"}); > db.httplogs.insert({"url_path":"/user", "status":"200"}); > db.httplogs.insert({"url_path":"/user", "status":"500"}); > db.httplogs.insert({"url_path":"/user", "status":"500"});
Aggregationフレームワークで集計する
それではAggregationフレームワークを使って集計してみましょう。先ほど保存したアクセスログのデータ使用して、
URLごとのアクセス数の集計をAggregationフレームワークで実施するには、
> db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } } );
実行してみます。
> db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } } ); { "result" : [ { "_id" : "/user", "count" : 3 }, { "_id" : "/top", "count" : 3 }, { "_id" : "/", "count" : 4 } ], "ok" : 1 }
集計できました。
複数キーで集計する
次はURLとHTTPステータスコードの2つをキーとして集計してみましょう。 複数キーで集計するには、
> db.httplogs.aggregate( { $group : { "_id" : { "url_path" : "$url_path", "status" : "$status" }, "count" : { "$sum" : 1 } } } );
実行してみます。
> db.httplogs.aggregate( { $group : { "_id" : { "url_path" : "$url_path", "status" : "$status" }, "count" : { "$sum" : 1 } } } ); { "result" : [ { "_id" : { "url_path" : "/user", "status" : "500" }, "count" : 2 }, { "_id" : { "url_path" : "/user", "status" : "200" }, "count" : 1 }, ...
こちらも集計できました。
Aggregationフレームワークでできること
このように、
他にもAggregationフレームワークには集計に便利なオペレータが用意されており、
たとえば以下のような、
SELECT name as '_id', AVG(score) as 'average' FROM scores
WHERE year = 'junior'
GROUP BY = name
上記のSQLをAggregationフレームワークで表現すると、
db.scores.aggregate(
{ $match : { "year" : "junior" } },
{ $project : { "name" : 1, "score" : 1 } },
{ $group : { "_id" : "$name",
"average" : { "$avg" : "$score" } } }
);
このようにオペレータをフィルタのように使い、 その他のオペレータについては、 公式マニュアルには他にも、 上記のオペレータで可能な範囲は、 それでは、 これまでにAggregationフレームワークで実行した集計を、 Map関数は、 keyにはグルーピングするキーを指定します。先ほどと同様に、 Map関数内部でkeyとvalueを作成するために、 Reduce関数には、 Map関数とReduce関数が作成できたら、 それでは、 URLアクセス数の合計が、 次はURLとHTTPステータスコードの2つをキーとして集計してみましょう。 複数キーで集計するためには、 URLとHTTPステータスコードをキーとするために、 Reduce関数は先ほどのままで良いので、 URLとHTTPステータスコードが集計のキーとなっていることが確認できました。 Map/ それでは最後に、 Hadoopは、 公式マニュアルも充実しており、 MongoDBとHadoopを組み合わせて使用することによって、 今回の記事では、 今回はMongoDBで集計を実施する方法を紹介しました。MongoDBには、 また、 次回はMongoDBの運用について紹介する予定です。お楽しみに!
SQL Aggregationオペレータ WHERE $match GROUP BY $group HAVING $match SELECT $project ORDER BY $sort LIMIT $limit SUM() $sum COUNT() $sum MongoDBのMap/
Map関数の準備
> map = function() {
emit(this.url_path, {count: 1});
}
Reduce関数の準備
> reduce = function(key, values) {
var count = 0;
values.forEach(function(v) {
count += v['count'];
});
return {count: count};
}
Map/
> db.httplogs.mapReduce( map, reduce, {out: {inline:1}} );
{
"results" : [
{
"_id" : "/",
"value" : {
"count" : 4
}
},
{
"_id" : "/top",
"value" : {
"count" : 3
}
},
{
"_id" : "/user",
"value" : {
"count" : 3
}
}
],
"timeMillis" : 1,
"counts" : {
"input" : 10,
"emit" : 10,
"reduce" : 3,
"output" : 3
},
"ok" : 1,
}
複数キーで集計する
> map = function() {
emit({url_path: this.url_path, status: this.status}, {count: 1});
}
> db.httplogs.mapReduce( map, reduce, {out: {inline:1}} );
{
"results" : [
{
"_id" : {
"url_path" : "/",
"status" : "200"
},
"value" : {
"count" : 2
}
},
{
"_id" : {
"url_path" : "/",
"status" : "500"
},
"value" : {
"count" : 2
}
},
...
Map/
その他集計処理ミドル
Hadoopと組み合わせて使う
まとめと次回のテーマ