Skip to content

Compare a canvas KPI against a target measure - #9820

Open
dfliess wants to merge 1 commit into
rilldata:mainfrom
dfliess:kpi-measure-comparison
Open

Compare a canvas KPI against a target measure#9820
dfliess wants to merge 1 commit into
rilldata:mainfrom
dfliess:kpi-measure-comparison

Conversation

@dfliess

@dfliess dfliess commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

We wanted to show a measure next to its target in a canvas KPI, and we could not find a way to do it. If there is one and we missed it please let us know, feel free to close this.

What we found: a canvas KPI can only compare a measure with itself, over an earlier time range. But a budget, a target or a forecast is a second measure, so today there is no way to render "revenue vs target". We drew those cards by hand instead, as Vega custom charts, about 35 lines of positioned text per card. This PR is what we use now, in case it is useful to you too.

kpi_grid:
  metrics_view: sales
  measures: [revenue, gross_margin_pct]
  measure_comparisons:
    - measure: revenue
      compare_to: target_revenue
    - measure: gross_margin_pct
      compare_to: target_margin_pct
  comparison: [previous, percent_change]

How it works: the comparison query asks for the other measure over the same time range. It does not ask for the same measure over an earlier range. The result is then stored under the name of the first measure, so KPI.svelte renders it as before and we did not have to touch it.

Things we were not sure about

  • The name. We called it measure_comparisons. We avoided comparison_measures because that name already means something else in the codebase. Rename it if you prefer.
  • A list, not a map. We wrote each pair as a list item, because other canvas widgets already store per-measure settings that way. A map would work too.
  • An entry for a measure that the grid no longer shows is ignored, and not an error. If you remove a measure in the inspector, its entry stays in the YAML. We did not want that to break the whole KPI, because the inspector does not show this option, so nobody could fix it from the UI.
  • Percentage measures need delta, not percent_change. Rill already hides percent_change for percentage measures. delta then gives the difference in points. We wrote this in the docs instead of adding a special case.
  • It also works without a time dimension, which the time comparison does not.

Some questions. We do not know Rill well enough to answer these ourselves.

  1. We run two queries, one per measure, over the same range and the same filter. Maybe one query is enough, but that is a bigger change to the component and we did not want to make it here.
  2. There is no UI for this, it is YAML only. We did not know where the option should go in the inspector.
  3. We only did this in one place, the KPI widget. If you want the same thing in the leaderboard, the pivot or Explore, then the target probably belongs in the metrics view instead, and this should be built there. We did the small version because we did not know.

Happy to rename things or rework it, and just as happy to keep it on our side if you do not want it.

A canvas KPI can only compare a measure with itself, over an earlier time
range. But a budget, a target or a forecast is a second measure, so there is
no way to render "revenue vs target" today, and those cards end up drawn by
hand as Vega custom charts.

`measure_comparisons` says which measure a KPI is compared against:

```yaml
kpi_grid:
  metrics_view: sales
  measures: [revenue]
  measure_comparisons:
    - measure: revenue
      compare_to: target_revenue
  comparison: [previous, percent_change]
```

The comparison query then asks for that measure over the same time range,
instead of the same measure over an earlier range. The result is stored under
the name of the first measure, so the existing rendering path does not change.
Each pair is a list item, because other canvas widgets already store
per-measure settings that way.

Both measures must belong to the same metrics view, and the reconciler checks
`compare_to`. An entry for a measure the grid no longer shows is ignored, and
not an error: removing a measure in the inspector leaves its entry behind, and
breaking the resource for that would leave a state nobody can fix from the UI,
since the inspector does not show this option.

It also works without a time dimension, which the time comparison does not.
For a percentage measure use `delta`, since Rill already hides
`percent_change` there, and `delta` gives the difference in points.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant