fix(measurements): page day sums and sleep nights in SQL

The day-grouped list read the newest `limit` raw rows (the web asked
for 5000), folded them into days and returned every day at once: one
page of thousands of rows, and every day older than the 5000th raw row
missing without a word. The sleep list did the same at a year.

Both now pick the page in Postgres. The day mode selects the page's
local days with the distinct-day total from `COUNT(*) OVER ()` in the
same query, then folds only those days into per-source groups in SQL
and lets the source ladder choose among the groups, so no raw row
reaches the process. The sleep mode cuts sessions with the same gap
rule as the reconstruction, pages wake days the same way and reads only
the page's stage rows; the 365-day cut and `meta.windowFrom` are gone.
A sleep page holds at most 1000 nights, echoed in `meta.limit`.

The web list pages both modes at 25 like the per-sample list. The
`partial` day marker is dropped from the contract; no day is cut any
more.

Refs #1245
This commit is contained in:
Marc-André Bombeck committed 2026-10-19 16:43:39 +02:00
1 parent 01bf8fbb64
commit cfd6219981
10 files changed
+987 -551

No files matched your search

+8 -16
View File
@@ -4198,7 +4198,14 @@ paths:
tags:
- Measurements
summary: List measurements
description: Filter by type + date range. Response is paged via offset/limit; default limit 100, hard cap 500.
description: >-
Filter by type + date range. Response is paged via offset/limit; default limit 100.
With `groupBy=day` on a cumulative type the rows are one sum per local day, and `limit` / `offset` page over
those days; every returned day is complete. `type=SLEEP_DURATION` returns one row per night, paged the same way
over nights. On both, `meta.total` counts the days or nights in the filtered window, and a page holds at most
1000 of them (`meta.limit` echoes the value applied).
parameters:
- in: query
name: type
@@ -46242,9 +46249,6 @@ components:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
partial:
description: True on the one bucket whose day was cut off by the read cap, so its sum understates the real total.
type: boolean
required:
- id
- type
@@ -47069,9 +47073,6 @@ components:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
partial:
description: True on the one bucket whose day was cut off by the read cap, so its sum understates the real total.
type: boolean
required:
- id
- type
@@ -47140,12 +47141,6 @@ components:
type: integer
minimum: 0
maximum: 9007199254740991
windowFrom:
description: "Set on the per-night SLEEP_DURATION mode when the requested window was longer than 365 days: the instant
the read started from. The window is a year ending at `to` (or now); page further back by moving `to`."
type: string
format: date-time
pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
required:
- total
- limit
@@ -47259,9 +47254,6 @@ components:
type: integer
minimum: -9007199254740991
maximum: 9007199254740991
partial:
description: True on the one bucket whose day was cut off by the read cap, so its sum understates the real total.
type: boolean
status:
type: string
enum: