feat(lapis): support scalar functions in aggregation fields via dot notation - #1780
feat(lapis): support scalar functions in aggregation fields via dot notation#1780fhennig wants to merge 7 commits into
Conversation
…otation Users can now specify computed fields like `date.isoWeek` in the `fields` parameter of the `/aggregated` endpoint. LAPIS validates the function against a whitelist (currently `isoWeek` for date fields), generates a `map()` step before `groupBy()` in the SaneQL query, and renames the internal alias columns back to the user-facing `field.function` names in the response. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Add a doc comment to ScalarFunction explaining new functions must be whitelisted there, and describe the <field>.<function> syntax in the OpenAPI description for the aggregated endpoint's fields parameter. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cover stratifying and ordering by a computed field (date.isoWeek), bad requests for unknown functions and wrong field types, and that computed fields are rejected on /details. Verified against a real SILO instance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a concepts page explaining the <field>.<function> dot-notation syntax for the fields parameter (e.g. date.isoWeek), and link it from the references introduction. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Code review (high effort)Reviewed 🐞 Correctness bug (CONFIRMED)
Repro: None of the added tests exercise this path — 🧹 Cleanup findings (lower severity)
Note: I also checked whether the dot-notation parsing regresses metadata fields whose names literally contain a 🤖 Generated with Claude Code |
orderBy previously used a plain-field-only cleaner, so a computed field (e.g. "date.isoWeek") with different casing than in `fields` would not be recognized as the same field, silently breaking the alias rewrite and sending an invalid column reference to SILO. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
computedFields was recomputed via filterIsInstance three separate times over the same list; compute it once and reuse it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support for scalar/computed fields (e.g. date.isoWeek) in the fields parameter for the /aggregated endpoint by introducing a typed Field model, validating scalar functions against a whitelist, generating a pre-groupBy() map() step in SaneQL, and translating internal alias columns back to user-facing keys in the response.
Changes:
- Introduces
Field.PlainvsField.ComputedplusScalarFunctionwhitelist and validation (type-checked). - Extends aggregated SaneQL generation with a
map()step for computed fields and adjusts ordering/response key rewriting. - Adds unit + e2e tests and documentation for computed fields; blocks computed fields in
/details.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| lapis/src/main/kotlin/org/genspectrum/lapis/request/Field.kt | Introduces Field sealed type and parsing for <field>.<function> computed syntax with type validation. |
| lapis/src/main/kotlin/org/genspectrum/lapis/request/ScalarFunction.kt | Adds scalar function whitelist (currently isoWeek for DATE). |
| lapis/src/main/kotlin/org/genspectrum/lapis/request/OrderByField.kt | Canonicalizes orderBy fields using the same converter as fields (supports computed fields casing). |
| lapis/src/main/kotlin/org/genspectrum/lapis/silo/SiloQuery.kt | Adds computed-field map() step before groupBy() and groups by computed aliases. |
| lapis/src/main/kotlin/org/genspectrum/lapis/model/SiloQueryModel.kt | Splits plain vs computed fields, rewrites computed orderBy to aliases, and renames aliases back in responses. |
| lapis/src/main/kotlin/org/genspectrum/lapis/model/mutationsOverTime/QueriesOverTimeModel.kt | Updates aggregated call site to named parameters after signature expansion. |
| lapis/src/main/kotlin/org/genspectrum/lapis/controller/LapisController.kt | Rejects computed fields for /details requests with a 400. |
| lapis/src/main/kotlin/org/genspectrum/lapis/controller/ControllerDescriptions.kt | Documents computed-field dot notation in aggregated fields OpenAPI description. |
| lapis/src/test/kotlin/org/genspectrum/lapis/silo/SiloQueryToSaneQlTest.kt | Adds SaneQL rendering test for computed fields (map + groupBy alias). |
| lapis/src/test/kotlin/org/genspectrum/lapis/silo/SiloQueryTest.kt | Updates aggregated action tests to named parameters. |
| lapis/src/test/kotlin/org/genspectrum/lapis/request/SequenceFiltersRequestWithFieldsTest.kt | Updates tests to use Field.Plain. |
| lapis/src/test/kotlin/org/genspectrum/lapis/request/ScalarFunctionFieldTest.kt | New tests for computed-field parsing/validation. |
| lapis/src/test/kotlin/org/genspectrum/lapis/request/OrderByFieldConverterTest.kt | Adds coverage for computed fields in orderBy conversion/canonicalization. |
| lapis/src/test/kotlin/org/genspectrum/lapis/model/SiloQueryModelTest.kt | Adds tests for map-step emission, alias renaming, and orderBy alias rewriting. |
| lapis/src/test/kotlin/org/genspectrum/lapis/model/mutationsOverTime/Helpers.kt | Updates aggregated action helper call site to named parameters. |
| lapis/src/test/kotlin/org/genspectrum/lapis/controller/LapisControllerCommonFieldsTest.kt | Updates controller tests to use Field.Plain. |
| lapis/src/test/kotlin/org/genspectrum/lapis/controller/Helpers.kt | Updates request helper to construct Field.Plain. |
| lapis-e2e/test/details.spec.ts | Adds e2e assertion that computed fields are rejected on /details. |
| lapis-e2e/test/aggregated.spec.ts | Adds e2e coverage for computed fields in /aggregated including ordering and error cases. |
| lapis-docs/src/content/docs/references/introduction.mdx | Links to computed fields concept doc. |
| lapis-docs/src/content/docs/concepts/computed-fields.mdx | New documentation page describing computed fields and available functions. |
| lapis-docs/astro.config.mjs | Adds “Computed fields” to docs navigation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fun `isoWeek on non-date field throws BadRequestException`() { | ||
| val ex = assertThrows<BadRequestException> { underTest.convert("country.isoWeek") } | ||
| assert(ex.message.orEmpty().contains("STRING")) { "Expected error to mention type, got: ${ex.message}" } | ||
| } |
| A field can also be a computed field of the form \"<field>.<function>\" (e.g. \"date.isoWeek\"), which applies a | ||
| scalar function to the field and stratifies by the result. The response uses the full \"<field>.<function>\" string | ||
| as the field name. Currently, the only supported function is \"isoWeek\", which is only valid for date fields.""" |
…eries SILO accepts a quoted identifier containing a literal "." as a column name (verified against a live SILO instance), so the computed field's canonical name (e.g. "date.isoWeek") can be used directly as the SaneQL map/groupBy/orderBy column instead of routing through an internal __scalar_<function>_<field> alias. This removes the response-column rename and orderBy-rewrite steps entirely, along with the class of bugs that comes from keeping two names in sync. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
WIP
resolves #1767
Summary
Users can now specify computed fields like
date.isoWeekin thefieldsparameter of the/aggregatedendpoint. LAPIS validates the function against a whitelist (currentlyisoWeekfor date fields), generates amap()step beforegroupBy()in the SaneQL query, and renames the internal alias columns back to the user-facingfield.functionnames in the response.PR Checklist
llms.txt.