# Aggregates and Reporting

> Use aggregate helpers, groupBy, having, and report-style selects

Package: Kunii
Canonical: https://kuratchi.dev/docs/kunii/aggregates-and-reporting
Markdown: https://kuratchi.dev/docs/kunii/aggregates-and-reporting.md

## Aggregate helpers

Aggregate terminals follow the same shape as `count()`: single column in, scalar out.

```ts
await db.orders.where({ status: 'paid' }).sum('amount'); // -> number | null
await db.orders.avg('amount');                           // -> number | null
await db.users.min('createdAt');                         // -> number | null
await db.users.max('createdAt');                         // -> number | null
```

JSON path columns work too:

```ts
await db.orders.sum('payload.amount');
```

## `groupBy` + `having` + object-form `select`

For reporting queries, `select` accepts an object form that projects plain columns alongside aggregates. Each entry key becomes the SQL alias.

```ts
db.posts
  .where({ published: 1 })
  .select({
    userId: true,
    total: { sum: 'views' },
    n: { count: '*' },
  })
  .groupBy('userId')                          // single col or array
  .having({ total: { gte: 1000 }, n: { gte: 5 } })
  .orderBy({ total: 'desc' })
  .limit(10)
  .many();
```

Aggregate types: `count`, `countDistinct`, `sum`, `avg`, `min`, `max`.

`having` reuses the full `where` vocabulary (`gt`, `gte`, `lt`, `lte`, `eq`, `ne`, `between`, `notBetween`, and so on) operating on the aliases from your select object.

## Read next

- [Querying](/docs/kunii/querying)
- [Relations](/docs/kunii/relations)
- [Writes and Transactions](/docs/kunii/writes-and-transactions)
