[Looker]Aggregate Awarenessという一見とっつきにくそうな機能を実際に使ってみながら理解する

目覚めよ新たな集計たち…
2020.08.31

大阪オフィス所属だが現在は奈良県でフルリモートワークしている玉井です。

今回は、Lookerのバージョン7.8から出たAggregate Awarenessという機能をご紹介します。

公式情報

Aggregate Awarenessとは?

ざっくり一言でいうと、クエリに応じて参照するテーブルを動的に変えてくれる機能です。

…上記の一言だけだと誤解する可能性があるので、もう少しだけ補足すると、異なる時間(日付)毎に集計された複数のテーブルを作成し、分析時(Explore使用時)に使用された時間(日付)に合わせて、参照するテーブルを変えることができる…という感じです。

何のために使うのか?

Lookerに限らず、他のBIツールでもよくあることなのですが、開発したダッシュボードのパフォーマンスが遅い場合、原因としてよくあるのが「ダッシュボード側でデータを別の時間軸に集計し直してる」というものです。例えば、データを日単位として持っているテーブルがあったとき(1レコード=1日)、ダッシュボード側でそれを月単位や年単位に集計している感じです。データが多ければ多いほど、集計に時間を要してしまい、結果的にダッシュボードのパフォーマンスが低下してしまうことになります(もちろんDB側の事情もありますが)。

そういう時の解決策としてメジャーなのが「DB側で予め集計済のテーブルを用意しておく」です。月単位の集計が必要なのであれば、予め月単位に集計済のテーブルを用意しておき、ダッシュボードはそれを参照するだけにします。こうすれば、ダッシュボード側で集計処理をする必要がなくなるため、パフォーマンスの低下を防ぐことができます。

しかし、この方法には2つのデメリットがあります。

  • 集計がテーブル毎に固定されてしまう
    • 月単位のテーブルを参照している場合、月単位以外の集計がすぐに見れない
  • そもそもDB側にテーブルを用意するのが面倒
    • ダッシュボード側の要件が変われば、DB側のそれに追随する必要がある

これらのデメリットを回避しつつ、集計済テーブルを効率よく使うための機能がAggregate Awarenessです。

手法としては以前からあったもの

Aggregate Awarenessは、バージョン7.8で実装されたものですが、実は考え方(手法)としては、以前よりあったものになります。「クエリに使用されている時間系のdimentionに応じて、参照テーブルを変える」っていうのをLiquidで実装する…という手法が以前よりありました。

手法(概念)としてのAggregate Awareness

公式情報

実装の考え方

viewファイルにはsql_table_nameというパラメータがあり、ここには「viewファイルとして定義する実際のテーブル」を記述する…っていうのはLookMLを書いたことのある方ならわかると思います。分析(Explore)はexploreで色々なviewファイルをJOINして構成しますが、とどのつまり参照するテーブルはviewファイルで定義しているものになります。であれば、このsql_table_nameを、分析時に使われている項目に応じて動的に変わるようにする…という考え方になります。

サンプル

上記の公式ドキュメントに掲載されているサンプルコードを紹介します。

view: orders {
 sql_table_name:
 {% if orders.created_date._in_query %}
 orders
 {% elsif orders.created_week._in_query %}
 orders_smry_week
 {% elsif orders.created_month._in_query %}
 orders_smry_month
 {% else %}
 orders_smry_year
 {% endif %} ;;
dimension_group: created {
 type: time
 timeframes: [date, week, month, year]
 sql: ${TABLE}.created_at ;;
 }

Liquidで使えるパラメータに_in_queryというものがあります。読んで字の如く「このdimension(またはmeasure)がクエリに使われていたら」というロジックを意味します。つまり、Exploreで使用するdimension等に応じた動作をLookMLに持たせることができます

dimension_groupcreatedが日付(date)として使われたら、sql_table_nameordersとなる、createdが週(week)として使われたらsql_table_nameorders_smry_weekとなる、createdが月(month)だったら、参照するテーブルはorders_smry_monthとなる…という感じで、dimension_groupcreatedの単位に応じて、viewファイルの参照テーブルを動的に変化させることができます(SQLでいうと、FROM句が動的に変わります)。

解消できるデメリットは1つだけ

記事の冒頭で、「集計済テーブルを事前に用意する方法」に対して、デメリットが2つあるということを書きました。

Aggregate Awareness(手法)を実装することで、分析の時間軸に応じて動的にテーブルが変わるため、「集計がテーブル毎に固定されてしまう」というデメリットは回避することができます。

しかし、もうひとつのデメリットである「そもそもDB側にテーブルを用意するのが面倒」は(この手法では)回避できません。この手法を実装するためには、DB側で予め複数の集計済テーブルを用意する必要があります(そして、そのテーブルを動的に使い分けることになる)。

そこで、今回の「機能としてのAggregate Awareness」が登場します。

機能としてのAggregate Awareness(v7.8〜)をやってみた

早速、実際にやってみましょう。

事前準備

PDT(永続派生テーブル)を有効にしておく必要があります。理由はあとでわかります。

普通にExploreで分析をする

例えば当ブログの昨年のSNSに関する数値を見たいとします。

上記は日別の結果ですが、ちょっとわかりづいらいので、月単位に集計しなおして見たいとします。dimension_groupの投稿日をMonthに変えます。

ここまでは普通の操作になります。このデータは「1レコード=1post(1記事)」なので、それを月単位に集計すると、そこそこ時間がかかります。というわけで、早速Aggregate Awarenessを使っていきましょう。

ExploreからAggregate Awareness用のLookMLを生成する

Aggregate AwarenessはLookMLを書くのですが、イチから書かずとも、Exploreから生成することができます。右上の歯車アイコンからGet LookMLを選びます。

すると、Aggregate AwarenessのLookMLが表示されるので、これをコピって使います。お気づきの方もいるかもしれませんが、これはネイティブ派生テーブルを作成するときと同じ流れとなっています。

modelファイルにAggregate Awareness用のLookMLを書く(ペーストする)

生成したLookMLですが、exploreなので、modelファイルに貼り付けます。

先程Exploreで設定した項目がそのまま記述されていますね。ちなみに、このexploreは、既存exploreに対してRefimentsとして追記される形になっています。Refimentsについては下記をどうぞ。

また、datagroup_triggerも自動で記述されていますが、なぜdatagroup_triggerがあるのかについては、後で説明します。

Aggregate Awareness用のExploreを使ってみる

前述した通り、Aggregate Awarenessは元々のexploreにRefimentsで追記されているので、使用するExploreは先程と同じです。

まずはdate単位で集計したときのクエリです。

クエリの内容自体は変わらないのですが、注目すべきなのは下記のコメントです。

-- Did not use t_blogposts::rollup__post_month; it does not include the following fields in the query: t_blogposts.post_date

記述しているAggregate Awarenessの条件に該当しないから普通のクエリになっていますよ的なコメントが出るようになりました。

で、次は月単位のクエリをやってみます。

まず下記コメントが出ました。

-- use existing t_blogposts::rolluppost_month in cmdevio.LR$WNGR61598848078538_t_blogposts$rolluppost_month`

そしてFROM句を見ると、LR$WNGR61598848078538_t_blogposts$rollup__post_monthというテーブルを参照しています。名前からわかる通り、これは人間が用意したテーブルではなく、Lookerが生成したテーブルです。

Aggregate Awarenessは集計済テーブルも自動で作ってくれる

そう、Aggregate AwarenessはLooker側が集計済テーブルを作ってくれます。そして、この動作は永続派生テーブル(PDT)の機能が使われます。だから、最初にPDTを有効にしておく必要がありました。

そして、実際に作られたテーブルである以上、データの更新に応じて、定期的な再作成が必要…これもPDTをと同じですね。だから、Aggregate Awarenessの記述にはdatagroup_triggerがあります。集計済テーブルの更新タイミングもdatagroupを使用することができます。

機能としてのAggregate Awarenessが発動する条件

ここまでやってみると「aggregate_tableに書かれている条件に該当すればどんなテーブルでも作成できるのか?」みたいな疑問が出てきますが、公式ドキュメントには、Aggregate Awarenessが発動する条件が明確に書かれています。

  • Exploreで行う分析が集計済テーブルから実際に算出できる集計である
    • 例えば年単位に集計してあるテーブルから日単位の分析はできない
  • Exploreでdimentionを使う場合、下記の条件を満たす
    • 集計済テーブルにあるdimensionのサブセットである
  • Exploreでmeasureを使う場合、下記の条件を満たす
    • sum, count, average, min, maxのいずれかである(加法)
    • 集計済テーブルのクエリと全く同じ場合は、他タイプのmeasure(非加法)を使用可
  • Exploreでフィルタを使う場合、下記のいずれかの条件を満たす
    • 集計済テーブルに存在するdimensionをフィルタに使う
    • aggregate_tableに記述されているフィルタと一致する

仕様がちょっとややこしいですが、Aggregate Awarenessの基本的な考え…「粒度の異なる集計済テーブルを予め用意し、分析に応じて参照テーブルを動的に変える」がわかっていれば、それに該当しないようなテーブルは作成できない…っていうのが身体で分かると思います。

おわりに

とりあえず使ってみたという感じですが、aggregate_tableに記述できるパラメータが他にもたくさんあるっぽいので、使いこなすのにはそれなりにの経験値が求められる気がしました。