diff --git a/lib/aggregate.ex b/lib/aggregate.ex index 972ebad..a24ee81 100644 --- a/lib/aggregate.ex +++ b/lib/aggregate.ex @@ -137,7 +137,12 @@ defmodule AshSql.Aggregate do result = remaining_aggregates |> Enum.group_by(fn aggregate -> - {aggregate.relationship_path || [], aggregate.resource, aggregate.join_filters || %{}, + expanded_path = + aggregate.resource + |> AshSql.Join.relationship_path_to_relationships(aggregate.relationship_path) + |> Enum.map(& &1.name) + + {expanded_path || [], aggregate.resource, aggregate.join_filters || %{}, aggregate.query.action.name} end) |> Enum.flat_map(fn {{path, resource, join_filters, read_action}, aggregates} -> diff --git a/lib/join.ex b/lib/join.ex index 0a28be5..e1c3b4a 100644 --- a/lib/join.ex +++ b/lib/join.ex @@ -331,7 +331,15 @@ defmodule AshSql.Join do raise "no such relationship #{inspect(resource)}.#{name}" end - relationship_path_to_relationships(relationship.destination, rest, [relationship | acc]) + case Map.get(relationship, :through) do + through_path when is_list(through_path) -> + relationships = relationship_path_to_relationships(resource, through_path, []) + destination = Map.get(List.last(relationships) || relationship, :destination) + relationship_path_to_relationships(destination, rest, Enum.reverse(relationships) ++ acc) + + _ -> + relationship_path_to_relationships(relationship.destination, rest, [relationship | acc]) + end end def related_subquery( @@ -491,9 +499,18 @@ defmodule AshSql.Join do ecto_query end + resource_for_prefix = + case relationship do + %{type: :many_to_many, through: through} when not is_list(through) -> + through + + _ -> + relationship.destination + end + {:ok, ecto_query - |> set_join_prefix(query, Map.get(relationship, :through, relationship.destination)) + |> set_join_prefix(query, resource_for_prefix) |> Ecto.Query.exclude(:select)} {:error, error} -> diff --git a/lib/query.ex b/lib/query.ex index 1a1a087..4204e17 100644 --- a/lib/query.ex +++ b/lib/query.ex @@ -143,21 +143,39 @@ defmodule AshSql.Query do |> case do {:ok, lateral_join_source_query} -> lateral_join_source_query = - if Enum.count(path) == 2 do + if Enum.count(path) >= 2 do + through_entries = Enum.drop(path, 1) + Map.update!(lateral_join_source_query, :__ash_bindings__, fn bindings -> - bindings - |> Map.put(:lateral_join_bindings, [start_bindings + 1]) - |> Map.update!(:bindings, fn bindings -> - Map.put( - bindings, - start_bindings + 1, - %{ - source: path |> Enum.at(1) |> elem(3) |> Map.get(:source), - path: [path |> Enum.at(1) |> elem(3) |> Map.get(:name)], - type: :inner - } - ) - end) + {updated_bindings, _} = + Enum.reduce(through_entries, {bindings, start_bindings + 1}, fn entry, + {acc_bindings, + binding_idx} -> + rel = elem(entry, 3) + + acc_bindings = + acc_bindings + |> Map.update( + :lateral_join_bindings, + [binding_idx], + &(&1 ++ [binding_idx]) + ) + |> Map.update!(:bindings, fn b -> + Map.put( + b, + binding_idx, + %{ + source: Map.get(rel, :source), + path: [Map.get(rel, :name)], + type: :inner + } + ) + end) + + {acc_bindings, binding_idx + 1} + end) + + updated_bindings end) else lateral_join_source_query