Skip to main content

Stats & Reporting

Aggregations — counts, sums, top-K per group — are where an interpreted engine spends the most and where compilation pays off the most. ConjureDB plans the grouping and aggregate placement at build time.

Group and aggregate​

group key (pipeline) opens a scope: the inner pipeline runs once per key, and it is not itself a reduction — it may filter, sort, limit, project or aggregate.

Counting players per level is a keyed reduction, so the natural result surface is a map: the group key supplies K, the child pipeline supplies V.

query PlayerCountsByLevel() -> map<int, long> {
from Player p | group p.Level (aggregate count())
}

A row-shaped reduction names its outputs:

type LevelStat {
Players: long
AvgScore: double?
}

query LevelStats() -> map<int, LevelStat> {
from Player p
| group p.Level (
aggregate { Players = count(), AvgScore = avg(p.Score) }
)
}

count() is a non-nullable long and is zero on empty input. avg, sum, min and max are absent on empty input — double? here — and the declared type has to admit that rather than inventing a zero. A whole-table reduction drops the group and the name:

query TotalScore() -> long? {
from Player p | aggregate sum(p.Score)
}

Aggregate calls are legal only inside aggregate or an admitted window invocation. For a HAVING-style query, reduce first and then filter the named output.

Top-K per group with a window​

There is no window stage. A ranking is a named derive value inside a scoped group, ordered by that scope's sort:

type PlayerRank {
Level: int
Name: string
Score: int
Rank: long
}

query Top3PerLevel() -> PlayerRank[] {
from Player p
| group p.Level (
sort { -p.Score, p.Id }
| derive Rank = row_number()
| filter Rank <= 3
| select { Level = p.Level, Name = p.Name, Score = p.Score, Rank }
)
}

row_number, rank, dense_rank, lag and lead carry their own window role and take no frame. An aggregate used as a window value does need one — avg(p.Score) over rows(-2, 0).

Call it from C#​

using ConjureDB;

using var ctx = DbContextBuilder<GameDbContext>.Create()
.WithNoPersistence()
.Build();

var perLevel = ctx.LevelStats(); // map<int, LevelStat>
var podium = ctx.Top3PerLevel();

For the full catalog of aggregate and window functions, and the SQL ⇄ DSL mapping, see the Query Language reference and SQL reference & examples.