ash_postgres/lib/calculation.ex

115 lines
2.7 KiB
Elixir
Raw Normal View History

2022-01-14 08:11:30 +13:00
defmodule AshPostgres.Calculation do
@moduledoc false
require Ecto.Query
def add_calculations(query, [], _, _), do: {:ok, query}
2022-01-14 08:11:30 +13:00
def add_calculations(query, calculations, resource, source_binding) do
2022-01-14 08:11:30 +13:00
query = AshPostgres.DataLayer.default_bindings(query, resource)
{:ok, query} =
AshPostgres.Join.join_all_relationships(
query,
%Ash.Filter{
resource: resource,
expression: Enum.map(calculations, &elem(&1, 1))
},
left_only?: true
)
aggregates =
calculations
|> Enum.flat_map(fn {_calculation, expression} ->
used_calculations =
Ash.Filter.used_calculations(
2022-01-14 08:11:30 +13:00
expression,
query.__ash_bindings__.resource,
[]
2022-01-14 08:11:30 +13:00
)
AshPostgres.Aggregate.used_aggregates(
expression,
query.__ash_bindings__.resource,
used_calculations,
[]
)
2022-01-14 08:11:30 +13:00
end)
|> Enum.uniq()
case AshPostgres.Aggregate.add_aggregates(
query,
aggregates,
query.__ash_bindings__.resource,
false,
source_binding
) do
{:ok, query} ->
query =
if query.select do
query
else
Ecto.Query.select_merge(query, %{})
end
dynamics =
Enum.map(calculations, fn {calculation, expression} ->
type =
AshPostgres.Types.parameterized_type(
calculation.type,
Map.get(calculation, :constraints, [])
)
expr =
AshPostgres.Expr.dynamic_expr(
query,
expression,
query.__ash_bindings__,
false,
type
)
expr =
if type do
Ecto.Query.dynamic(type(^expr, ^type))
else
expr
end
{calculation.load, calculation.name, expr}
end)
{:ok, add_calculation_selects(query, dynamics)}
2022-01-14 08:11:30 +13:00
{:error, error} ->
{:error, error}
end
2022-01-14 08:11:30 +13:00
end
defp add_calculation_selects(query, dynamics) do
{in_calculations, in_body} =
2022-01-14 08:11:30 +13:00
Enum.split_with(dynamics, fn {load, _name, _dynamic} -> is_nil(load) end)
calcs =
in_body
|> Map.new(fn {load, _, dynamic} ->
{load, dynamic}
end)
calcs =
if Enum.empty?(in_calculations) do
calcs
2022-01-14 08:11:30 +13:00
else
Map.put(
calcs,
:calculations,
Map.new(in_calculations, fn {_, name, dynamic} ->
{name, dynamic}
end)
)
2022-01-14 08:11:30 +13:00
end
Ecto.Query.select_merge(query, ^calcs)
2022-01-14 08:11:30 +13:00
end
end