106 lines
2.9 KiB
Markdown
106 lines
2.9 KiB
Markdown
# 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))
|
|
})
|
|
```
|