Translates a validated AshDyan.Request into an Ash.Query, runs it through
the resource's normal read action (so Ash policies apply), and aggregates the
result in memory into a chart-ready shape.
Design note
Ash's Ash.Query (3.x) does not expose a generic group_by builder, and the
shape of grouped aggregates is data-layer dependent. To keep AshDyan
data-layer agnostic, safe, and predictable, the engine:
- selects only the columns it needs (the metric column, the time field, the group_by fields, and the filter fields),
- applies the caller's filters and the configured
limit(a hard cap that prevents full-cardinality group-bys from blowing up the DB), - runs the query through the resource's read action — so
Ash.Policyauthorization applies unchanged, - aggregates the returned rows in memory into the stable
labels/seriesoutput shape.
This keeps the security boundary (the dyan DSL whitelist + enforced limits)
intact while avoiding data-layer-specific query shapes. Percentiles, in
particular, are computed in memory so they work on any data layer; the
capability check still surfaces data-layer limits explicitly via
AshDyan.supports?/2.
Summary
Functions
@spec build_query(AshDyan.Request.t(), [AshDyan.run_opt()]) :: {:ok, Ash.Query.t()} | {:error, term()}
Build an Ash.Query that selects exactly the columns needed for the request.
@spec run_query(Ash.Query.t(), AshDyan.Request.t(), [AshDyan.run_opt()]) :: {:ok, [Ash.Resource.Record.t()]} | {:error, term()}
Run a built query through the resource's read action.