| @objectstack/spec | patch |
|---|---|
| @objectstack/service-analytics | patch |
| @objectstack/rest | patch |
feat(analytics): honour widget dateGranularity, sortBy/sortOrder, and limit in the dataset query (#3588)
Three presentation options were accepted by the metadata layer and then dropped
by the analytics query builder. They reached no SQL, produced no error, and the
only way to notice was to read the sql a dataset response echoes — so a
dashboard could declare dateGranularity: 'month' and quietly render one bar
per record.
dateGranularitynow buckets.DatasetSelectiongained an optionaldateGranularity, applied to every selecteddatedimension. Precedence per dimension: an explicittimeDimensionsgranularity, then the selection's, then the dataset dimension's own default. A widget can bucket a trend by month without the dataset committing every other consumer to that granularity.order/limit/offsetnow apply on every path. They are applied to the ASSEMBLED grid — after measure-scoped sub-queries merge, aftercompareTocolumns attach, and after derived measures are computed — so a derived measure is a valid sort key and the ObjectQL aggregate path (which has no ordering grammar, and which native SQL hands every date-bucketed query to) orders identically to native SQL. A single-query selection still pushes the window down into the statement. Anorderkey that names nothing the selection projects is now rejected (400) rather than silently ignored.limitis deterministic. Without anorder, a limit orders by the selected dimensions first, so it truncates a reproducible window instead of an arbitrary subset.- Widget
optionsis a contract again. The four query-affecting keys (dateGranularity,sortBy,sortOrder,limit) plusstageOrderare declared onDashboardWidgetOptionsSchema, so a typo likesortDirectionis an author-time error. The bag stays open — renderer extras (icon,columns,striped, …) pass through untouched.
Two latent bugs surfaced while fixing the above and are fixed here too:
order/limitwere forwarded to EVERY sub-query. A measure-scoped supplementary query selects one measure, so an inheritedORDER BYnamed a column it never selected, and an inheritedLIMITtruncated it before the merge — dropping rows from the assembled grid. Nothing hit this only because nothing passedorder.- The
compareTopass built its query by hand and skipped granularity resolution, so a month-bucketed primary grid was merged against raw-timestamp comparison rows. No dimension key matched and every<measure>__comparecolumn came back empty.
ObjectQLStrategy now also echoes a representative sql (with date_trunc,
WHERE, ORDER BY, and LIMIT; filter values parameterized, never inlined).
Previously the sql field simply vanished from the response whenever a query
was date-bucketed, leaving an author unable to tell "not implemented" from "this
strategy doesn't report".