A chart draws data of any of these forms:
- a list of maps, with atom or string keys;
- a map of
:columnsand:rows, as a query result has them (Postgrex.Result,MyXQL.Result, Lotus's results), with atom or string keys; - an Ecto query or schema with its repo,
Fude.query(queryable, repo), which the database groups and aggregates.
The values become what the browser draws: a Decimal a number; a
Date, NaiveDateTime or DateTime a time (a date at its midnight, in
UTC); nil a gap; anything else a category, by its text. No value has to
be cast for the browser.
Only the fields that a chart draws are read: a query of thirty columns for a chart of two reads two.
In memory
The server makes the chart from the rows: it groups and aggregates them,
puts them in bins, stacks them, and sends the browser only what it
draws. 200,000 rows take about 0.2 seconds (mix run bench/compile.exs).
In the database
from(o in Order, where: o.shop_id == ^shop.id)
|> Fude.query(Repo)
|> Fude.line(x: {:month, :placed_at}, y: {:sum, :total}, color: :channel)For each layer, one query groups by the channels that are not aggregated (a field, its unit of time, its bin) and aggregates the others, in a subquery so that each database groups by columns. A histogram reads the least and the greatest value first, for its bins. PostgreSQL, MySQL and SQLite run them, each with its own functions of dates; their results are the same as in memory (the tests run on the three).
A layer with no aggregate (a scatter of points), a median, or small
multiples read the fields the chart draws instead, at most :limit rows
(100,000 when not given):
Fude.query(Order, Repo, limit: 20_000, repo_opts: [prefix: "tenant_42"])The query keeps its own where and join; its order_by is dropped for
the groups, which come in the order of their keys.
Masu's grids
A grid of Masu gives its filtered rows as an Ecto query
(Masu.Live.selected/1 with every row selected): a chart of what the
grid shows is a chart of that query.
{:query, query} = Masu.Live.selected("orders")
Fude.bar(Fude.query(query, Repo), x: :status, y: {:count, :id})Several datasets
A chart names its data (data: "sales", "data" when not given), so
that a saved spec gets it again when it draws:
<.chart id="revenue" spec={@report.chart} data={@sales} />