Documentation
¶
Overview ¶
Package trends computes time-series — how many times an event happened per day (optionally unique users), the third core analysis primitive alongside funnels and retention. Deterministic and storage-agnostic.
Index ¶
- func FiniteSeries(s []MeasureSeries) bool
- func MedianOf(vals []float64) float64
- func NumericProps(events []event.Event) []string
- func Percentile(vals []float64, frac float64) float64
- type Interval
- type Measure
- type MeasurePoint
- type MeasureResult
- type MeasureSeries
- type Point
- type Result
- type Series
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FiniteSeries ¶ added in v0.9.3
func FiniteSeries(s []MeasureSeries) bool
FiniteSeries is Finite for a measure-breakdown result.
func MedianOf ¶ added in v0.10.0
MedianOf is the engine's ONE median: arithmetic mean of the two central values for even n. Exported for the same reason as Percentile — anything reporting a "median" (the Median measure, agent conversation turns) must share this definition, or the same word means two numbers.
func NumericProps ¶ added in v0.9.0
NumericProps returns the property names that carry at least one numeric value across the events — the columns a measure (sum/avg/p90) can aggregate. Sorted for determinism. Lets the ask bar resolve "revenue" to a real numeric property, or say honestly that none exists.
func Percentile ¶ added in v0.10.0
Percentile is the nearest-rank percentile of vals at fraction frac (0..1). Empty input is 0 (no data), matching applyMeasure's empty-slice contract. This is the EXACT computation the p90/p95/p99 measures use, exported so other packages (agent tool-call + conversation health) share ONE percentile definition instead of reimplementing the tail math and drifting from it.
Types ¶
type Interval ¶ added in v0.9.1
type Interval string
Interval is a bucketing grain for the series.
func ParseInterval ¶ added in v0.9.1
ParseInterval maps a request string to a grain; empty = day. Unknown grains are an error, never silently day — a wrong-grain chart is a silent-wrong answer.
type Measure ¶ added in v0.9.0
type Measure string
Measure is a numeric aggregation over an event property — the money/growth questions Count can't answer: revenue (sum of "amount"), average order value (avg), p90 latency, min/max. This is the single most common "it can't do X" a new user hits on day one.
func ParseMeasure ¶ added in v0.9.0
ParseMeasure maps a string (query param, MCP arg) to a Measure, defaulting to Sum for a bare/unknown value so a caller that asks for a numeric aggregation always gets one.
type MeasurePoint ¶ added in v0.9.0
type MeasurePoint struct {
Date time.Time `json:"date"`
Value float64 `json:"value"`
N int `json:"n"`
}
MeasurePoint is one day's aggregated numeric value. N is how many events contributed — 0 marks an empty day so avg/median/p90 read as "no data", not a real zero.
type MeasureResult ¶ added in v0.9.0
type MeasureResult struct {
Event string `json:"event"`
Property string `json:"property"`
Measure Measure `json:"measure"`
Points []MeasurePoint `json:"points"`
Total float64 `json:"total"`
N int `json:"n"` // total events that carried a numeric value
// Truncated: same meaning as Result.Truncated — the span cap trimmed the oldest buckets,
// so Points cover less of the window than Total/N do. Total stays the honest window
// aggregate (sum=1005) even when the rendered bars only add to 5; without this flag that
// 200x gap looked like the response contradicting itself for no stated reason.
Truncated bool `json:"truncated,omitempty"`
}
MeasureResult is the daily numeric series plus the aggregate over the WHOLE window (so Total for avg/median/p90 is correct, not a misleading mean-of-daily-means).
func ComputeMeasure ¶ added in v0.9.0
func ComputeMeasure(events []event.Event, eventName, property string, m Measure, from, to time.Time) MeasureResult
ComputeMeasure aggregates a numeric event property per day between from and to. Events missing the property, or whose value isn't numeric, are skipped (never coerced to 0). Deterministic and storage-agnostic, same as Compute.
func (MeasureResult) Finite ¶ added in v0.9.3
func (r MeasureResult) Finite() bool
Finite reports whether every aggregate in the result is a representable JSON number. Summing values near math.MaxFloat64 overflows to +Inf, which JSON cannot encode — callers turn that into an explicit "overflow" error instead of a broken response or a lied number.
type MeasureSeries ¶ added in v0.9.3
type MeasureSeries struct {
Value string `json:"value"`
Total float64 `json:"total"`
N int `json:"n"`
}
MeasureSeries is one breakdown group's aggregate — "revenue by plan" yields one row per plan value, with the same Total/N semantics as MeasureResult.
func ComputeMeasureBreakdown ¶ added in v0.9.3
func ComputeMeasureBreakdown(events []event.Event, eventName, property string, m Measure, breakdown string, from, to time.Time) []MeasureSeries
ComputeMeasureBreakdown aggregates a numeric property per value of a breakdown property — "sum of amount by plan". One row per group value (events missing the breakdown property fall into "(none)"), sorted by Total desc. Both GET /v1/trends and the MCP trends tool serialize this same result, so they can't disagree. The measure+breakdown combination used to be silently DROPPED: the un-broken grand total came back with no series and no error, presenting an unsegmented number as if it were the requested split.
type Result ¶
type Result struct {
Event string `json:"event"`
Unique bool `json:"unique"` // true = distinct users, false = raw count
Points []Point `json:"points"`
Total int `json:"total"`
// Truncated marks that the display cap (maxDayBuckets / the hourly 744) trimmed the OLDEST
// buckets: Points cover less than the requested window, Total still covers all of it. It is
// set rather than left implicit because a reader can otherwise only conclude that the number
// and the chart disagree — which is the same thing as not trusting either.
Truncated bool `json:"truncated,omitempty"`
}
Result is the daily series for one event.
func Compute ¶
Compute returns daily counts for eventName (empty = all events) between from and to. unique=true counts distinct users per day instead of raw events. Days with no activity are filled with zero so the line/bars are continuous.
func ComputeInterval ¶ added in v0.9.1
func ComputeInterval(events []event.Event, eventName string, from, to time.Time, unique bool, iv Interval) Result
ComputeInterval is Compute with a bucketing grain. Buckets with no activity fill with zero so the series is continuous. Hourly output is capped at 31 days of buckets (744) — the guardrail every incumbent applies to keep charts readable.
func ComputeXAU ¶ added in v0.9.1
ComputeXAU plots DAU/WAU/MAU as a daily series: each point = distinct users active in the rolling half-open window (point − windowDays, point], per the TRENDS-XAU contract (mixpanel XAU semantics; posthog words it "the N days leading up to the label"). The Total echoes the LAST point (the current value) — summing rolling actives would double-count meaninglessly.
type Series ¶
type Series struct {
Value string `json:"value"`
Points []Point `json:"points"`
Total int `json:"total"`
}
Series is one line of a broken-down trend (e.g. signups from "google" over time).
func ComputeBreakdown ¶
func ComputeBreakdown(events []event.Event, eventName, property string, from, to time.Time, unique bool) []Series
ComputeBreakdown splits a trend into one series per value of property — the multi-line "signups by source over time" report. Events missing the property fall into "(none)". Series are sorted by total descending.