Skip to content

Compute aggregates of related items - #2455

Draft
Flo0807 wants to merge 1 commit into
feature/remove-item-queryfrom
feature/relationship-aggregates
Draft

Flo0807 wants to merge 1 commit into
feature/remove-item-queryfrom
feature/relationship-aggregates

Conversation

@Flo0807

@Flo0807 Flo0807 commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Stack (merge in this order)

  1. Build queries only in the Ecto adapter #2338 Build queries only in the Ecto adapter, based on develop
  2. Let the adapter turn filter conditions and metric aggregates into queries #2340 Let the adapter turn filter conditions and metric aggregates into queries
  3. Configure filters in the LiveResource like fields #2431 Configure filters in the LiveResource like fields
  4. Describe the resource in the adapter and cast expressions with it #2448 Describe the resource in the adapter and cast expressions with it
  5. Read through a Backpex.Query #2449 Read through a Backpex.Query
  6. Apply the parts of a query an adapter doesn't run in memory #2450 Apply the parts of a query an adapter doesn't run in memory
  7. Write through neutral changes and let adapters declare writes #2451 Write through neutral changes and let adapters declare writes
  8. Replace item_query, :select and :custom_alias with scope, load and computed values #2454 Replace item_query, :select and :custom_alias with scope, load and computed values
  9. Compute aggregates of related items #2455 Compute aggregates of related items ← this PR

Aggregates over relationships to many items as computed values, the second part of the item_query replacement. It covers the joins of subqueries of related items, e.g. the number of the posts of a user or the author of the newest comment.

Backpex.Expr.aggregate/3

@impl Backpex.LiveResource
def computed(_assigns) do
  [
    post_count: aggregate(:count, :posts),
    published_count: aggregate(:count, :posts, where: expr(published == true)),
    total_likes: aggregate(:sum, :posts, value: expr(likes)),
    top_post: aggregate(:first, :posts, value: expr(title), sort: [likes: :desc]),
    last_commenter: aggregate(:first, [:posts, :comments], value: expr(author.name), sort: [inserted_at: :desc])
  ]
end

Kinds: :count, :sum, :avg, :min, :max and :first (after :sort). The path can go through several relationships ([:posts, :tags]), the value through relationships to one item (expr(author.name)). It lives in Backpex.Expr instead of Backpex.Query as in the design: it is an expression, and one function avoids clashes of count/min/max with Kernel on import Backpex.Expr.

  • Ecto: a left join of a subquery of the values grouped by the primary key of the items (DISTINCT ON for :first), shared by aggregates that only differ in their value (one column per value), so items are never duplicated; :count is 0 without related items. The index view sorts by and searches aggregates.
  • In memory: computed from the loaded relationships, which Backpex loads for adapters that don't run computed values.

Testing

On 607bbae:

  • mix test: 1530 passed (189 doctests, 1341 tests), including integration_test/options/computed_test.exs against PostgreSQL (Backpex.Test.UserStatsLive: count, count with where, sum, first with sort, first over posts.tags, sorting and search)
  • mix test --only e2e: 60 passed
  • mix test.coverage after both runs with --cover: 90.32 %
  • MIX_ENV=test mix lint: clean, mix docs --warnings-as-errors and mix sobelow --config clean
  • demo: bun run test 51 passed, bun run test:playwright 38 passed

On 8c5cce5 (shared joins, merged values of #2454; f392b8e only changes docs):

  • mix test: 1547 passed (190 doctests, 1357 tests)
  • mix test --only e2e: 60 passed
  • MIX_ENV=test mix lint: clean, mix docs --warnings-as-errors and mix sobelow --config clean

@codecov

codecov Bot commented Nov 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/backpex/adapters/ecto/where.ex 84.44% 7 Missing ⚠️
lib/backpex/expr.ex 78.57% 3 Missing ⚠️
lib/backpex/query/runtime.ex 72.72% 3 Missing ⚠️
lib/backpex/resource.ex 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

- Compute aggregates of related items
- Share one join for aggregates that only differ in their value
- Use the comments of the blog in the examples of aggregates
- Plan the joins of aggregates with the conditions of the search
@Flo0807
Flo0807 force-pushed the feature/relationship-aggregates branch from 8b36ca3 to 5b9e555 Compare November 13, 2026 09:46

This branch has not been deployed

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

Labels

breaking-change A breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant