Skip to content
Merged
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 # uses unique_users as the query computes it
format_preset: currency_usd
```

## How it behaves

- Both expressions share every other setting of the measure: `treat_nulls_as`, formatting, tags and security policies. Only the SQL differs. Setting `exact_expression` without `expression` is an error, and only simple measures can define it: a measure with `requires` or `per` (a derived measure), a measure with a `window`, and an ad hoc measure defined in a dashboard have no exact variant.
- 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 derived measure such as `revenue_per_user` above is calculated from the other measures as the query computes them: in a table where `unique_users` is exact, the ratio uses the exact count. It shows no chip of its own. Precision is resolved once per query: a measure requested exactly anywhere in a query, including inside a measure filter, 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
37 changes: 37 additions & 0 deletions docs/docs/guide/dashboards/explore/exact-measures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
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. |
| 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. That includes measures calculated from other measures, such as a ratio of two unique counts: they are computed from the values shown next to them, so a ratio follows the precision of the unique counts in the same table.

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 page (its chart and table) or the pivot. Within that part, precision is resolved once, so a measure never has two values in the same table.

## 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 page 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`. Only simple measures without a `window` can define it: derived measures are calculated from the measures as a query computes them.

- **`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