Let a query order by a value under a repeated level - #979
Open
skearnes wants to merge 1 commit into
Open
Conversation
An ordering key and an aggregate's argument both had to be scalar, which left "the ten highest-yielding reactions" unwritable: a yield lives under outcomes, products, and measurements, so the path resolves to a list rather than a number. Both a cheap model and an expensive one reached for it independently while translating questions, and neither could have succeeded. A Reduction names how to reduce that list to the one value the reaction is judged by, and resolve() already returns a list expression for a repeated path, so it compiles to one DuckDB list aggregate around what the resolver produces. It is refused over a scalar path, which needs no reduction, and inside an aggregated query's order_by, where there is no reaction left to reduce over. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| f"{reduction.path}: {reduction.reduce} reduces a repeated level, and this " | ||
| f"path is already scalar; order by the path itself" | ||
| ) | ||
| return _REDUCERS[reduction.reduce].format(expression=resolved.expression) |
There was a problem hiding this comment.
Reducer ignores resolved leaf type
When an arithmetic reduction such as sum or avg targets a repeated nonnumeric path like outcomes.products.measurements.string_value, _reduced() emits a DuckDB list aggregate without validating the resolved leaf type, causing the query to fail with a DuckDB type error instead of a compile-time QueryError.
This was referenced Aug 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Order.keyandMeasure.pathboth required a scalar path, so a value under a repeated level could not be ordered or aggregated by:That makes "the ten highest-yielding reactions" unwritable. It surfaced while measuring natural-language translation: both Haiku 4.5 and Opus 5 reached for it independently, and it was Opus's only failure across ten questions — unrepairable, because nothing they could have written would have worked. See ord-logbook#45, finding 7.
Changes
Reduction—{"reduce": "min"|"max"|"avg"|"sum"|"count", "path": ...}— names how to reduce a repeated path to one value per reaction.Order.keyandMeasure.pathnow accept one.resolve()already returns a list expression for a repeated path, so this compiles to a single DuckDB list aggregate around what the resolver produces.countfilters nulls rather than usinglen, which would count them.Testing
uv run pytest -n auto— 1210 passed. Ten new tests: each reducer's expression, both refusals, a measure reducing, agreement with whatresolve()produces, and one that executes the SQL.Against the full corpus (2,428,291 reactions) the previously-unwritable query now runs:
And it is right, not merely runnable — the compiled SQL returns the same ten reactions in the same order as a hand-written reference query:
Notes
Worth knowing separately from this change: the top yields in ORD are junk — the highest is
9.02e+19percent. The query is correct; the corpus has percentage values that are not percentages. That is a data-quality question for another day, but anything that ranks by yield will surface it.🤖 Generated with Claude Code
Greptile Summary
The PR adds per-reaction reductions over repeated paths for ordering and aggregate measures, along with query documentation and tests.
Reductionwith min, max, average, sum, and non-null count operations.Confidence Score: 4/5
The incompatible reducer and leaf-type combinations should be rejected before this PR is merged because currently valid query payloads can produce DuckDB type errors.
The reduction compiler verifies repeatedness but ignores the resolved leaf type, allowing arithmetic list reducers to be emitted for concrete repeated VARCHAR paths.
Files Needing Attention: ord_schema/search/query.py
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR Q[Reduction in query] --> R[Resolve repeated path] R --> L[Flat list expression per reaction] L --> D[List reducer] D --> S[Scalar value per reaction] S --> O[ORDER BY] S --> A[Aggregate measure]Reviews (1): Last reviewed commit: "Let a query order by a value under a rep..." | Re-trigger Greptile