-
Notifications
You must be signed in to change notification settings - Fork 0
Aggregates and Hyperfunctions
The aggregate map in timeBucket takes resultName: { fn: "column", …options }. Aggregate columns are checked against the model's scalar fields at compile time (avg/sum/min/max and the numeric hyperfunctions require numeric columns), and each aggregate's result type is inferred.
Supported: avg, sum, min, max, count, stddev, stddevPop, variance, varPop, histogram, percentile, rate, delta, timeWeightedAverage, candlestick, stats, first, last.
aggregate: {
avgTemp: { avg: "temperature" },
spread: { stddev: "temperature" }, // stddevPop / variance / varPop too — numeric like avg
devices: { count: "deviceId", distinct: true }, // count(DISTINCT deviceId)
dist: { histogram: "temperature", min: 0, max: 100, buckets: 10 },
}-
avg/sum/min/max/count— the basics. -
stddev/stddevPop/variance/varPop— sample & population statistics, numeric likeavg(default JSnumber;as: "string"for the exact decimal). -
counttakesdistinct: trueforcount(DISTINCT col). -
histogramreturns anumber[]ofbuckets + 2counts — the first and last entries are the below-min/ above-maxoverflow bins. Takes noas/fill.
first / last return the value of one column at the earliest / latest time in each bucket (first(value, time) / last(value, time)) — e.g. open/close per hour:
aggregate: {
open: { first: "temperature" }, // value at the earliest time in the bucket
close: { last: "temperature" }, // value at the latest time
tag: { last: "label" }, // any column type — keeps the column's type
}- Ordered by the model's time column by default; pass
by: "<field>"to order by another column. - Accept any column and keep that column's type. Take no
as/fill;nullin empty buckets undergapfill.
By default every aggregate comes back as a JS number — sum/avg are computed as double precision, so a magnitude past 2^53 loses precision. Opt an individual aggregate into an exact type:
aggregate: {
total: { sum: "deviceId", as: "bigint" }, // → bigint (exact integer)
samples: { count: "deviceId", as: "bigint" }, // → bigint (no overflow past ~2.1B rows)
exact: { avg: "temperature", as: "string" }, // → string (exact decimal)
fast: { sum: "temperature" }, // → number (default)
}-
as: "bigint"→ Postgres::bigint, a native JSbigint. Not available onavg(fractional). -
as: "string"→ Postgres::text, an exact decimalstring(JSON-safe). Not available oncount. -
as: "number"(default) → JSnumber.
The result-row type follows as, and disallowed combinations (avg + bigint, count + string) are compile errors.
percentile, rate, delta, timeWeightedAverage, candlestick, and stats take no as / fill and require the timescaledb_toolkit extension — present on Tiger Cloud and the timescale/timescaledb-ha image, but not in the slim timescaledb image.
aggregate: {
p95: { percentile: "latency", p: 0.95 }, // approximate p95 (number)
reqRate: { rate: "requestsTotal" }, // per-second rate of a reset-aware counter
reqDelta:{ delta: "requestsTotal" }, // total change over the bucket
twAvg: { timeWeightedAverage: "gauge" }, // time-weighted average (LOCF; or method: "linear")
}-
percentile— approximate percentile (pis the fraction in[0, 1]), viaapprox_percentile/percentile_agg. Returns anumber. -
rate(per-second) anddelta(total change) wrapcounter_aggfor monotonic counters — both reset-aware (a counter resetting to a lower value is handled). Return anumber. -
timeWeightedAverage—average(time_weight(...)), weighting each value by how long it held (ideal for irregularly-sampled gauges).methodis"locf"(default) or"linear". Returns anumber.
candlestick returns an object { open, high, low, close, vwap } per bucket — it takes a price (candlestick) and a volume column (so it suits a model that has both). Not combinable with gapfill.
// on a Trade model with `time`, `price`, and `volume` columns:
const candles = await prisma.trade.timeBucket({
bucket: "1 day",
range: { start, end },
aggregate: { ohlc: { candlestick: "price", volume: "volume" } },
});
// candles[i].ohlc -> { open, high, low, close, vwap }stats returns the Toolkit stats_agg 1-D statistical summary as a single object per bucket — { average, sum, numVals, stddev, variance, skewness, kurtosis }. Note that, unlike the standalone stddev / variance ops (which are sample statistics), the stddev and variance here are population (the Toolkit default) — equivalent to stddevPop / varPop. skewness and kurtosis have no other path in the package, and stats_agg rolls up in continuous aggregates. Not combinable with gapfill.
const [row] = await prisma.sensorReading.timeBucket({
bucket: "1 hour",
range: { start, end },
groupBy: ["deviceId"],
aggregate: { temp: { stats: "temperature" } },
});
// row.temp -> { average, sum, numVals, stddev, variance, skewness, kurtosis }