Skip to content

Commit

Permalink
fix: properly join to related references in relationship filters
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Oct 24, 2023
1 parent 920bf6a commit 5230204
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 7 deletions.
63 changes: 57 additions & 6 deletions lib/join.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ defmodule AshPostgres.Join do
other
end)
|> Ash.Filter.relationship_paths()
|> to_joins(filter)
|> to_joins(filter, query.__ash_bindings__.resource)

true ->
filter
|> Ash.Filter.relationship_paths()
|> to_joins(filter)
|> to_joins(filter, query.__ash_bindings__.resource)
end

Enum.reduce_while(relationship_paths, {:ok, query}, fn
Expand Down Expand Up @@ -128,25 +128,45 @@ defmodule AshPostgres.Join do
end)
end

defp to_joins(paths, filter) do
defp to_joins(paths, filter, resource) do
paths
|> Enum.map(fn path ->
if can_inner_join?(path, filter) do
{:inner,
AshPostgres.Join.relationship_path_to_relationships(
filter.resource,
resource,
path
)}
else
{:left,
AshPostgres.Join.relationship_path_to_relationships(
filter.resource,
resource,
path
)}
end
end)
end

# defp expand_join_paths(joins) do
# Enum.flat_map(joins, fn {type, path} ->
# path
# |> sub_paths()
# |> Enum.map(&add_relationship_filter_paths/1)
# end)
# end

# defp add_relationship_filter_paths(path) do
# last = List.last(path)
# prefix = :lists.droplast(path)

# end

# defp sub_paths(path) do
# Enum.map(1..Enum.count(path), fn i ->
# Enum.take(path, i)
# end)
# end

def relationship_path_to_relationships(resource, path, acc \\ [])
def relationship_path_to_relationships(_resource, [], acc), do: Enum.reverse(acc)

Expand Down Expand Up @@ -304,7 +324,6 @@ defmodule AshPostgres.Join do
AshPostgres.Expr.dynamic_expr(query, filter, bindings, true)
end

{:ok, query} = join_all_relationships(query, filter)
from(row in query, where: ^dynamic)
end

Expand Down Expand Up @@ -609,6 +628,22 @@ defmodule AshPostgres.Join do
})
|> AshPostgres.DataLayer.add_binding(binding_data)

{:ok, related_filter} =
Ash.Filter.hydrate_refs(
relationship.filter,
%{
resource: relationship.destination,
aggregates: %{},
calculations: %{},
public?: false
}
)

related_filter =
Ash.Filter.move_to_relationship_path(related_filter, full_path)

{:ok, query} = join_all_relationships(query, related_filter)

used_calculations =
Ash.Filter.used_calculations(
filter,
Expand Down Expand Up @@ -745,6 +780,22 @@ defmodule AshPostgres.Join do

query = AshPostgres.DataLayer.add_binding(query, binding_data)

{:ok, related_filter} =
Ash.Filter.hydrate_refs(
relationship.filter,
%{
resource: relationship.destination,
aggregates: %{},
calculations: %{},
public?: false
}
)

related_filter =
Ash.Filter.move_to_relationship_path(related_filter, full_path)

{:ok, query} = join_all_relationships(query, related_filter)

used_calculations =
Ash.Filter.used_calculations(
filter,
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
%{
"ash": {:hex, :ash, "2.15.18", "abc793ce246cde5b68a881a4fb74237498f375dd146bb997a03bd91b96e1900f", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 1.1.47 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cb33816af2b924db832ee36aefc7d17019bd822793ece44f7356780496a72dd7"},
"ash": {:hex, :ash, "2.15.20", "0702181ca817ab1cad5e3ccb53851c56a9c8ec0a010780ad7eb881a997151e38", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:spark, ">= 1.1.47 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efa3afe081ed22f1c7d6dc45de13b8b80f8ab831ebaa255108aad11cacd23b13"},
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
Expand Down
6 changes: 6 additions & 0 deletions test/filter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -969,4 +969,10 @@ defmodule AshPostgres.FilterTest do
|> Api.read!()
end
end

test "can reference related items from a relationship expression" do
Post
|> Ash.Query.filter(comments_with_high_rating.title == "foo")
|> Api.read!()
end
end
4 changes: 4 additions & 0 deletions test/support/resources/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ defmodule AshPostgres.Test.Post do
manual(AshPostgres.Test.Post.CommentsContainingTitle)
end

has_many :comments_with_high_rating, AshPostgres.Test.Comment do
filter(expr(ratings.score > 5))
end

has_many(:ratings, AshPostgres.Test.Rating,
destination_attribute: :resource_id,
relationship_context: %{data_layer: %{table: "post_ratings"}}
Expand Down

0 comments on commit 5230204

Please sign in to comment.