Aggregates
Aggregate functions are exported from rado and combine with groupBy and
having for summarizing data.
import {avg, count, countDistinct, max, min, sum} from 'rado'
count
// All rows
const [total] = await db.select(count()).from(User)
// Non-null values of a column
const [withEmail] = await db.select(count(User.email)).from(User)
// Distinct values
const [authors] = await db.select(countDistinct(Post.authorId)).from(Post)
count returns a number. For the common "count with a filter" case there's
also the shortcut db.$count(Table, condition?).
sum and avg
const [totalViews] = await db.select(sum(Post.views)).from(Post)
const [avgViews] = await db.select(avg(Post.views)).from(Post)
Note the return type: string | null. Sums and averages can exceed the safe
integer range or carry precision, so the database's exact answer is returned
as a string. Convert explicitly when you know it's safe, or use mapWith:
const [avgViews] = await db.select(avg(Post.views).mapWith(Number)).from(Post)
// number | null
sumDistinct and avgDistinct aggregate distinct values only.
min and max
const [newest] = await db.select(max(Post.createdAt)).from(Post)
const [first] = await db.select(min(User.id)).from(User)
These preserve the column's type (Sql<T>).
Grouping
const perAuthor = await db
.select({
authorId: Post.authorId,
posts: count(),
totalViews: sum(Post.views)
})
.from(Post)
.groupBy(Post.authorId)
Filter groups with having (where where filters rows, having filters
groups):
import {gt} from 'rado'
const prolific = await db
.select({authorId: Post.authorId, posts: count()})
.from(Post)
.groupBy(Post.authorId)
.having(gt(count(), 5))
Aggregating into JSON
To collect grouped values into an array instead of collapsing them, see Include. You can also reach for the JSON aggregation functions directly:
import {jsonAggregateArray} from 'rado'
const titlesPerAuthor = await db
.select({
authorId: Post.authorId,
titles: jsonAggregateArray(Post.title)
})
.from(Post)
.groupBy(Post.authorId)
jsonAggregateArray compiles to the right function per dialect
(json_group_array, jsonb_agg or json_arrayagg).
Arbitrary functions
Any SQL function, whether built in or extension-provided, can be called through the
Functions proxy:
import {Functions, sql} from 'rado'
const {coalesce} = Functions
await db
.select({
name: coalesce(User.name, sql.inline('anonymous'))
})
.from(User)
The result is typed Sql<any>; cast or wrap with a typed
sql expression when you want a precise type:
const len = sql`length(${User.name})`