--- name: lookml-liquid description: Use this skill to use Liquid variables in LookML for dynamic SQL, HTML, and Links, including advanced patterns for query optimization. license: Apache-2.0 metadata: publisher: google version: v1 --- # Instructions 1. **Syntax**: * `{{ value }}`: Output syntax (inserts text). * `{% if condition %}`: Tag syntax (logic). 2. **Common Variables**: * `value`: Raw value from DB (best for comparisons). * `rendered_value`: Formatted value (best for display). * `_filters['view.field']`: User-selected filter values. * `parameter_name._parameter_value`: Selected parameter value. 3. **Best Practices**: * **SQL Injection**: Always use `| sql_quote` when inserting user input (like `_filters`) into SQL that generates string literals. * **Booleans**: If your dialect requires literal `TRUE`/`FALSE` (like BigQuery), append `| sql_boolean` to `_in_query` or `_is_selected` variables (e.g., `{{ view.field._in_query | sql_boolean }}`). * **Dependency Awareness**: Remember that `_in_query` checks for usage in **SELECT, Filters, and `required_fields`**. It is NOT limited to just the visible columns. * **Performance**: Avoid referencing `{{ field._value }}` in `link` parameters if the field isn't already in the query, as this forces Looker to add the field to the `GROUP BY` clause, potentially fan-outing the result set. Use `row['view.field']` instead if you only need the value from the browser result row. # Advanced Variable Usage ## `_in_query` vs `_is_selected` | Variable | Definition | Critical Difference (Totals) | | :--- | :--- | :--- | | `_in_query` | Returns `true` if the field is in the SELECT clause, Filters, or `required_fields`. | **Remains `true`** during totals calculation if the field contributed to the query. | | `_is_selected` | Returns `true` if the field is in the SELECT clause or `required_fields`. | **Returns `false`** during totals calculation (Row/Column/Grand Totals) for dimensions, because dimensions are removed from the query to calculate totals. | > [!WARNING] > If you use `_is_selected` to conditionally render logic for a dimension, that logic will **fail** (return false) in the Totals row. Use `_in_query` if you need the logic to persist in totals, or explicitly handle the `false` state for totals if that is the desired behavior. # Liquid Variable Definitions The following table describes the Liquid variables that you can use with LookML and where they can be used. **Usage Key:** * **A**: `action` * **DV**: `default_value` (dashboards) * **DE**: `description` * **F**: `filters` (dashboard elements) * **H**: `html` * **LA**: Label parameters (`label`, `view_label`, `group_label`, `group_item_label`) * **LI**: `link` * **S**: SQL parameters (`sql`, `sql_on`, `sql_table_name`) | Variable | Definition | Usage | | :--- | :--- | :--- | | `value` | The raw value of the field. | A, H, LI | | `rendered_value` | The formatted value of the field. | A, H, LI | | `filterable_value` | The value formatted for URL filtering. | A, H, LI | | `link` | The default drill link URL. | A, H, LI, S | | `linked_value` | The value with default formatting and linking. | A, H, LI | | `_filters['view.field']` | User filters applied to the field. | A, DE, H, LA, LI | | `{% date_start filter %}` | Start date of a date filter. | S | | `{% date_end filter %}` | End date of a date filter. | S | | `{% condition filter %} sql {% endcondition %}` | Applies filter logic to SQL. | S | | `{% parameter name %}` | Value of a parameter. | DE, LA, S | | `name._parameter_value` | Injects parameter value (safe for logic). | DE, H, LA, LI, S | | `_user_attributes['name']` | User attribute value. | A, DE, H, LA, LI, S, DV, F | | `_model._name` | Model name. | A, DE, H, LA, LI, S | | `_view._name` | View name. | A, DE, H, LA, LI, S | | `_explore._name` | Explore name. | A, DE, H, LA, LI, S | | `_field._name` | Field name. | A, DE, H, LA, LI, S | | `view._in_query` | `true` if *any* field from view is queried. | DE, LA, LI, S | | `view.field._in_query` | `true` if field is in query/filter. | DE, LA, LI, S | | `view.field._is_selected` | `true` if field is in SELECT. | DE, LA, LI, S | | `view.field._is_filtered` | `true` if field is filtered. | DE, LA, LI, S | # Advanced Use Cases ## 1. Aggregate Awareness (Dynamic Table Selection) Use `_in_query` to route queries to smaller, pre-aggregated tables when the user doesn't request granular details. This significantly improves query performance. ```lookml 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. Dynamic Joins (`sql_on`) Use `_in_query` in joins to avoid joining heavy tables unless they are actually required by the user's selection. ```lookml 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 %} ;; } } ``` *Note: The `sql_where` trick is one way to force a join drop in some dialects, but standard `sql_on` logic with `{% if %}` is cleaner if supported.* ## 3. Column-Specific Logic (Dynamic Denominator) Change a calculation based on what other fields are present in the query. ```lookml 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 %} ;; } ``` # Examples ## Dynamic HTML (Conditional Formatting) ```lookml dimension: status { html: {% if value == 'complete' %} {{ rendered_value }} {% else %} {{ rendered_value }} {% endif %} ;; } ``` ## Templated Filters (Derived Table) ```lookml view: customer_facts { derived_table: { sql: SELECT customer_id, SUM(amount) FROM orders WHERE {% condition order_date %} created_at {% endcondition %} GROUP BY 1 ;; } } ``` ## Complex Logic (Loops & Split) LookML Liquid can handle string manipulation and loops, which is useful for parsing complex filter parameters or unnesting values. This is used infrequently, but very handy in complex modeling tasks. ```lookml 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 %} ;; } ```