mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-20 05:23:18 +12:00
fix: simplify aggregate bindings & calculation reference building
This commit is contained in:
parent
179cee4b24
commit
5cf9affff4
2 changed files with 38 additions and 120 deletions
|
@ -111,6 +111,15 @@ defmodule AshPostgres.Aggregate do
|
|||
{:cont, {:ok, query, [{aggregate.load, aggregate.name, exists} | dynamics]}}
|
||||
|
||||
true ->
|
||||
root_data_path =
|
||||
case root_data do
|
||||
{_, path} ->
|
||||
path
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
|
||||
with {:ok, agg_root_query} <-
|
||||
AshPostgres.Join.maybe_get_resource_query(
|
||||
first_relationship.destination,
|
||||
|
@ -166,7 +175,8 @@ defmodule AshPostgres.Aggregate do
|
|||
first_relationship,
|
||||
relationship_path,
|
||||
aggregates,
|
||||
source_binding
|
||||
source_binding,
|
||||
root_data_path
|
||||
) do
|
||||
if select? do
|
||||
new_dynamics =
|
||||
|
@ -459,7 +469,8 @@ defmodule AshPostgres.Aggregate do
|
|||
%{manual: {module, opts}} = first_relationship,
|
||||
_relationship_path,
|
||||
aggregates,
|
||||
source_binding
|
||||
source_binding,
|
||||
root_data_path
|
||||
) do
|
||||
field = first_relationship.destination_attribute
|
||||
|
||||
|
@ -496,7 +507,7 @@ defmodule AshPostgres.Aggregate do
|
|||
AshPostgres.DataLayer.add_binding(
|
||||
query,
|
||||
%{
|
||||
path: [],
|
||||
path: root_data_path,
|
||||
type: :aggregate,
|
||||
aggregates: aggregates
|
||||
}
|
||||
|
@ -510,7 +521,8 @@ defmodule AshPostgres.Aggregate do
|
|||
first_relationship,
|
||||
_relationship_path,
|
||||
aggregates,
|
||||
source_binding
|
||||
source_binding,
|
||||
root_data_path
|
||||
) do
|
||||
join_relationship_struct = Ash.Resource.Info.relationship(source, join_relationship)
|
||||
|
||||
|
@ -562,7 +574,7 @@ defmodule AshPostgres.Aggregate do
|
|||
AshPostgres.DataLayer.add_binding(
|
||||
query,
|
||||
%{
|
||||
path: [],
|
||||
path: root_data_path,
|
||||
type: :aggregate,
|
||||
aggregates: aggregates
|
||||
}
|
||||
|
@ -575,7 +587,8 @@ defmodule AshPostgres.Aggregate do
|
|||
first_relationship,
|
||||
_relationship_path,
|
||||
aggregates,
|
||||
source_binding
|
||||
source_binding,
|
||||
root_data_path
|
||||
) do
|
||||
field = first_relationship.destination_attribute
|
||||
|
||||
|
@ -604,7 +617,7 @@ defmodule AshPostgres.Aggregate do
|
|||
AshPostgres.DataLayer.add_binding(
|
||||
query,
|
||||
%{
|
||||
path: [],
|
||||
path: root_data_path,
|
||||
type: :aggregate,
|
||||
aggregates: aggregates
|
||||
}
|
||||
|
|
131
lib/expr.ex
131
lib/expr.ex
|
@ -765,8 +765,7 @@ defmodule AshPostgres.Expr do
|
|||
query,
|
||||
%Ref{
|
||||
attribute: %Ash.Query.Calculation{} = calculation,
|
||||
relationship_path: [],
|
||||
resource: resource
|
||||
relationship_path: relationship_path
|
||||
} = type_expr,
|
||||
bindings,
|
||||
embedded?,
|
||||
|
@ -781,6 +780,7 @@ defmodule AshPostgres.Expr do
|
|||
)
|
||||
|
||||
validate_type!(query, type, type_expr)
|
||||
resource = Ash.Resource.Info.related(bindings.resource, relationship_path)
|
||||
|
||||
case Ash.Filter.hydrate_refs(
|
||||
calculation.module.expression(calculation.opts, calculation.context),
|
||||
|
@ -792,6 +792,12 @@ defmodule AshPostgres.Expr do
|
|||
}
|
||||
) do
|
||||
{:ok, expression} ->
|
||||
expression =
|
||||
Ash.Filter.move_to_relationship_path(
|
||||
expression,
|
||||
relationship_path
|
||||
)
|
||||
|
||||
expression =
|
||||
Ash.Actions.Read.add_calc_context_to_filter(
|
||||
expression,
|
||||
|
@ -840,18 +846,6 @@ defmodule AshPostgres.Expr do
|
|||
)
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
_query,
|
||||
%Ref{
|
||||
attribute: %Ash.Resource.Calculation{} = calculation
|
||||
},
|
||||
_bindings,
|
||||
_embedded?,
|
||||
_type
|
||||
) do
|
||||
raise "cannot build expression from resource calculation! #{calculation.name}"
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
query,
|
||||
%Ref{attribute: %Ash.Query.Aggregate{} = aggregate} = ref,
|
||||
|
@ -876,7 +870,12 @@ defmodule AshPostgres.Expr do
|
|||
|
||||
{ref_binding, field_name, value} =
|
||||
if first_optimized_aggregate? do
|
||||
ref = %{ref | relationship_path: ref.relationship_path ++ aggregate.relationship_path}
|
||||
ref = %{
|
||||
ref
|
||||
| attribute: %Ash.Resource.Attribute{name: :fake},
|
||||
relationship_path: ref.relationship_path ++ aggregate.relationship_path
|
||||
}
|
||||
|
||||
ref_binding = ref_binding(ref, bindings)
|
||||
|
||||
if is_nil(ref_binding) do
|
||||
|
@ -912,19 +911,14 @@ defmodule AshPostgres.Expr do
|
|||
ref_binding = ref_binding(ref, bindings)
|
||||
|
||||
if is_nil(ref_binding) do
|
||||
IO.inspect(ref)
|
||||
IO.inspect(bindings)
|
||||
raise "Error while building reference: #{inspect(ref)}"
|
||||
end
|
||||
|
||||
{ref_binding, aggregate.name, nil}
|
||||
end
|
||||
|
||||
ref_binding =
|
||||
if ref.relationship_path == [] || first_optimized_aggregate? do
|
||||
ref_binding
|
||||
else
|
||||
ref_binding + 1
|
||||
end
|
||||
|
||||
expr =
|
||||
if value do
|
||||
value
|
||||
|
@ -964,85 +958,6 @@ defmodule AshPostgres.Expr do
|
|||
end
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
query,
|
||||
%Ref{
|
||||
attribute: %Ash.Query.Calculation{} = calculation,
|
||||
relationship_path: relationship_path
|
||||
} = ref,
|
||||
bindings,
|
||||
embedded?,
|
||||
_type
|
||||
) do
|
||||
binding_to_replace =
|
||||
Enum.find_value(bindings.bindings, fn {i, binding} ->
|
||||
if binding.path == relationship_path do
|
||||
i
|
||||
end
|
||||
end)
|
||||
|
||||
if is_nil(binding_to_replace) do
|
||||
raise """
|
||||
Error building calculation reference: #{inspect(relationship_path)} is not available in bindings.
|
||||
|
||||
In reference: #{ref}
|
||||
"""
|
||||
end
|
||||
|
||||
temp_bindings =
|
||||
bindings.bindings
|
||||
|> Map.delete(0)
|
||||
|> Map.update!(binding_to_replace, &Map.merge(&1, %{path: [], type: :root}))
|
||||
|
||||
type =
|
||||
AshPostgres.Types.parameterized_type(
|
||||
calculation.type,
|
||||
Map.get(calculation, :constraints, [])
|
||||
)
|
||||
|
||||
validate_type!(query, type, ref)
|
||||
|
||||
case Ash.Filter.hydrate_refs(
|
||||
calculation.module.expression(calculation.opts, calculation.context),
|
||||
%{
|
||||
resource: ref.resource,
|
||||
aggregates: %{},
|
||||
calculations: %{},
|
||||
public?: false
|
||||
}
|
||||
) do
|
||||
{:ok, hydrated} ->
|
||||
hydrated =
|
||||
Ash.Actions.Read.add_calc_context_to_filter(
|
||||
hydrated,
|
||||
calculation.context[:actor],
|
||||
calculation.context[:authorize?],
|
||||
calculation.context[:tenant],
|
||||
calculation.context[:tracer]
|
||||
)
|
||||
|
||||
expr =
|
||||
do_dynamic_expr(
|
||||
query,
|
||||
Ash.Filter.update_aggregates(hydrated, fn aggregate, _ ->
|
||||
%{aggregate | relationship_path: []}
|
||||
end),
|
||||
%{bindings | bindings: temp_bindings},
|
||||
embedded?,
|
||||
type
|
||||
)
|
||||
|
||||
if type do
|
||||
Ecto.Query.dynamic(type(^expr, ^type))
|
||||
else
|
||||
expr
|
||||
end
|
||||
|
||||
_ ->
|
||||
raise "Failed to hydrate references for #{inspect(ref.resource)} in #{inspect(calculation.module.expression(calculation.opts, calculation.context))}"
|
||||
end
|
||||
end
|
||||
|
||||
defp do_dynamic_expr(
|
||||
query,
|
||||
%Type{arguments: [arg1, arg2, constraints]},
|
||||
|
@ -1511,11 +1426,12 @@ defmodule AshPostgres.Expr do
|
|||
end
|
||||
|
||||
defp ref_binding(
|
||||
%{attribute: %Ash.Query.Aggregate{name: name}, relationship_path: []},
|
||||
%{attribute: %Ash.Query.Aggregate{name: name}, relationship_path: relationship_path},
|
||||
bindings
|
||||
) do
|
||||
Enum.find_value(bindings.bindings, fn {binding, data} ->
|
||||
data.type == :aggregate &&
|
||||
data.path == relationship_path &&
|
||||
Enum.any?(data.aggregates, &(&1.name == name)) && binding
|
||||
end)
|
||||
end
|
||||
|
@ -1531,17 +1447,6 @@ defmodule AshPostgres.Expr do
|
|||
end)
|
||||
end
|
||||
|
||||
defp ref_binding(%{attribute: %Ash.Query.Aggregate{}} = ref, bindings) do
|
||||
Enum.find_value(bindings.bindings, fn {binding, data} ->
|
||||
data.type in [:inner, :left, :root] &&
|
||||
Ash.SatSolver.synonymous_relationship_paths?(
|
||||
bindings.resource,
|
||||
data.path,
|
||||
ref.relationship_path
|
||||
) && binding
|
||||
end)
|
||||
end
|
||||
|
||||
defp do_get_path(
|
||||
query,
|
||||
%GetPath{arguments: [left, right], embedded?: pred_embedded?} = get_path,
|
||||
|
|
Loading…
Reference in a new issue