Query introspection lets a dimension or metric branch its SQL based on what’s in the current query — which fields the user selected, and which fields they filtered on. The same definition can produce a differentDocumentation Index
Fetch the complete documentation index at: https://lightdash-liquid-syntax.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
SELECT clause for every query.
This is useful for “smart” metrics that adapt to the question being asked. For example, a total_revenue metric that defaults to completed orders only — but stops applying that filter the moment the user filters status themselves.
What you can check
Inside asql: block, two new Liquid checks are available:
| Check | Returns true when… |
|---|---|
{% if ld.query.fields contains "table.field" %} | The named field is selected or grouped in the current query |
{% if ld.query.filters contains "table.field" %} | A filter is applied to the named field in the current query |
{table_name}.{field_name} — same identifier you’d see in the URL of an explore. The check covers both dimensions and metrics, so ld.query.fields contains "orders.total_order_amount" works for a metric being selected.
You can use either ld.query or the longer lightdash.query — they’re aliases.
These are Liquid template tags (
{% %}), not the Lightdash parameter substitution syntax (${...}). They’re evaluated server-side when a query runs, before the SQL is sent to your warehouse.Where you can use it
Query introspection works in these places:- Dimension SQL in your dbt YAML
- Metric SQL in your dbt YAML
- Additional dimensions in your dbt YAML
- Custom dimensions created via the UI (custom fields)
Wrapping in {% raw %} for dbt YAML
dbt’s Jinja engine also uses {% %} delimiters, and it will choke on Liquid-specific tags like {% elsif %}. You must wrap any Liquid block in {% raw %} ... {% endraw %} so dbt passes it through untouched. Lightdash then evaluates the unwrapped Liquid at query time.
When you create a custom dimension via the UI, no
{% raw %} wrapper is needed — the SQL goes straight to Lightdash without passing through dbt.Examples
Smart-default filter (filter-aware metric)
A revenue metric that defaults to “completed orders only” — but if the user filters on status themselves, the metric respects their filter instead.- dbt v1.9 and earlier
- dbt v1.10+
- Lightdash YAML
| The user’s query | SQL Lightdash sends to the warehouse |
|---|---|
No filter on status | SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) |
status IN ('returned') | SUM(amount) |
CASE WHEN disappears entirely once the user applies their own filter — the metric stops second-guessing them.
Default to a parameter, but back off when the user filters
A common pattern: a metric should default to some sensible time window — the last 30 days, say — but if the user applies their own date filter, the default shouldn’t fight them. This composes a parameter for the default with ald.query.filters check for the override.
- dbt v1.9 and earlier
- dbt v1.10+
- Lightdash YAML
| The user’s query | What active_users counts |
|---|---|
No filter on event_date | Users active in the last lookback_days (default 30) |
event_date >= '2026-01-01' | Users active in the user-selected window — parameter ignored |
ld.parameters.x and ld.query.filters are resolved before the SQL leaves Lightdash.
A custom dimension built in the UI, no PR required
Query introspection isn’t only for fields defined in YAML. Any user with edit access to a chart can spin up a one-off introspection-aware dimension from the explore, save it with the chart, and share it — without touching dbt or opening a PR. Open the explore → Dimensions sidebar → Add → Custom dimension → Custom SQL. Paste the Liquid block directly into the SQL field. No{% raw %} wrapper needed — the SQL goes straight to Lightdash without passing through dbt.

More patterns
- Skip an expensive operation when not needed — wrap a window function or a
LEFT JOIN-driven calculation in{% if ld.query.fields contains "joined_table.field" %}so the cost only shows up when the user has selected something that needs it. - Drill-aware defaults — a
time_graindimension that returnsDATE_TRUNC('day', ...)when the user is grouping at day level, but${TABLE}.event_datewhen they’re not, avoiding a needless function call. - Self-narrating labels — a string dimension that returns a different value depending on what’s filtered, useful for embedded tiles and exports where the viewer can’t see filter chips.
How to find a field’s dotted id
The dotted id is{explore_table_name}.{field_name}:
explore_table_nameis the dbt model name. For a joined table, use thealias:value if one is set on the join — otherwise the value underjoin:. It’s never the join’slabel.field_nameis the YAML key for the dimension or metric — not the label.
{table}.{field} (with a dot, not the underscore that appears in URLs and CSS ids).
Things to know
elseis safe by default. If the field id you reference doesn’t exist in the explore, the check returns false and theelsebranch fires. A typo in the field name will not throw an error.- Date dimensions are expanded into per-interval field ids. A column like
event_datewith time intervals enabled becomesevent_date_day,event_date_week,event_date_month, etc. — each is its own id.ld.query.filters contains "events.event_date"will not match a filter onevents.event_date_day. Useorto match every interval you care about, or check the specific one you expect. - Both checks see the full query. The introspection covers every dimension, metric, and filter in the query — not just the field you’re branching from.
- Combine with parameters. Liquid blocks can mix
ld.query.*checks withld.parameters.*checks in the same template. Both are resolved before the SQL leaves Lightdash. - No effect on existing SQL. If a field’s
sql:doesn’t contain a Liquid block, nothing changes — the renderer skips it entirely. Adopting query introspection on one field doesn’t affect any others.
Related
- How to use parameters — the companion feature for
${ld.parameters.x}substitution and{% if ld.parameters.x == ... %}Liquid blocks - Dimensions reference
- Metrics reference
- Custom fields — using query introspection in UI-created custom dimensions