|
| 1 | +--- |
| 2 | +"@objectstack/spec": patch |
| 3 | +"@objectstack/service-analytics": patch |
| 4 | +"@objectstack/rest": patch |
| 5 | +--- |
| 6 | + |
| 7 | +feat(analytics): honour widget `dateGranularity`, `sortBy`/`sortOrder`, and `limit` in the dataset query (#3588) |
| 8 | + |
| 9 | +Three presentation options were accepted by the metadata layer and then dropped |
| 10 | +by the analytics query builder. They reached no SQL, produced no error, and the |
| 11 | +only way to notice was to read the `sql` a dataset response echoes — so a |
| 12 | +dashboard could declare `dateGranularity: 'month'` and quietly render one bar |
| 13 | +per record. |
| 14 | + |
| 15 | +- **`dateGranularity` now buckets.** `DatasetSelection` gained an optional |
| 16 | + `dateGranularity`, applied to every selected `date` dimension. Precedence per |
| 17 | + dimension: an explicit `timeDimensions` granularity, then the selection's, |
| 18 | + then the dataset dimension's own default. A widget can bucket a trend by month |
| 19 | + without the dataset committing every other consumer to that granularity. |
| 20 | +- **`order` / `limit` / `offset` now apply on every path.** They are applied to |
| 21 | + the ASSEMBLED grid — after measure-scoped sub-queries merge, after `compareTo` |
| 22 | + columns attach, and after derived measures are computed — so a derived measure |
| 23 | + is a valid sort key and the ObjectQL aggregate path (which has no ordering |
| 24 | + grammar, and which native SQL hands every date-bucketed query to) orders |
| 25 | + identically to native SQL. A single-query selection still pushes the window |
| 26 | + down into the statement. An `order` key that names nothing the selection |
| 27 | + projects is now rejected (400) rather than silently ignored. |
| 28 | +- **`limit` is deterministic.** Without an `order`, a limit orders by the |
| 29 | + selected dimensions first, so it truncates a reproducible window instead of an |
| 30 | + arbitrary subset. |
| 31 | +- **Widget `options` is a contract again.** The four query-affecting keys |
| 32 | + (`dateGranularity`, `sortBy`, `sortOrder`, `limit`) plus `stageOrder` are |
| 33 | + declared on `DashboardWidgetOptionsSchema`, so a typo like `sortDirection` is |
| 34 | + an author-time error. The bag stays open — renderer extras (`icon`, `columns`, |
| 35 | + `striped`, …) pass through untouched. |
| 36 | + |
| 37 | +Two latent bugs surfaced while fixing the above and are fixed here too: |
| 38 | + |
| 39 | +- `order`/`limit` were forwarded to EVERY sub-query. A measure-scoped |
| 40 | + supplementary query selects one measure, so an inherited `ORDER BY` named a |
| 41 | + column it never selected, and an inherited `LIMIT` truncated it before the |
| 42 | + merge — dropping rows from the assembled grid. Nothing hit this only because |
| 43 | + nothing passed `order`. |
| 44 | +- The `compareTo` pass built its query by hand and skipped granularity |
| 45 | + resolution, so a month-bucketed primary grid was merged against raw-timestamp |
| 46 | + comparison rows. No dimension key matched and every `<measure>__compare` |
| 47 | + column came back empty. |
| 48 | + |
| 49 | +`ObjectQLStrategy` now also echoes a representative `sql` (with `date_trunc`, |
| 50 | +`WHERE`, `ORDER BY`, and `LIMIT`; filter values parameterized, never inlined). |
| 51 | +Previously the `sql` field simply vanished from the response whenever a query |
| 52 | +was date-bucketed, leaving an author unable to tell "not implemented" from "this |
| 53 | +strategy doesn't report". |
0 commit comments