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 12 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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 |
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.
…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>
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>
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>
…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>
- Change Field from a sealed class with inner Plain/Computed subclasses to
a sealed interface with top-level PlainField and ComputedField
- ComputedField exposes outputColumnName (e.g. "date.isoWeek") matching the
SequencePositionField pattern from main, eliminating the need for a
separate internal alias and column-renaming step in SiloQueryModel
- Extract dot-notation parsing into ScalarFunctionFieldConverter in
request/converter/, mirroring main's converter package structure
- Revert SaneQlAssignment to auto-quoting ("name":=value), undoing the
temporary workaround; SaneQlAssignment(field.outputColumnName, ...) now
produces the correct quoted alias directly
- Update all call sites and tests accordingly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
50e05da to
2092281
Compare
- PlainFieldConverter: reject dot-notation fields with a clear error
("Scalar functions are not supported in fields for this endpoint")
so the /details e2e test gets the expected message
- AggregatedFieldConverterTest: pass scalarFunctionFieldConverter in constructor
- ScalarFunctionFieldTest: use JUnit assertTrue instead of Kotlin assert
(Kotlin assert is a no-op without -ea JVM flag)
- ControllerDescriptions: remove backslash-escapes from raw strings
(\" in triple-quoted strings renders as literal \", not ")
- computed-fields.mdx: apply prettier formatting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The page was added to the nav but not to the ordered page list used by the next-button and navigation link tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AggregatedAction already has @JsonInclude(NON_EMPTY), so empty lists are suppressed anyway. The JSON serialization is never sent to SILO (it uses SaneQL), so the annotation served no purpose. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.