Metric context
A metric on its own is just an aggregation formula. The actual number depends on the context it runs in: which dimensions group the data, what filters apply, what time window is in play.
This is why one metric can power many reports, and why it's worth understanding what's in that context and how to bend it.
Practice in the AQL Playground: Metric vs Explore (1) · Metric vs Explore (2).
The same metric, three contexts
Take a basic revenue metric:
metric revenue {
definition: @aql sum(order_items.quantity * products.price) ;;
}
Drop it into three different reports and you get three different answers:
// Report 1: grouped by country → revenue per country
explore { dimensions { countries.name } measures { revenue } }
// Report 2: grouped by month → revenue per month
explore { dimensions { orders.created_at | month() } measures { revenue } }
// Report 3: no grouping, with a filter → grand total for 2024
explore { measures { revenue } filters { orders.created_at matches @2024 } }
Same definition. The context (dimensions and filters) gives it a different shape each time.
What's in the context
There are four levers you can pull:
| Lever | What it does | Modified with |
|---|---|---|
| Condition | What rows the metric sees | where() |
| Relationships | Which join paths the metric uses | with_relationships() |
| Level of detail | What grain the metric aggregates at | of_all(), exclude(), dimensionalize() |
| Window | Time-shifted or running calculations | relative_period(), running_total() |
Each of these is a function you pipe a metric through to override one piece of its context.
Quick demos
Override the condition: count only male users, no matter what the report filters on:
count(users.id) | where(users.gender == 'Male')
Override the relationship: when two paths exist between models, pick one:
sum(order_items.revenue)
| with_relationships(
order_items.order_id > orders.id,
order_items.country_id > countries.id
)
Override the level of detail: total order value across all dimensions, ignoring what the report is grouping by:
sum(products.price * order_items.quantity) | exclude(order_items)
Add a window: running total of orders by month:
count(orders.id) | running_total(run: orders.created_at | month())
Why this matters
Most "advanced" AQL is about modifying context. Period-over-period comparisons, percent-of-total, cohort retention: they're all "evaluate this metric in a slightly different context than the surrounding report is using". Once you've internalized that, the rest of the language clicks.
Next
→ Level of detail: controlling what grain a metric is aggregated at.