The runtime request spec for a dynamic analysis.
A caller sends a map (or a t/0 struct) describing what chart data they want.
normalize/1 fills in defaults and validate/1 checks it against the
resource's dyan DSL whitelist.
Shape
%{
domain: MyApp.Shop,
resource: MyApp.Order,
type: :time_bucket, # :frequency | :aggregate | :time_bucket | :percentile | :histogram
column: :total_amount,
function: :sum, # required for :aggregate
bucket: :day, # required for :time_bucket
time_field: :inserted_at,
group_by: [:status], # optional, checked against max_group_by
percentiles: [50, 90], # required for :percentile
bins: 10, # optional for :histogram (default 10)
bin_width: nil, # optional for :histogram (auto-computed if nil)
filters: %{status: "paid", region: ["EU", "US"]},
limit: 200,
# Presentation options (apply to every type):
sort_by: :value, # :value | :label
sort_order: :desc, # :asc | :desc
top: nil, # keep the N largest slices, roll the rest into "Other"
cumulative: false, # running totals (time_bucket)
normalize: nil # :percentage to convert series to share-of-total
}Filters
filters are parsed by Ash.Filter.parse/2, so they support the full range
of operators Ash understands on whitelisted fields — not just equality. For
example:
%{status: "paid"} # equality
%{region: ["EU", "US"]} # membership (in)
%{total_amount: %{gt: 100}} # comparison
%{inserted_at: %{between: [start, end]}} # rangeOnly fields declared in allow_filters_on may be filtered.
Summary
Functions
Normalize a request map into a t/0 struct with defaults applied.
Validate a normalized request against the resource's dyan DSL whitelist.
Types
@type analysis_type() ::
:frequency | :aggregate | :time_bucket | :percentile | :histogram
@type sort_by() :: :value | :label
@type sort_order() :: :asc | :desc
@type t() :: %AshDyan.Request{ bin_width: number() | nil, bins: pos_integer() | nil, bucket: AshDyan.time_bucket() | nil, column: atom() | nil, cumulative: boolean(), domain: module() | nil, filters: map(), function: AshDyan.aggregate_function() | nil, group_by: [atom()], limit: pos_integer() | nil, normalize: :percentage | nil, percentiles: [pos_integer()], resource: module(), sort_by: sort_by() | nil, sort_order: sort_order(), time_field: atom() | nil, top: pos_integer() | nil, type: analysis_type() }
Functions
Normalize a request map into a t/0 struct with defaults applied.
@spec validate(t()) :: :ok | {:error, AshDyan.Error.t()}
Validate a normalized request against the resource's dyan DSL whitelist.
Returns :ok or {:error, %AshDyan.Error{}} naming the offending field.
Error reasons
The reason field of the returned AshDyan.Error is one of:
:not_a_resource/:not_analyzable— the:resourceis invalid or has nodyanconfiguration.:unknown_type—:typeis not one of the capabilities.:not_analyzable—:column/time_fieldis not whitelisted for the type.:not_allowed—:function/:bucket/:percentilesis not in the whitelist for that field, or:filtersreferences a non-allowed field.:too_many—:group_byexceedsmax_group_by.:too_large—:limitexceedsmax_limit.:bad_bins—:bins/:bin_widthis of the wrong type or non-positive.:unknown_attribute—:group_byreferences a non-existent attribute.:bad_type—:filters/limitis of the wrong type.