diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 2c2e066..e944651 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -552,12 +552,22 @@ defmodule AshPostgres.DataLayer do |> default_bindings(resource, context) case context[:data_layer][:lateral_join_source] do - {_, [{%{resource: resource}, _, _, _} | _]} -> + {_, [{%{resource: resource}, _, _, _} | rest]} -> parent = resource |> resource_to_query(nil) |> default_bindings(resource, context) + parent = + case rest do + [{resource, _, _, %{name: join_relationship_name}} | _] -> + binding_data = %{type: :inner, path: [join_relationship_name], source: resource} + add_binding(parent, binding_data) + + _ -> + parent + end + ash_bindings = data_layer_query.__ash_bindings__ |> Map.put(:parent_bindings, Map.put(parent.__ash_bindings__, :parent?, true)) diff --git a/lib/expr.ex b/lib/expr.ex index cb98881..2496dae 100644 --- a/lib/expr.ex +++ b/lib/expr.ex @@ -1055,14 +1055,15 @@ defmodule AshPostgres.Expr do type ) do parent? = Map.get(bindings.parent_bindings, :parent_is_parent_as?, true) + new_bindings = Map.put(bindings.parent_bindings, :parent?, parent?) do_dynamic_expr( %{ query - | __ash_bindings__: Map.put(bindings.parent_bindings, :parent?, parent?) + | __ash_bindings__: new_bindings }, expr, - bindings, + new_bindings, embedded?, type ) @@ -1607,7 +1608,8 @@ defmodule AshPostgres.Expr do end end - defp set_parent_path(query, parent) do + @doc false + def set_parent_path(query, parent) do # This is a stupid name. Its actually the path we *remove* when stepping up a level. I.e the child's path Map.update!(query, :__ash_bindings__, fn ash_bindings -> ash_bindings diff --git a/lib/sort.ex b/lib/sort.ex index 08ed7a7..92112ff 100644 --- a/lib/sort.ex +++ b/lib/sort.ex @@ -20,6 +20,7 @@ defmodule AshPostgres.Sort do %{ resource: resource, aggregates: %{}, + parent_stack: query.__ash_bindings__[:parent_resources] || [], calculations: %{}, public?: false } @@ -84,14 +85,30 @@ defmodule AshPostgres.Sort do |> Ash.Filter.hydrate_refs(%{ resource: resource, aggregates: query.__ash_bindings__.aggregate_defs, + parent_stack: query.__ash_bindings__[:parent_resources] || [], calculations: %{}, public?: false }) |> Ash.Filter.move_to_relationship_path(relationship_path) |> case do {:ok, expr} -> + bindings = + if query.__ash_bindings__[:parent_bindings] do + Map.update!(query.__ash_bindings__, :parent_bindings, fn parent -> + Map.put(parent, :parent_is_parent_as?, false) + end) + else + query.__ash_bindings__ + end + expr = - AshPostgres.Expr.dynamic_expr(query, expr, query.__ash_bindings__, false, type) + AshPostgres.Expr.dynamic_expr( + query, + expr, + bindings, + false, + type + ) {:cont, {:ok, query_expr ++ [{order, expr}]}} diff --git a/test/sort_test.exs b/test/sort_test.exs index 00a297f..44f2ad5 100644 --- a/test/sort_test.exs +++ b/test/sort_test.exs @@ -4,6 +4,8 @@ defmodule AshPostgres.SortTest do alias AshPostgres.Test.{Api, Comment, Post, PostLink} require Ash.Query + require Ash.Sort + import Ash.Expr test "multi-column sorts work" do Post @@ -181,4 +183,46 @@ defmodule AshPostgres.SortTest do |> Ash.Query.sort(:c_times_p) |> Api.read!() end + + test "calculations can sort on expressions" do + post1 = + Post + |> Ash.Changeset.new(%{title: "aaa", score: 0}) + |> Api.create!() + + post2 = + Post + |> Ash.Changeset.new(%{title: "bbb", score: 1}) + |> Api.create!() + + post3 = + Post + |> Ash.Changeset.new(%{title: "ccc", score: 0}) + |> Api.create!() + + PostLink + |> Ash.Changeset.new() + |> Ash.Changeset.manage_relationship(:source_post, post1, type: :append) + |> Ash.Changeset.manage_relationship(:destination_post, post3, type: :append) + |> Api.create!() + + PostLink + |> Ash.Changeset.new() + |> Ash.Changeset.manage_relationship(:source_post, post2, type: :append) + |> Ash.Changeset.manage_relationship(:destination_post, post2, type: :append) + |> Api.create!() + + PostLink + |> Ash.Changeset.new() + |> Ash.Changeset.manage_relationship(:source_post, post3, type: :append) + |> Ash.Changeset.manage_relationship(:destination_post, post1, type: :append) + |> Api.create!() + + posts_query = + Ash.Query.sort(Post, Ash.Sort.expr_sort(source(post_links.state))) + + Post + |> Ash.Query.load(linked_posts: posts_query) + |> Api.read!() + end end