mongodbを使ってみる (7) - MapReduce -

2013-01-17T00:00:00+00:00 JavaScript MongoDB

MongoDBにもれなく付いてくる(笑)MapReduceを使ってみる。ただ使っても面白くないので

  • fluentdを使ってnginxのアクセスログをtailする
  • fluentdでパースされたアクセスログをfluent-plugin-mongoを使ってMongoDBにプッシュする
  • MongoDB MapReduce APIを使って、アクセスカウント統計を作る

という流れで

fluentd側のセットアップ

sudo /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-mongo

でとりあえずfluent-plugin-mongoを入れる。んで

<source>
    type tail
    format apache
    path /var/log/nginx/access.log
    tag nginx.accesslog
</source>

<match nginx.accesslog>
    type mongo
    database fluent
    collection accesslog
</match>

なtd-agent.confを作って

/usr/lib/fluent/ruby/bin/fluentd -c td-agent.conf

で起動。fluent-plugin-mongoな詳解は後日やろうかと

MongoDB側を確認

nginxなWebサーバーにアクセスした後にMongoDB側を見ると

{ "_id" : ObjectId("50f664408cdabd1739000001"), "path" : "/" }
{ "_id" : ObjectId("50f664408cdabd1739000002"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd1739000004"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd1739000005"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd1739000007"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd1739000008"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd1739000009"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd173900000a"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd173900000b"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd173900000c"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd173900000d"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd173900000e"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd173900000f"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd1739000010"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f6647c8cdabd1739000011"), "path" : "/" }
{ "_id" : ObjectId("50f6647c8cdabd1739000012"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f664ba8cdabd1739000013"), "path" : "/" }
{ "_id" : ObjectId("50f664ba8cdabd1739000014"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712338cdabd1739000015"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712338cdabd1739000016"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712338cdabd1739000018"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712338cdabd1739000019"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712708cdabd173900001b"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712708cdabd173900001c"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712708cdabd173900001d"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712708cdabd173900001e"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712708cdabd1739000020"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712708cdabd1739000021"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712708cdabd1739000022"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712708cdabd1739000023"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f712708cdabd1739000024"), "path" : "/home/index" }
{ "_id" : ObjectId("50f712708cdabd1739000025"), "path" : "/favicon.ico" }

という風にずどどどどとぶっこまれるのが確認できる。ただこれフィルターしているので、あれなんすけど

{ "_id" : ObjectId("50f664408cdabd1739000001"), "path" : "/" }
{ "_id" : ObjectId("50f664408cdabd1739000002"), "path" : "/favicon.ico" }
{ "_id" : ObjectId("50f664408cdabd1739000003") }

という風にpath属性が無いのもあるので

MongoDBでMapReduce APIを使うJavaScriptを書く

var res = db.accesslog.mapReduce(
  // Map
  function() {
    // fluentからmongoへプッシュされたログの行毎のデータはthisからアクセス出来る模様
    emit(this.path, { count: 1 });
  },
  // Reduce
  function(key, values) {
    var result = { count: 0 };

    values.forEach(function(value) {
      result.count += value.count;
    });

    return result;
  },
  {
    // mapReduceをする際のクエリーを指定する(オプショナル)
    query: { path: { $ne: null }},
    // 処理後に呼ばれる
    finalize: function(key, value) {
      value.path = key;

      return value;
    },
    out: { inline: 1 } // 出力はインライン。mapReduceでリターンされた値で参照できる
  }
);

printjson(res)

な感じで実行すると

{
    "results" : [
        {
            "_id" : "/",
            "value" : {
                "count" : 9,
                "path" : "/"
            }
        },
        {
            "_id" : "/favicon.ico",
            "value" : {
                "count" : 16,
                "path" : "/favicon.ico"
            }
        },
        {
            "_id" : "/home/index",
            "value" : {
                "count" : 7,
                "path" : "/home/index"
            }
        }
    ],
    "timeMillis" : 15,
    "counts" : {
        "input" : 32,
        "emit" : 32,
        "reduce" : 3,
        "output" : 3
    },
    "ok" : 1,
}

な感じで出力される。で結果を取得せずにコレクションとかにぶちこんでくれっていう場合にはoutオプションを変えてやれば良い。それが以下な感じ(inlineは除外する)

コレクション名

{ out: "コレクション名" }

で指定すると指定したコレクション名にぶち込まれる

mergeとreplace

  • 既存するデータをぶち消して、新しい結果をぶち込むのがreplace
  • 既存するデータを消さずにそのまま新しい結果をマージするのがmerge

な感じなんじゃないかなーっと

以上な感じでMongoDBにあるMapReduce APIを使ってごにょごにょする事も出来る模様で

MongoDB(Perlクライアント)でtailable Cursorを使う mongodbを使ってみる (6) - findAndModify -