fix: push limit/offset into the query instead of materializing then slicing
cog-api's paginate() sliced an already-fully-materialized result: every page of a deep sweep re-ran the whole cog_spending()/cog_revenue() query and re-listified every row, just to keep up to 1000 and discard the rest. A 193,105-row/194-page fleet-wide sweep (cog_explorer's Southern guide, corpus summary build) repeated that full cost 194 times and wedged the production server for hours on 2026-08-06 -- single request, CPU-bound, single-threaded plumber process, no other request could get through, not even /health. cog_spending()/cog_revenue() gain optional limit/offset, pushed into .build_verb_sql() as SQL LIMIT/OFFSET behind the existing (already deterministic) ORDER BY. The full unpaginated row count rides along via COUNT(*) OVER() in the same scan -- exposed as a total_rows attribute -- so a caller walking pages never needs a second round trip to ask how many there are. A page now costs O(limit), not O(full result). Mutually exclusive with complete = TRUE (which fills a grid over the FULL requested (year, category) space -- pagination over a partial slice of already-grouped rows has no defined meaning for the cells it would fill) and with recipe (whose result comes from a separate, not-yet-wired query path). Both abort with a clear classed condition rather than silently ignoring the parameter. limit/offset default to NULL; every existing call site is unaffected.
This commit is contained in:
+12
-1
@@ -13,7 +13,9 @@ cog_revenue(
|
||||
basis = c("harmonized", "raw"),
|
||||
recipe = NULL,
|
||||
revenue_concept = c("general", "total"),
|
||||
complete = FALSE
|
||||
complete = FALSE,
|
||||
limit = NULL,
|
||||
offset = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
@@ -115,6 +117,15 @@ possibly-misleading `"harmonized"`/`"raw"` value.}
|
||||
`recipe` or with `expenditure_concept = "total"` (class
|
||||
`uscogdata_complete_unsupported`) — neither draws its cells from
|
||||
`code_set`.}
|
||||
|
||||
\item{limit}{Maximum number of result rows to return, pushed into the SQL
|
||||
query itself (`LIMIT`/`OFFSET`) rather than applied after the full
|
||||
result is materialized. `NULL` (the default) returns every matching row,
|
||||
exactly as before this parameter existed. Mutually exclusive with
|
||||
`recipe` and with `complete = TRUE` -- see `offset` and `total_rows`.}
|
||||
|
||||
\item{offset}{Rows to skip before `limit` starts counting (0-based).
|
||||
Ignored if `limit` is `NULL`; defaults to `0L` when `limit` is set.}
|
||||
}
|
||||
\value{
|
||||
Tibble with columns `year`, `canonical_govid`, `gov_name`,
|
||||
|
||||
+17
-2
@@ -13,7 +13,9 @@ cog_spending(
|
||||
basis = c("harmonized", "raw"),
|
||||
recipe = NULL,
|
||||
expenditure_concept = c("primary", "direct", "total"),
|
||||
complete = FALSE
|
||||
complete = FALSE,
|
||||
limit = NULL,
|
||||
offset = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
@@ -135,6 +137,15 @@ possibly-misleading `"harmonized"`/`"raw"` value.}
|
||||
`recipe` or with `expenditure_concept = "total"` (class
|
||||
`uscogdata_complete_unsupported`) — neither draws its cells from
|
||||
`code_set`.}
|
||||
|
||||
\item{limit}{Maximum number of result rows to return, pushed into the SQL
|
||||
query itself (`LIMIT`/`OFFSET`) rather than applied after the full
|
||||
result is materialized. `NULL` (the default) returns every matching row,
|
||||
exactly as before this parameter existed. Mutually exclusive with
|
||||
`recipe` and with `complete = TRUE` -- see `offset` and `total_rows`.}
|
||||
|
||||
\item{offset}{Rows to skip before `limit` starts counting (0-based).
|
||||
Ignored if `limit` is `NULL`; defaults to `0L` when `limit` is set.}
|
||||
}
|
||||
\value{
|
||||
Tibble with columns `year`, `canonical_govid`, `gov_name`,
|
||||
@@ -144,7 +155,11 @@ Tibble with columns `year`, `canonical_govid`, `gov_name`,
|
||||
and `value_source` when `complete = TRUE`.
|
||||
Carries a `provenance` attribute matching `inst/schemas/provenance-v1.json`,
|
||||
whose `completion` block reports `applied`, `rows_filled`, and the
|
||||
per-year `absence_means` rule that was applied.
|
||||
per-year `absence_means` rule that was applied. When `limit` is set,
|
||||
also carries a `total_rows` attribute: the full unpaginated row count,
|
||||
computed by the same query (`COUNT(*) OVER()`) rather than a second
|
||||
round trip -- so a caller walking pages never has to ask "how many are
|
||||
there" separately.
|
||||
}
|
||||
\description{
|
||||
One row per `(year, canonical_govid, spend_subtype, category)`. Amounts are
|
||||
|
||||
Reference in New Issue
Block a user