Current section
Files
Jump to
Current section
Files
docs/datasets.md
# Datasets
Incant datasets describe analytical data: dimensions, metrics, default table views, heatmaps, and drilldowns. They are metadata-first. Data-source adapters own SQL generation and execution for QuackDB, Postgres, external APIs, or other analytical backends.
```elixir
defmodule MyApp.Admin.Datasets.CampaignPerformance do
use Incant.Dataset, source: MyApp.Analytics.QuackDB
title "Campaign Performance"
from "campaign_daily"
dimensions do
dimension :date, type: :date
dimension :source
dimension :campaign
dimension :ad_group
dimension :keyword
dimension :landing_page
end
metrics do
metric :impressions, :sum
metric :clicks, :sum
metric :orders, :sum
metric :cost, :sum, format: :money
metric :revenue, :sum, format: :money
metric :ctr, expr: "clicks / nullif(impressions, 0)", format: :percent
metric :cpa, expr: "cost / nullif(orders, 0)", format: :money
metric :roas, expr: "revenue / nullif(cost, 0)", format: :number
end
filters do
filter :date, :date_range
filter :campaign, :select, options: &MyApp.Admin.Options.campaigns/2
end
table density: :compact do
group_by [:campaign]
columns [:campaign, :clicks, :cost, :orders, :revenue, :cpa, :roas]
sort :cost, :desc
heatmap [:cost, :orders, :roas]
drilldown :campaign, group_by: [:ad_group], columns: [:ad_group, :clicks, :cost, :cpa]
drilldown :ad_group, group_by: [:keyword], columns: [:keyword, :clicks, :cost, :cpa]
end
end
```
Register datasets on an admin root:
```elixir
defmodule MyApp.Admin do
use Incant.Admin
dataset MyApp.Admin.Datasets.CampaignPerformance
end
```
Build and run normalized dataset queries through the configured source:
```elixir
query =
Incant.Dataset.query(MyApp.Admin.Datasets.CampaignPerformance,
filters: %{"range" => "30d"},
page: 1,
page_size: 50
)
{:ok, result} = Incant.Dataset.run(MyApp.Admin.Datasets.CampaignPerformance, filters: query.filters)
```
Data sources implement the `Incant.DataSource` query callback and receive `%Incant.Query{}` with `from`, dimensions, metrics, groupings, columns, filters, sort, pagination, variables, and context. Incant normalizes source rows into `%Incant.Result{}`.
Dataset filters use the same semantic controls as resource filters and are URL-persistent through `filter[...]` query params. The default LiveView adapter renders them in a right-side filter rail beside the dataset table.
Use atoms for closed app-owned enums and strings for external analytical values. UTM values, campaign names, referrers, provider model names, and other imported dimensions should stay strings; do not convert arbitrary external data to atoms.
Static options are fine for closed sets:
```elixir
filter :status, :select, options: [:draft, :active, :archived]
```
Analytical options can be dynamic:
```elixir
filter :campaign, :select, options: &MyApp.Admin.Options.campaigns/2
```
Option callbacks receive `%{source: filter}` and the current context, and should return a list of values or `{label, value}` pairs.
Drilldowns can switch grouping and columns through URL state:
```elixir
table do
group_by [:campaign]
columns [:campaign, :clicks, :cost]
drilldown :campaign, group_by: [:ad_group], columns: [:ad_group, :clicks, :cost]
end
```
The default LiveView adapter renders available drilldowns above the dataset table and passes the selected drilldown into `%Incant.Query{}`.
Chart binding, saved views, and deeper breadcrumb-style drilldown paths are planned follow-up work.