ORM Aggregate and Group Guide
Why aggregate and group matter
Many backend queries do not need raw rows only. They need derived results such as counts, sums, averages, grouped totals, or grouped summaries attached to related records.
Vona ORM provides aggregate and group operations as typed model-level capabilities rather than forcing every summary query into ad hoc SQL.
count
The simplest aggregate is count.
Representative pattern:
const total = await this.scope.model.post.count();A count operation can still depend on:
columndistinctwherejoins
So even the simplest summary query still participates in the wider ORM query model.
aggregate
Use aggregate when the query should return one summary object.
Representative pattern:
const result = await this.scope.model.post.aggregate({
aggrs: {
count: ['*', 'stars'],
sum: 'stars',
avg: 'stars',
min: 'stars',
max: 'stars',
},
});A useful mental model is:
aggrsdeclares which aggregate functions should run- Vona infers the returned shape from the aggregate declaration
aggregatereturns one summary object rather than grouped rows
Representative parameter areas include:
aggrsdistinctwherejoins
group
Use group when the query should return grouped rows instead of one summary object.
Representative pattern:
const result = await this.scope.model.post.group({
groups: 'userId',
aggrs: {
count: '*',
sum: 'stars',
},
});Representative parameter areas include:
groupscolumnsaggrsdistinctwherejoinslimitoffsethavingorders
This matters because grouped results are still part of the structured ORM query language, not a separate unmanaged SQL world.
A practical result-shape distinction is:
aggregatereturns one summary objectgroupreturns grouped rows keyed by the chosen group columns and aggregate aliases
having and grouped ordering
Grouped queries often need filtering and ordering on derived fields.
Representative pattern:
const result = await this.scope.model.post.group({
groups: 'userId',
aggrs: {
count: '*',
sum: 'stars',
},
having: {
count_all: {
_gt_: 20,
},
sum_stars: {
_gt_: 30,
_lt_: 50,
},
},
orders: [['count_all', 'desc']],
});This is one of the reasons aggregate/group deserves dedicated documentation instead of being hidden inside a basic select page.
A practical alias rule is:
- aggregate aliases such as
count_allandsum_starsbecome the names used inhavingand grouped ordering
That is also why grouped or summary outputs often deserve explicit DTO treatment once they become stable API contracts.
Aggregate and group on relations
Aggregate and group are not limited to top-level model queries. They can also participate in relations.
Dynamic relation example
Representative aggregate-on-relation pattern:
const users = await this.scope.model.user.select({
with: {
posts: $relationDynamic.hasMany('test-vona:post', 'userId', {
aggrs: {
count: '*',
sum: 'stars',
},
}),
},
});Representative group-on-relation pattern:
const users = await this.scope.model.user.select({
with: {
posts: $relationDynamic.hasMany('test-vona:post', 'userId', {
groups: 'id',
aggrs: {
count: '*',
sum: 'stars',
},
}),
},
});Static relation example
Representative static relation pattern:
@Model({
entity: EntityUser,
relations: {
posts: $relation.hasMany('test-vona:post', 'userId', {
aggrs: {
count: '*',
sum: 'stars',
},
}),
},
})
class ModelUserStats {}That means summary-shaped relations can still be expressed through model metadata instead of living outside the ORM relation system.
Relationship to select and relations
Read this guide together with:
A practical split is:
- use the select guide for row-oriented query structure and operators
- use this guide when the result shape is aggregate- or group-oriented
- use the relations guide when the summary is attached through relation metadata or dynamic relations
Implementation checks for aggregate and grouped query changes
When editing summary-oriented backend queries, ask:
- is this a row-oriented select, a one-object aggregate, or a grouped result set?
- should the summary live at the top level or inside a relation?
- do
havingand derived-field ordering belong in the group definition? - should DTO inference or OpenAPI output reflect the summary shape explicitly?
That helps AI keep statistical and reporting queries aligned with Vona’s typed ORM model.