Skip to main content

Aggregations

Compute summary values across your data with aggregate() and annotate().

Aggregate Functions

FunctionDescriptionSQL
Count("field")Count rowsCOUNT(field)
Sum("field")Sum valuesSUM(field)
Avg("field")AverageAVG(field)
Min("field")MinimumMIN(field)
Max("field")MaximumMAX(field)
RawAgg("SQL")Custom SQLRaw expression

aggregate() — Single Result

Returns a single dictionary with computed values:

from ryx import Count, Sum, Avg, Min, Max

stats = await Post.objects.aggregate(
total_posts=Count("id"),
total_views=Sum("views"),
avg_views=Avg("views"),
top_views=Max("views"),
min_views=Min("views"),
)

print(stats)
# → {
# 'total_posts': 150,
# 'total_views': 45000,
# 'avg_views': 300.0,
# 'top_views': 5000,
# 'min_views': 0,
# }

annotate() — Per-Row Values

Adds computed fields to each row in the result:

posts = await Post.objects.annotate(
comment_count=Count("comments.id"),
avg_rating=Avg("ratings.score"),
)

for post in posts:
print(f"{post.title}: {post['comment_count']} comments")

values() + annotate() = GROUP BY

Combine values() with annotate() for grouped aggregations:

# Posts per author
by_author = await (
Post.objects
.values("author_id")
.annotate(post_count=Count("id"), total_views=Sum("views"))
)

for row in by_author:
print(f"Author {row['author_id']}: {row['post_count']} posts, {row['total_views']} views")

This generates:

SELECT "author_id", COUNT("id") AS "post_count", SUM("views") AS "total_views"
FROM "posts"
GROUP BY "author_id"

Distinct Aggregations

# Count unique authors
unique_authors = await Post.objects.aggregate(
authors=Count("author_id", distinct=True),
)

Raw Aggregations

For database-specific functions:

from ryx import RawAgg

result = await Post.objects.aggregate(
percentile=RawAgg("PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY views)"),
)

Next Steps

Values & Annotate — Advanced GROUP BY patterns