Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/docs/developers/build/dashboards/canvas-widgets/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ Adhoc measures belong to a single widget: to use the same calculation in two wid

For a calculation that many widgets or dashboards share, add a measure to the [metrics view](/developers/build/metrics-view) instead.

## Exact values

Measures that define an [`exact_expression`](/developers/build/metrics-view/measures/exact-measures) are approximate by default and show a `≈` chip next to their name in KPIs, tables, pivots and leaderboards. A chart shows one chip next to its title that covers every measure of the chart. Viewers click a chip to compute that measure, or that chart, exactly in that widget (in a leaderboard widget, in that one leaderboard) until they change the time range, comparison, grain, timezone, filters or tab. Other widgets keep their approximate values until their own chip is clicked. The selection is not part of the URL, so links and scheduled reports of the canvas open approximate. See [Exact values](/guide/dashboards/explore/exact-measures) in the guide for the chip states.

## Navigation

All Data widgets also provide a button to "Go to explore" that can navigate to the Explore dashboard if available.
Expand Down
73 changes: 73 additions & 0 deletions docs/docs/developers/build/metrics-view/measures/exact-measures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: "Approximate and Exact Measures"
description: Serve fast approximate aggregates by default and let viewers switch to exact values on demand
sidebar_label: "Approximate and Exact"
sidebar_position: 45
---

Distinct counts, quantiles and other sketch-based aggregates come in two flavours in every OLAP engine: a fast approximate form and a much slower exact form. An approximate distinct count keeps a small HyperLogLog sketch per group and is usually within one or two percent of the truth; the exact form holds every distinct value per group, so on large tables it can be ten to a hundred times slower and cannot be served from [rollups](/developers/build/metrics-view/rollups).

A measure can define both. `expression` stays the default and is treated as approximate, and `exact_expression` is used only when a viewer or a query explicitly asks for exact values.

```yaml
measures:
- name: unique_users
display_name: Unique Users
expression: uniqCombined64(user_id) # fast, approximate default
exact_expression: uniqExact(user_id) # used only when exact values are requested
format_preset: humanize

- name: p95_latency
display_name: P95 Latency
expression: quantileTDigest(0.95)(latency_ms)
exact_expression: quantileExact(0.95)(latency_ms)
format_preset: interval_ms

- name: revenue_per_user
display_name: Revenue per User
requires: [total_revenue, unique_users]
expression: total_revenue / unique_users # follows the precision of unique_users
format_preset: currency_usd
```

## How it behaves

- Both expressions share every other setting of the measure: `requires`, `per`, `window`, `treat_nulls_as`, formatting, tags and security policies. Only the SQL differs. Setting `exact_expression` without `expression` is an error.
- Dashboards show the approximate value by default and mark measures that have an exact variant with a small `≈` chip. Clicking the chip computes that measure exactly for the current filters and time range, in the leaderboard, table or chart the chip sits in; see [Exact values in Explore](/guide/dashboards/explore/exact-measures).
- A measure that references an exact measure (such as `revenue_per_user` above) is computed from the exact value too, and requesting such a measure exactly computes the measures it references exactly. You do not declare anything for it. Precision is resolved once per query: a measure requested exactly anywhere in a query, including inside a measure filter or through a derived measure, is computed exactly everywhere in it.
- Measures without an `exact_expression` are unchanged. Rill does not know whether a lone `expression` is approximate, so they show no chip.
- Queries that compute a measure exactly never use rollups, because exact aggregates cannot be derived from pre-aggregated rows.
- Rill validates `exact_expression` like `expression` when the metrics view reconciles, and the exact variant must produce a numeric type.

## Engine functions

| Engine | Approximate | Exact |
|--------|-------------|-------|
| ClickHouse | `uniq(x)`, `uniqCombined64(x)`, `quantileTDigest(p)(x)` | `uniqExact(x)`, `quantileExact(p)(x)` |
| DuckDB | `approx_count_distinct(x)`, `approx_quantile(x, p)` | `count(DISTINCT x)`, `quantile_cont(x, p)` |
| Druid | `APPROX_COUNT_DISTINCT_DS_HLL(x)` | `COUNT(DISTINCT x)` |
| Pinot | `DISTINCTCOUNTHLL(x)` | `DISTINCTCOUNT(x)` |

:::caution Druid
Druid treats `COUNT(DISTINCT x)` as approximate unless the connection sets the query context `useApproximateCountDistinct=false`, and in exact mode older Druid versions allow only one distinct count per query. A query that computes two measures exactly can fail; the dashboard shows the error on the measure's chip.
:::

## Requesting exact values from the API

Every measure in a [metrics API](/developers/build/metrics-view/metrics-sql) aggregation request accepts `exact: true`:

```json
{
"metrics_view": "events",
"measures": [
{ "name": "unique_users", "exact": true },
{ "name": "total_revenue" }
]
}
```

The flag is a no-op for measures without an `exact_expression`. The [AI tools](/guide/ai/mcp) use the same flag when a user asks for exact or reconciled numbers. Scheduled reports and alerts do not set it unless the stored query does.

## Disabling exact queries

Set `rill.metrics.exact_measures: false` in the `variables` of `rill.yaml` to make any query that would compute a measure with its `exact_expression` fail with an error, including queries that request it only inside a measure filter. Use it as a kill switch when exact aggregations put too much load on the database; approximate queries keep working, and so does a flag on a measure without an `exact_expression`.
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ Explore these advanced capabilities to enhance your measures:
- **[Case Statements and Filters](/developers/build/metrics-view/measures/case-statements)** - Use conditional logic and filtering in your measures
- **[Referencing Measures](/developers/build/metrics-view/measures/referencing)** - Reference and combine existing measures in your calculations
- **[Quantiles](/developers/build/metrics-view/measures/quantiles)** - Calculate percentiles and quantiles for statistical analysis
- **[Approximate and Exact](/developers/build/metrics-view/measures/exact-measures)** - Serve fast approximate aggregates by default and let viewers switch to exact values on demand
- **[Fixed Measures](/developers/build/metrics-view/measures/fixed-measures)** - Create measures with fixed values and constants
- **[Window Functions](/developers/build/metrics-view/measures/windows)** - Apply window functions for advanced analytical operations
38 changes: 38 additions & 0 deletions docs/docs/guide/dashboards/explore/exact-measures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: "Exact Values"
description: Switch an approximate measure to its exact value with one click
sidebar_label: "Exact Values"
sidebar_position: 36
---

Some measures are expensive to compute exactly. Distinct counts such as unique users, and quantiles such as P95 latency, are therefore often defined with a fast approximate aggregate that is within a percent or two of the truth. When the project developer also defines an [exact expression](/developers/build/metrics-view/measures/exact-measures) for such a measure, the dashboard shows the approximate value by default and lets you switch to the exact value when you need to reconcile a number.

## Read the chip

A small chip after the measure name tells you how the value was computed:

| Chip | Meaning |
|------|---------|
| `≈` outlined | Approximate. The default; fast. |
| `=` filled | Exact. You switched this measure to its exact expression. |
| `=` dashed | Exact through a related measure: a measure this one is calculated from, or a measure calculated from it, is exact. |
| spinner | The exact value is being computed. The previous approximate value stays visible, dimmed, until it arrives. |
| `!` | The exact query failed or timed out. Hover for the error, click to return to the approximate value. |

Measures without an exact expression show no chip.

Each chip controls the part of the dashboard it sits in: the big-number row (the big numbers and their charts), one leaderboard, the dimension table, the Time Dimension Detail table or the pivot. Within that part, precision is resolved once, so a measure never has two values in the same table. Switching a unique count to exact makes every ratio calculated from it in that part exact (dashed chip). Switching a ratio to exact makes the unique counts it is calculated from exact as well, again with the dashed chip. Clicking a dashed chip keeps that measure exact on its own, so it stays exact when you switch the related measure back.

## Switch a measure to exact

Click the `≈` chip next to the measure name, in a big number, a leaderboard or dimension table header, the Time Dimension Detail header, or a pivot column header. Only that part of the dashboard is recomputed exactly: the big-number row with its charts, that one leaderboard, the dimension table, the Time Dimension Detail table or the pivot. Other leaderboards and tables keep their approximate value until you click their own chip, so each click runs one expensive query rather than one for every table on the page. Click the chip again to return to the approximate value.

Exact queries can take noticeably longer than approximate ones, so the switch applies only to the view you are looking at:

- Changing the time range, comparison, grain, timezone, filters, the selected dimension or the view (explore, dimension table, Time Dimension Detail, pivot) returns every measure to approximate. New queries are always approximate until you click again.
- Showing, hiding or reordering measures keeps your selection.
- The selection is not part of the URL, so bookmarks, shared links and public URLs always open approximate. Scheduled reports and alerts are approximate as well.
- A CSV or Parquet export downloads what you see, so exporting while a measure is exact gives you the exact values.
- A measure filter (for example "publishers where unique users > 1,000") is evaluated with the exact value only in queries that also show that measure. Switch the measure to exact and keep it visible when the filter has to be exact.

In a canvas dashboard the chip works the same way for KPIs, tables, pivots, leaderboards and charts. A click applies to that widget only, and to one leaderboard at a time inside a leaderboard widget. KPIs and table, pivot and leaderboard columns each have their own chip. A chart has one chip, next to its title, that switches every measure of the chart at once.
2 changes: 2 additions & 0 deletions docs/docs/reference/project-files/metrics-views.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ _[array of object]_ - Used to define the numeric aggregates of columns from your

- **`expression`** - _[string]_ - a combination of operators and functions for aggregations

- **`exact_expression`** - _[string]_ - An optional exact variant of `expression` for measures whose default expression is approximate (e.g. `expression: uniq(user_id)` with `exact_expression: uniqExact(user_id)` in ClickHouse, or `approx_count_distinct(user_id)` with `count(DISTINCT user_id)` in DuckDB). Dashboards show the approximate value by default and only use the exact expression when a viewer or query explicitly asks for exact values. It shares all other measure settings with `expression`.

- **`window`** - _[anyOf]_ - A measure window can be defined as a keyword string (e.g. 'time' or 'all') or an object with detailed window configuration. For more information, see the [window functions](/developers/build/metrics-view/measures/windows) documentation.

- **option 1** - _[string]_ - Shorthand: `time` or `true` means time-partitioned, `all` means non-partitioned.
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/reference/project-files/rill-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ _[object]_ - A map of key-value pairs for setting variables on your project. It

- **`rill.metrics.approximate_comparisons_two_phase_limit`** - _[integer]_ - Row-limit threshold under which metrics comparison queries use a two-phase strategy (base values first, comparison values second). Default: 250.

- **`rill.metrics.exact_measures`** - _[boolean]_ - Allow queries to compute measures with their `exact_expression` when requested (per measure, with `exact: true`). When false, any query that would compute a measure exactly fails with an error; a flag on a measure without an `exact_expression` stays a no-op. Acts as a kill switch for expensive exact aggregations. Default: true.

- **`rill.metrics.exactify_druid_topn`** - _[boolean]_ - Split Druid TopN queries into two queries to improve measure accuracy, at the cost of performance. Default: false.

- **`rill.metrics.druid_mvd_filtered_group_by`** - _[boolean]_ - Narrow unnested (multi-value) dimensions to the filtered values when grouping in Druid, so results contain only the filtered values rather than every value co-occurring in matching rows. Default: false.
Expand Down
Loading
Loading