Skip to main content

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.

Try it interactively

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:

LeverWhat it doesModified with
ConditionWhat rows the metric seeswhere()
RelationshipsWhich join paths the metric useswith_relationships()
Level of detailWhat grain the metric aggregates atof_all(), exclude(), dimensionalize()
WindowTime-shifted or running calculationsrelative_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.


Open Markdown
Let us know what you think about this document :)