雑文発散

«前の日記(2015-07-28) 最新 次の日記(2015-07-30)» 編集
過去の日記

2015-07-29 [長年日記]

[Elasticsearch] Elasticsearch 2.0.0 で導入予定の Pipeline Aggregations が便利そうなので自分なりにまとめてみたよ

先日の Elasticsearch 勉強会で Pipeline Aggregations のドキュメントを紹介されてから、数日ちまちまと眺めているのだけど、「これはもしかしてあればいいのになぁと思っていた機能なのでは!?」と思うようになって、ちょっとだけマジメにドキュメントを読み始めた。

実は以前に公開された 2.0.0 の紹介記事でも Pipeline Aggregations に触れられているんだけど、パフォーマンス改善やマイグレーションの話に目がいってしまって、「ふーん」とスルーしてしまっていた(せっかく書かれていたのに申し訳ない)。

改めてその記事を見直すと、Pipeline Aggregations の話が一番上に書いてある。一番上ってことは、一番訴求したい項目であって、一番ウリな機能ってことじゃないか!!!

ちょっと落ち着こう。

Pipeline Aggregations とは「Aggregation で集計された結果をさらに Aggregation できる」というのが超絶ざっくりした解説になる。

最初(というか単にアルファベット順)に説明されているのは、Avg Bucket Aggregation で、こいつが何をできるかというと、こういう表が Elasticsearch のクエリ一発で表せるようになるってことだよね、きっと。この「平均」の部分を Elasticsearch 側で計算してくれる。

売上数 売上額
2015-01 3 550
2015-02 2 60
2015-03 2 375
平均 328.33

縦方向の集計としては、「平均」の他に「最大」「最小」「合計」の計算が可能みたいだ。上の表で言えば、「平均」の部分の代わりに「最大の売上額」「最小の売上額」「売上額の合計」が簡単に出せそうだ。

こいつらを整理するとこんな体系になっている様子。「* Bucket Aggregation」という名前が一連のシリーズだと思えばいいのかな。

こういった「集計後のデータのさらに平均だとか合計だとか」を、Web アプリなどに表示したい場合ってどうしていただろう? わりと「データベースで集計した結果をアプリ側でさらに計算して平均(合計)を出す」ということをやっているのではないだろうか。少なくともオレはそうしている。

もちろん SQL をちょっとがんばれば、この表を一発で返すこともできるだろうけど、アプリでちょいちょいとやってしまった方が楽そうに思う場合が多いので。

でも、例えば Avg Bucket Aggregation のクエリはすごいシンプルに書ける。リファレンスから引用するとこんな感じ。

{
    "aggs" : {
        "sales_per_month" : {
            "date_histogram" : {
                "field" : "date",
                "interval" : "month"
            },
            "aggs": {
                "sales": {
                    "sum": {
                        "field": "price"
                    }
                }
            }
        },
        "avg_monthly_sales": {
            "avg_bucket": {
                "buckets_paths": "sales_per_month>sales"
            }
        }
    }
}

Avg Bucket Aggregation の本体は "avg_monthly_sales" 以下の JSON 部分になる。ちなみに "avg_monthly_sales" というのは、ラベルみたいなものなので好きな名前を付ければ良いところ。本当の本体は "avg_bucket" の部分で、これが「Avg Bucket Aggregation を使え」という指定。さらに "buckets_paths" が、前段の集計のどの結果を使うかの指定。上の例で言えば、「sales_per_month の中の sales という値」を対象にする、という意味になっているのだと思う。

「sales_per_month の中の」を表すのが「>」みたい。これは AGG_SEPARATOR と呼ばれるものらしい。詳細は buckets_path Syntax に書いてあるけど、まだあまり理解していない。

縦方向の集計をしていると、GROUP BY foo HAVING bar <= 50 みたいに集計結果の絞り込みが欲しくなるときがあるけど、それは Bucket Selector Aggregation で実現できるようになりそう。

つまり、次のような全体の集計があるとした場合を考えてみる。

売上数 売上額
2015-01 3 550
2015-02 2 60
2015-03 2 375

Bucket Selector Aggregation を使えば、次のように「売上が 200 未満の集計結果のみを表示」などができる。こういうの待ち望んでいた感じ、あるよね!!

売上数 売上額
2015-02 2 60

さて、縦方向の集計があれば、横方向の集計もあるわけで、Derivative Aggregation では変化量(差分)を集計することができる。つまりこのような表の「前月との差額」を一発で集計できるみたいだ。(あ、でも、こういうの、横方向の集計とは言わないかな。単に横に表を広げるのでそういう言い方をしてしまっている)

売上数 売上額 前月との差額
2015-01 3 550
2015-02 2 60 -490
2015-03 2 375 315

また、Cumulative Sum Aggregation では、このような「累積額」を一発集計できるやつっぽい。

売上数 売上額 累積額
2015-01 3 550 550
2015-02 2 60 610
2015-03 2 375 985

さらに Bucket Script Aggregation を使うと、もっと細かい横方向の拡張ができるようだ。

例えば、こういう表が一発で作れるみたい。「Tシャツが売上に占める割合」の部分を Bucket Script Aggregation で求めることができる。

総売上数 Tシャツの売上数 総売上額 Tシャツの売上額 Tシャツが売上に占める割合
2015-01 3 2 50 10 20%
2015-02 2 1 60 15 25%
2015-03 2 1 40 20 50%

この他にも Moving Average AggregationSerial Differencing Aggregation が Pipeline Aggregations に含まれている。それぞれ、だいたい何をやっているのかは把握できたものの、うまく説明する自信がないので、いまは置いておく。時系列データを分析する場合には使えそうな予感だけはしている。

Elastic 社 CTO の Shay さんが「Elasticsearch は検索がスタートだったけど、集計にも使われることが増えてきたので、そちらにも力を入れ始めている」というような発言をされていた(と思う)んだけど、それはこういうことだったんだなぁと思った。

そしてこういう用途への拡張を意識して「PostgreSQL のようにデータの信頼性を維持できる Elasticsearch にしていきたい」という発言に繋がったのだなぁなどと思ったりもした。

あ、ちなみにここに書いたものは、全てドキュメントから把握してまとめたもの。本当は実際に HEAD の ELasticsearch を動かしつつ試してみようとか思っていたんだけど、なんか手元でビルドがうまくいかずにまだ試せていない。間違っていたところがあったらごめんね。