lookml-liquid について
Lookerモデルで動的ロジックを書く人向けの、LookML Liquid変数とタグのリファレンススキルです。変数の用途、条件分岐、ユーザー入力のSQLでの安全な扱い、クエリー内のフィールド状態がロジックや性能に与える影響を説明します。
主な機能
- 01Liquid変数と使用箇所を一覧化
- 02_in_queryと_is_selectedを比較
- 03ユーザー入力のSQLクォートを解説
- 04動的なテーブル選択と結合を提示
- 05フィールド参照の性能への影響を説明
活用シーン
- 01クエリー内のフィールドに応じてテーブルを選択
- 02フィルター値からSQL条件を生成
- 03選択フィールドに応じて計算を変更
- 04リンクフィールドによる余分なグループ化を回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ looker-open-source/ looker-skills --skill lookml-liquidインストール後、Claude Code で/lookml-liquidを入力して読み込まれたか確認します。
詳細
手順
- 構文:
{{ value }}:出力構文(テキストを挿入します)。{% if condition %}:タグ構文(ロジックを記述します)。
- 一般的な変数:
value:DB の生の値(比較に適しています)。rendered_value:書式設定済みの値(表示に適しています)。_filters['view.field']:ユーザーが選択したフィルター値。parameter_name._parameter_value:選択されたパラメーター値。
- ベストプラクティス:
- SQL インジェクション:SQL の文字列リテラルを生成する際、ユーザー入力(
_filtersなど)を挿入する場合は、必ず| sql_quoteを使用してください。 - ブール値:方言でリテラルの
TRUE/FALSEが必要な場合(BigQuery など)は、_in_queryまたは_is_selected変数に| sql_booleanを付けてください(例:{{ view.field._in_query | sql_boolean }})。 - 依存関係の把握:
_in_queryは、SELECT、フィルター、required_fieldsでの使用を確認します。表示列だけが対象ではありません。 - パフォーマンス:フィールドがすでにクエリに含まれていない場合は、
linkパラメーター内で{{ field._value }}を参照しないでください。Looker によってフィールドがGROUP BY句に追加され、結果セットがファンアウトする可能性があります。ブラウザーの結果行から値を取得するだけであれば、代わりにrow['view.field']を使用してください。
- SQL インジェクション:SQL の文字列リテラルを生成する際、ユーザー入力(
高度な変数の使い方
_in_query と _is_selected の違い
| 変数 | 定義 | 重要な違い(合計) |
|---|---|---|
_in_query | フィールドが SELECT 句、フィルター、または required_fields に含まれている場合、true を返します。 | フィールドがクエリに寄与していた場合、合計の計算中も true のままです。 |
_is_selected | フィールドが SELECT 句または required_fields に含まれている場合、true を返します。 | ディメンションは合計の計算のためにクエリから除外されるため、合計の計算中(行/列/総合計)は false を返します。 |
警告
ディメンションのロジックを条件付きでレンダリングするために
_is_selectedを使用すると、合計行ではそのロジックが 機能しません(falseを返します)。合計でもロジックを維持する必要がある場合は_in_queryを使用するか、合計での動作として意図するのであれば、falseの状態を明示的に処理してください。
Liquid 変数の定義
次の表では、LookML で使用できる Liquid 変数とその使用箇所について説明します。
使用箇所のキー:
- A:
action - DV:
default_value(ダッシュボード) - DE:
description - F:
filters(ダッシュボード要素) - H:
html - LA:ラベルパラメータ(
label、view_label、group_label、group_item_label) - LI:
link - S:SQL パラメータ(
sql、sql_on、sql_table_name)
| 変数 | 定義 | 使用箇所 |
|---|---|---|
value | フィールドの未加工の値です。 | A, H, LI |
rendered_value | フィールドの書式設定済みの値です。 | A, H, LI |
filterable_value | URL フィルタリング用に書式設定された値です。 | A, H, LI |
link | デフォルトのドリルリンク URL です。 | A, H, LI, S |
linked_value | デフォルトの書式設定とリンクが適用された値です。 | A, H, LI |
_filters['view.field'] | フィールドに適用されたユーザーフィルタです。 | A, DE, H, LA, LI |
{% date_start filter %} | 日付フィルタの開始日です。 | S |
{% date_end filter %} | 日付フィルタの終了日です。 | S |
{% condition filter %} sql {% endcondition %} | SQL にフィルタロジックを適用します。 | S |
{% parameter name %} | パラメータの値です。 | DE, LA, S |
name._parameter_value | パラメータの値を挿入します(ロジックに安全に使用できます)。 | DE, H, LA, LI, S |
_user_attributes['name'] | ユーザー属性の値です。 | A, DE, H, LA, LI, S, DV, F |
_model._name | モデル名です。 | A, DE, H, LA, LI, S |
_view._name | ビュー名です。 | A, DE, H, LA, LI, S |
_explore._name | Explore 名です。 | A, DE, H, LA, LI, S |
_field._name | フィールド名です。 | A, DE, H, LA, LI, S |
view._in_query | ビューのフィールドが 1 つでも クエリされている場合は true です。 | DE, LA, LI, S |
view.field._in_query | フィールドがクエリまたはフィルタに含まれている場合は true です。 | DE, LA, LI, S |
view.field._is_selected | フィールドが SELECT に含まれている場合は true です。 | DE, LA, LI, S |
view.field._is_filtered | フィールドがフィルタされている場合は true です。 | DE, LA, LI, S |
高度なユースケース
1. 集計認識(動的なテーブル選択)
ユーザーが詳細なデータを要求していない場合に、_in_query を使ってクエリをより小さな事前集計テーブルに振り分けます。これにより、クエリのパフォーマンスが大幅に向上します。
view: orders {
sql_table_name:
{% if orders.created_date._in_query or orders.created_hour._in_query %}
orders_daily_summary -- Fallback to daily partition if granular date used
{% elsif orders.created_month._in_query %}
orders_monthly_summary -- Use monthly summary for high-level queries
{% else %}
orders_all_transactions -- Default/Detail table
{% endif %} ;;
}
2. 動的な結合(sql_on)
結合で _in_query を使うと、ユーザーが選択した項目に実際に必要な場合を除き、負荷の高いテーブルの結合を避けられます。
explore: order_items {
join: users {
type: left_outer
sql_on: ${order_items.user_id} = ${users.id} ;;
relationship: many_to_one
}
join: user_facts {
type: left_outer
sql_on: ${users.id} = ${user_facts.user_id} ;;
relationship: one_to_one
# Only join user_facts if a field from it is actually selected/filtered
sql_where: {% if user_facts._in_query %} 1=1 {% else %} 1=0 {% endif %} ;;
}
}
注:一部の方言では、結合を解除させる方法の 1 つとして sql_where の手法がありますが、サポートされている場合は、{% if %} を使った標準的な sql_on ロジックのほうがすっきりします。
3. 列固有のロジック(動的な分母)
クエリにほかのフィールドが含まれているかどうかに応じて、計算を変更します。
measure: dynamic_rate {
type: number
sql:
{% if users.traffic_source._is_selected %}
${total_revenue} / NULLIF(${traffic_source_count}, 0)
{% else %}
${total_revenue} / NULLIF(${total_users}, 0)
{% endif %} ;;
}
例
動的 HTML(条件付き書式設定)
dimension: status {
html:
{% if value == 'complete' %}
<span style="color: green">{{ rendered_value }}</span>
{% else %}
<span style="color: red">{{ rendered_value }}</span>
{% endif %} ;;
}
テンプレート化されたフィルタ(派生テーブル)
view: customer_facts {
derived_table: {
sql:
SELECT customer_id, SUM(amount)
FROM orders
WHERE {% condition order_date %} created_at {% endcondition %}
GROUP BY 1 ;;
}
}
複雑なロジック(ループと分割)
LookML の Liquid は文字列操作やループを処理できるため、複雑なフィルタパラメータの解析や値のアンネストに役立ちます。使用頻度は低いものの、複雑なモデリングタスクでは非常に便利です。
view: brand_category_item {
parameter: filter { type: unquoted }
}
explore: complex_filter_parsing {
# Example: Parsing a string like "Brand1..Category1__Brand2..Category2"
# This logic splits the string by '__' then '..' to generate OR conditions
sql_where:
{% assign items = brand_category_item.filter._parameter_value | split: '__' %}
{% for item in items %}
{% assign parts = item | split: '..' %}
{% if forloop.first %} ( {% else %} OR ( {% endif %}
${products.brand} = '{{ parts[0] }}' AND ${products.category} = '{{ parts[1] }}'
)
{% endfor %}
{% if items.size == 0 %} 1=1 {% endif %}
;;
}