# Aggregates Aggregates allow you to retrieve summary information over groups of related data, like counts, sums, or averages. Define aggregates in the `aggregates` block of a resource. Aggregates can work over relationships or directly over unrelated resources: ```elixir aggregates do # Related aggregates - use relationship path count :published_post_count, :posts do filter expr(published == true) end sum :total_sales, :orders, :amount exists :is_admin, :roles do filter expr(name == "admin") end # Unrelated aggregates - use resource module directly count :matching_profiles_count, Profile do filter expr(name == parent(name)) end sum :total_report_score, Report, :score do filter expr(author_name == parent(name)) end exists :has_reports, Report do filter expr(author_name == parent(name)) end end ``` For unrelated aggregates, use `parent/1` to reference fields from the source resource. ## Aggregate Types - **count**: Counts related items meeting criteria - **sum**: Sums a field across related items - **exists**: Returns boolean indicating if matching related items exist (also supports unrelated resources) - **first**: Gets the first related value matching criteria - **list**: Lists the related values for a specific field - **max**: Gets the maximum value of a field - **min**: Gets the minimum value of a field - **avg**: Gets the average value of a field ## Using Aggregates ```elixir # Using code interface options (preferred) users = MyDomain.list_users!( load: [:published_post_count, :total_sales], query: [ filter: [published_post_count: [greater_than: 5]], sort: [published_post_count: :desc] ] ) # Manual query building (for complex cases) User |> Ash.Query.filter(published_post_count > 5) |> Ash.read!() # Loading on existing records Ash.load!(users, :published_post_count) ``` ### Join Filters For complex aggregates involving multiple relationships, use join filters: ```elixir aggregates do sum :redeemed_deal_amount, [:redeems, :deal], :amount do # Filter on the aggregate as a whole filter expr(redeems.redeemed == true) # Apply filters to specific relationship steps join_filter :redeems, expr(redeemed == true) join_filter [:redeems, :deal], expr(active == parent(require_active)) end end ``` ## Inline Aggregates Use aggregates inline within expressions: ```elixir # Related inline aggregates calculate :grade_percentage, :decimal, expr( count(answers, query: [filter: expr(correct == true)]) * 100 / count(answers) ) # Unrelated inline aggregates calculate :profile_count, :integer, expr( count(Profile, filter: expr(name == parent(name))) ) calculate :stats, :map, expr(%{ profiles: count(Profile, filter: expr(active == true)), reports: count(Report, filter: expr(author_name == parent(name))), has_active_profile: exists(Profile, active == true and name == parent(name)) }) ```