From 850aa2eb72e023950d52e369bc8e626f007ab5d9 Mon Sep 17 00:00:00 2001 From: delchev Date: Mon, 7 Sep 2026 10:06:58 +0300 Subject: [PATCH] proposal: a count tile over an aggregating report `kind: count` is specified as "the number of records the report yields", which on a report declaring measures is not the number of rows in its result set - those are groups. Specify the count as the report's `count(*)` measure summed over the rows, and reject an aggregating report that declares no such measure rather than let a generator show the number of groups. Co-Authored-By: Claude Opus 5 --- ...4-count-tile-over-an-aggregating-report.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 proposals/0034-count-tile-over-an-aggregating-report.md diff --git a/proposals/0034-count-tile-over-an-aggregating-report.md b/proposals/0034-count-tile-over-an-aggregating-report.md new file mode 100644 index 0000000..c905a3b --- /dev/null +++ b/proposals/0034-count-tile-over-an-aggregating-report.md @@ -0,0 +1,81 @@ +# A count tile over an aggregating report + +- **Status:** draft +- **Issue:** https://github.com/eclipse-dirigible/dirigible/issues/7102 + +## The problem + +A report `widget:` of `kind: count` is specified as "the number of records the report yields". A +report that declares `measures:` yields one row per group, so there are two different numbers the +tile could show and the specification names only the one nobody can compute from the result set: + +```yaml +reports: + - name: RequestsByStatus + source: VacationRequest + dimensions: [Status] + measures: ["count(*)", "sum(days)"] + widget: { kind: count, label: Vacation Requests } +``` + +The report's rows are `INSUFFICIENT 2`, `DRAFT 7`, `REJECTED 1`, `APPROVED 4` - fourteen requests in +four statuses. A generator that counts the rows of the result set puts **4** on the tile. The number +is wrong, and it is wrong in the direction that hides work: a tile reading 4 next to a list of 14 +records looks like a filter, not a defect. On a report grouped by a single dimension value the same +tile reads **1**. + +Nothing in the model distinguishes the two readings, so a conforming generator can be correct by the +letter of the specification and still show the number of groups. + +## The proposed shape + +No new key. The count of an aggregating report is its `count(*)` measure, summed over the rows: + +```yaml +reports: + # aggregating: the tile shows 14 - the count(*) measure summed over the four rows + - name: RequestsByStatus + source: VacationRequest + dimensions: [Status] + measures: ["count(*)", "sum(days)"] + widget: { kind: count, label: Vacation Requests } + + # not aggregating: one row IS one record, so the row count is the record count + - name: OpenRequests + source: VacationRequest + dimensions: [number, employee.name, fromDate, days] + filter: "Status == DRAFT" + widget: { kind: count, label: Open Requests } +``` + +## Expected behaviour + +- A `kind: count` widget over a report that declares **no** measures shows the number of rows the + report yields, which is the number of records it reads. Unchanged. +- A `kind: count` widget over a report that declares measures shows its `count(*)` measure **summed + over the report's rows**, under the same filter, lifecycle scope, parameters and widget pins that + the report itself applies. +- An aggregating report carrying a `kind: count` widget and **no** `count(*)` measure is rejected at + generation, naming the report and the fix (declare `count(*)`, or use `kind: value` for a measure + the report already has). A tile whose only available number is the number of groups must not be + generated. + +## Edge rules + +- `count(*)` and `count()` are the same measure; a `count()` is not it - it counts the rows + where that field has a value, which is a different number and one an author may want beside the + total. Only the star form satisfies the requirement. +- A report whose rows are its unit of account by construction - a ledger balance (one row per + account) or a statement (one row per declared line) - counts its rows and declares no measure, so + the requirement does not apply to it. +- Widget pins (`at:`) narrow the rows before the sum, so a pinned count is the record count of that + slice. +- A report that declares measures and no `widget:` is unaffected, as is one whose widget is of kind + `value` or `list`. + +## Prior art / workarounds + +The only workaround available today is `kind: value` over the report's own `count(*)` measure - but +`value` reads ONE cell (the first row, or the row the pins select), so on a status-dimensioned report +it shows the count of a single status. There is no authored shape that reaches the total, which is +why this is a defect in the specified behaviour of `count` rather than a request for a new key.