MongoDB : サルでもわかるMapReduceその2:ログの時間別集計を行う
RDBMSでは下記のようなSQLになると思います。(PostgreSQLでの例)
SELECT
to_char(timestamp, 'yyyy-mm-dd HH24'::text) AS ymdh ,count(*)
FROM accesslog
WHERE '2011-12-01' <= timestamp AND timestamp < '2011-12-02'
GROUP BY ymdh
ORDER BY ymdh
;
MapReduceを実行する
// 日本標準時を扱うためのユーティリティ関数
var JSTDate = function (str) { return ISODate(str + "T00+09:00"); };
// 日付範囲指定で対象を絞る。
// このように一時変数に記憶させておくと便利。
var query = { "timestamp" : { "$gte" : JSTDate("2011-11-01"), "$lt" : JSTDate("2011-11-02") } };
// map関数を定義。
var m = function () {
var getYMDH = function (d) {
d.setSeconds(0);
d.setMilliseconds(0);
d.setMinutes(0);
yy = d.getFullYear();
mm = d.getMonth() + 1;
dd = d.getDate();
hh = d.getHours();
if (mm < 10) { mm = "0" + mm; }
if (dd < 10) { dd = "0" + dd; }
if (hh < 10) { hh = "0" + hh; }
return yy + '-' + mm + '-' + dd + ' ' + hh + ':00:00';
};
emit(getYMDH(this.timestamp), {count:1});
};
// reduce関数を定義。
var r = function(key,values) {
var result = { count: 0};
values.forEach(function(value){
result.count += value.count;
});
return result;
};
// mapReduceを実行。
// 結果は'myresults'というコレクションに保存されます。
db.accesslog.mapReduce(m,r, { query : query , out : 'myresults' } );
// 結果をみる
db.myresults.find();
{ "_id" : "2011-11-01 00:00:00", "value" : { "count" : 7546 } }
{ "_id" : "2011-11-01 01:00:00", "value" : { "count" : 2885 } }
{ "_id" : "2011-11-01 02:00:00", "value" : { "count" : 823 } }
{ "_id" : "2011-11-01 03:00:00", "value" : { "count" : 823 } }
{ "_id" : "2011-11-01 04:00:00", "value" : { "count" : 590 } }
{ "_id" : "2011-11-01 05:00:00", "value" : { "count" : 579 } }
{ "_id" : "2011-11-01 06:00:00", "value" : { "count" : 1365 } }
{ "_id" : "2011-11-01 07:00:00", "value" : { "count" : 3230 } }
{ "_id" : "2011-11-01 08:00:00", "value" : { "count" : 4344 } }
{ "_id" : "2011-11-01 09:00:00", "value" : { "count" : 5754 } }
{ "_id" : "2011-11-01 10:00:00", "value" : { "count" : 8066 } }
{ "_id" : "2011-11-01 11:00:00", "value" : { "count" : 9099 } }
{ "_id" : "2011-11-01 12:00:00", "value" : { "count" : 12266 } }
{ "_id" : "2011-11-01 13:00:00", "value" : { "count" : 12504 } }
{ "_id" : "2011-11-01 14:00:00", "value" : { "count" : 7752 } }
{ "_id" : "2011-11-01 15:00:00", "value" : { "count" : 7876 } }
{ "_id" : "2011-11-01 16:00:00", "value" : { "count" : 6843 } }
{ "_id" : "2011-11-01 17:00:00", "value" : { "count" : 6197 } }
{ "_id" : "2011-11-01 18:00:00", "value" : { "count" : 5069 } }
{ "_id" : "2011-11-01 19:00:00", "value" : { "count" : 8490 } }
has more
it
{ "_id" : "2011-11-01 20:00:00", "value" : { "count" : 10966 } }
{ "_id" : "2011-11-01 21:00:00", "value" : { "count" : 17013 } }
{ "_id" : "2011-11-01 22:00:00", "value" : { "count" : 18394 } }
{ "_id" : "2011-11-01 23:00:00", "value" : { "count" : 15914 } }
ざっとこんな感じです。追記
もうちょっと簡単に書けることがわかりました。今回のような単純な件数カウント処理の場合は、map/reduceのvalueをただの数値にしてもよいでしょう。
さらに、Array.sum()という組み込みメソッドを使えばreduceは1行でいけます。
var m = function () {
var getYMDH = function (d) {
d.setSeconds(0);
d.setMilliseconds(0);
d.setMinutes(0);
yy = d.getFullYear();
mm = d.getMonth() + 1;
dd = d.getDate();
hh = d.getHours();
if (mm < 10) { mm = "0" + mm; }
if (dd < 10) { dd = "0" + dd; }
if (hh < 10) { hh = "0" + hh; }
return yy + '-' + mm + '-' + dd + ' ' + hh + ':00:00';
};
emit(getYMDH(this.timestamp), 1);
};
var r = function(key,values) {
return Array.sum(values);
};
db.accesslog.mapReduce(m,r, { query : query , out : 'myresults' } );
db.myresults.find();
{ "_id" : "2011-11-01 00:00:00", "value" : 7546 }
{ "_id" : "2011-11-01 01:00:00", "value" : 2885 }
{ "_id" : "2011-11-01 02:00:00", "value" : 823 }
{ "_id" : "2011-11-01 03:00:00", "value" : 823 }
{ "_id" : "2011-11-01 04:00:00", "value" : 590 }
{ "_id" : "2011-11-01 05:00:00", "value" : 579 }
{ "_id" : "2011-11-01 06:00:00", "value" : 1365 }
{ "_id" : "2011-11-01 07:00:00", "value" : 3230 }
{ "_id" : "2011-11-01 08:00:00", "value" : 4344 }
カテゴリ:
MongoDB