Skip to content

chore: Add (currently failing) test for aggregates ignoring relationship limit#801

Merged
zachdaniel merged 1 commit into
mainfrom
aggregate-relationship-limit-bug
Jul 22, 2026
Merged

chore: Add (currently failing) test for aggregates ignoring relationship limit#801
zachdaniel merged 1 commit into
mainfrom
aggregate-relationship-limit-bug

Conversation

@sevenseacat

Copy link
Copy Markdown
Contributor

An aggregate over a has_many that declares a limit counts/lists all related rows instead of the limited top-N. count(top_5_posts) should be bounded to 5.

Reproduction (failing test on main over the existing Comment.top_ratings, which is limit: 2, sort: [score: :desc]):

# aggregates on Comment:
count(:count_of_top_ratings, :top_ratings)
list(:top_rating_scores, :top_ratings, :score)

# create 5 ratings (scores 1..5), then:
assert %{count_of_top_ratings: 2, top_rating_scores: [5, 4]} = ...
# actual: count_of_top_ratings: 5, top_rating_scores: [5,4,3,2,1]

Root cause (ash_sql/lib/aggregate.ex): in get_subquery/… (the relationship-path clause), on_subquery runs select_all_aggregates (the GROUP BY + count/array_agg) before related_subquery calls limit_from_many (join.ex:397). So any limit is applied after grouping — it caps the number of groups, not rows-per-group. The generated SQL puts LIMIT after GROUP BY resource_id inside the lateral join.

What the fix needs: when the source relationship has limit/offset, the correlated+sorted rows must be wrapped in an inner … ORDER BY <rel.sort> LIMIT n subquery before the aggregate folds them:

SELECT resource_id, count(*)
FROM (SELECT * FROM comment_ratings WHERE <parent-corr> ORDER BY score DESC LIMIT 2) sc0
GROUP BY resource_id

Attempt / how far it got: gating on rel.limit, I passed sort?: true and split the default apply_relationship_subquery to build the inner correlated+ordered+limited subquery, then group outside. The inner subquery builds correctly (verified: parent_as correlation + ORDER BY score DESC + LIMIT). The blocker is composing the grouped outer level into the left_lateral_join — Ecto raises "invalid query was interpolated in a join"; the added nesting level needs the as/parent_as + start_bindings_at/current binding bookkeeping to be rebased, which the single-level aggregate path doesn't currently do.

Contributor checklist

Leave anything that you believe does not apply unchecked.

  • I accept the AI Policy, or AI was not used in the creation of this PR.
  • Bug fixes include regression tests
  • Chores
  • Documentation changes
  • Features include unit/acceptance tests
  • Refactoring
  • Update dependencies

@zachdaniel zachdaniel closed this Jul 22, 2026
@zachdaniel zachdaniel reopened this Jul 22, 2026
@zachdaniel
zachdaniel merged commit 7192a3e into main Jul 22, 2026
121 of 151 checks passed
@zachdaniel

Copy link
Copy Markdown
Contributor

This has been fixed in latest ash_sql

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants