Current section
Files
Jump to
Current section
Files
lib/quack_db/ecto/analytics.ex
if Code.ensure_loaded?(Ecto.Query.API) do
defmodule QuackDB.Ecto.Analytics do
@moduledoc """
DuckDB analytical expression helpers for Ecto queries.
These macros wrap DuckDB-specific SQL functions in `Ecto.Query.API.fragment/1`
so analytical queries can stay in Ecto's query DSL instead of scattering raw
fragment strings across application code.
import Ecto.Query
import QuackDB.Ecto.Analytics
from(event in "events",
group_by: event.category,
select: %{
category: event.category,
median_score: median(event.score),
p95_score: quantile_cont(event.score, 0.95),
scores: duckdb_list(event.score)
}
)
Helpers only build expressions. DuckDB clauses that are not representable in
Ecto's AST, such as `PIVOT`, `UNPIVOT`, `QUALIFY`, `GROUPING SETS`, and
`USING SAMPLE`, should still be sent as raw SQL.
"""
defmacro median(expression) do
quote do
fragment("median(?)", unquote(expression))
end
end
defmacro quantile_cont(expression, quantile) do
quote do
fragment("quantile_cont(?, ?)", unquote(expression), unquote(quantile))
end
end
defmacro quantile_disc(expression, quantile) do
quote do
fragment("quantile_disc(?, ?)", unquote(expression), unquote(quantile))
end
end
defmacro duckdb_list(expression) do
quote do
fragment("list(?)", unquote(expression))
end
end
defmacro string_agg(expression, separator) do
quote do
fragment("string_agg(?, ?)", unquote(expression), unquote(separator))
end
end
defmacro arg_max(argument, value) do
quote do
fragment("arg_max(?, ?)", unquote(argument), unquote(value))
end
end
defmacro arg_min(argument, value) do
quote do
fragment("arg_min(?, ?)", unquote(argument), unquote(value))
end
end
defmacro json_extract(expression, path) do
quote do
fragment("json_extract(?, ?)", unquote(expression), unquote(path))
end
end
defmacro json_extract_string(expression, path) do
quote do
fragment("json_extract_string(?, ?)", unquote(expression), unquote(path))
end
end
defmacro date_trunc(part, timestamp) do
quote do
fragment("date_trunc(?, ?)", unquote(part), unquote(timestamp))
end
end
defmacro time_bucket(interval, timestamp) do
quote do
fragment("time_bucket(?::INTERVAL, ?)", unquote(interval), unquote(timestamp))
end
end
end
end