👏

Lookerの備忘録⑤ Liquid Parameter(リキッドパラメーター)について。ダッシュボード上でユーザーの入力に応じて結果が変化

2024/12/10に公開

前回の続きです!

Lookerでは、ビジュアルなUIから柔軟な分析が可能ですが、複雑なロジックや条件分岐が必要な場合に、標準機能だけでは物足りないことがあります。
そんなときに役立つのが、LookerがサポートするLiquidテンプレート言語です。LiquidパラメーターはLookMLでの動的なSQL生成や、ユーザー入力(パラメーター)に応じたクエリ分岐などを実現します。

本記事では、Liquidを使ったフィルタリングや条件分岐の仕組み、templated_filtersやLiquid変数を活用する方法について解説します。また、当環境ではSnowflakeをDWHとして使用し、extra small(XS)サイズのWHをLooker向けに利用している前提を踏まえ、Liquid利用時のパフォーマンス面での注意点についても触れていきます。

例に漏れず今回も公式ドキュメントのまとめです

Liquidとは?

LiquidはRuby由来のテンプレートエンジンであり、LookerではSQLを動的に生成するための仕組みとして統合されています。
{% if %}{% case %}といったタグを用いて条件分岐を書いたり、{{ _user_attributes['region'] }}などで変数を参照したりすることが可能です。

つまり結果をユーザーの要望によって都度変えたいということです(日にち指定、調べる語句指定、人の指定、order_idの指定など)

基本構文例

  • {{ variable_name }}: 変数の出力
  • {% if condition %} ... {% endif %}: 条件分岐
  • {% case variable %} {% when value %} ... {% endcase %}: 複数パターンの条件分岐

Looker内では、sql:ブロックやderived_table:などでLiquidを用い、動的なSQLフラグメントを生成できます。

Templated Filtersで動的フィルタリング

templated_filtersは、ユーザーがexplore画面で指定したフィルター条件をLookML上のSQLに組み込むための機能です。
以下はユーザー指定のフィルタ条件に応じてWHERE句を動的生成する例です。

例:

view: orders {
  derived_table: {
    sql:
      SELECT
            *
      FROM
            WH.schema..orders
      WHERE
           {% condition orders.created_date %} created_date {% endcondition %}
    ;;
  }

  dimension: created_date {
    type: date
    sql: ${TABLE}.created_date ;;
  }
}

{% condition orders.created_date %}はユーザーがexploreでcreated_dateに指定した条件(例:last 30 days)をSQLのWHERE句条件に展開します。
これにより、explore上でユーザーが指定したフィルター内容が動的にSQLへ反映されます。

例:

view: orders {
  derived_table: {
    sql:
      SELECT
            *
      FROM
            WH.schema..orders
      WHERE
           created_date between {% parameter start_date %} and  {% parameter end_date %}
    ;;
  }

  parameter: start_date {
      type: date
    }

  parameter: end_date {
      type: date
    }
}

{% parameter start_date %} and {% parameter end_date %}はユーザーの指定した日にちの区間内でwhere内で展開してそれによる結果を返します。

Liquid変数とtemplated_filtersの組み合わせ

ユーザーが選択したパラメーター値に応じてWHERE句を切り替える例です。
parameter_value('region_param')によって、ユーザーの入力に応じてSQL条件が変更されます。

parameter: region_param {
  type: string
  allowed_value: { label: "APAC" value: "APAC" }
  allowed_value: { label: "EMEA" value: "EMEA" }
  allowed_value: { label: "AMER" value: "AMER" }
}

view: sales {
  derived_table: {
    sql:
      SELECT
           *
      FROM
           WH.schema..orders
      WHERE 
          {% if parameter_value('region_param') == 'APAC' %}
            region IN ('JP','CN','AU')
          {% elsif parameter_value('region_param') == 'EMEA' %}
            region IN ('DE','FR','UK')
          {% else %}
            region IN ('US','CA','MX')
          {% endif %}
    ;;
  }

  dimension: region {
    type: string
    sql: ${TABLE}.region ;;
  }
}

LiquidとPDT、そしてComputingを最小限にするには

Liquidは条件に合わせて再計算させる構造上、PDTのように扱うことはできません。なのでもしLiquidを大規模データで行う場合はかなり粒度を大きめにしたテーブルをPDTにしておいて、それに対してLiquidで再計算せるのが筋がいいかもしれません。

もしくはそもそものSnowflakeなどのDWH側で物理テーブルとして用意していたり、マテリアライズド(DWH側でキャッシュされてすぐ参照できる状態)されているViewなどを参照させることでこの辺りの再計算も早くなる傾向があります。

結局のところどこまでユーザーに柔軟性を持たせるかになりますが、個人的な見解としては、「極力ユーザーに柔軟性を与えず、基本は決められた範囲内で迅速かつ必要なデータや結果のみをユーザーに提供する」というのがBIツールとしてはベターだと思います。

理由はいくつかありますが、忌憚のない意見で締め括りますと

  • 基本BIツールをみにくるユーザーそこまで高度なことを要求していない
  • 仮に柔軟性の高いことをしたいのであれば別途分析を回した方が早い
  • Looker限らずBIツールの役割は、分析者が依頼を受けてやっていたようなことを全て当事者で確認できるレベル感に落とし込むことでの効率化とデータの共有性の加速

にあると思っているからです。
したがって、Liquidについて説明しましたが、当環境ではあまり使っておりません。

サービスがローンチして本当に可及的に結果を変動させたいような場合にのみ対応してます。
そうやって場合分けすることで、コストの最適化もできますしある程度の柔軟性を提供できるので、小中規模の会社でデータ民主化のようなことをするにはこれで良いかな、というのが肌感です。

次は構想中です!

Discussion