fix: properly load manual to_one relationships

This commit is contained in:
Zach Daniel 2022-10-23 22:05:23 -06:00
parent 03482cf908
commit 5e3b71e79b
5 changed files with 77 additions and 5 deletions

View file

@ -230,7 +230,11 @@ defmodule Ash.Actions.Load do
defp attach_to_one_loads(value, %{name: name, no_attributes?: true}, data, lead_path) do
map_or_update(data, lead_path, fn record ->
Map.put(record, name, value |> List.wrap() |> Enum.at(0))
if is_map(data) do
Map.put(record, name, value |> List.wrap() |> Enum.at(0) |> elem(1))
else
Map.put(record, name, value |> List.wrap() |> Enum.at(0))
end
end)
end
@ -248,7 +252,7 @@ defmodule Ash.Actions.Load do
map_or_update(data, lead_path, fn record ->
related =
Enum.filter(value, fn {key, _value} ->
Enum.find_value(value, fn {key, value} ->
key =
if is_map(key) || !single_primary_key do
key
@ -257,10 +261,16 @@ defmodule Ash.Actions.Load do
end
if last_relationship.source.primary_key_matches?(record, key) do
value
{:ok, value}
end
end)
|> List.wrap()
|> case do
{:ok, value} ->
value
_ ->
nil
end
Map.put(
record,

View file

@ -122,7 +122,7 @@ defmodule Ash.Resource do
|> Enum.filter(& &1.primary_key?)
|> Enum.map(&{&1.name, &1.type})
@primary_key @primary_key_with_types |> Enum.map(&elem(&1, 1))
@primary_key @primary_key_with_types |> Enum.map(&elem(&1, 0))
if Ash.Resource.Info.primary_key_simple_equality?(__MODULE__) do
def primary_key_matches?(left, right) do

View file

@ -91,6 +91,14 @@ defmodule Ash.Test.Policy.ComplexTest do
|> Api.read!(actor: me)
end
test "it properly scopes single loads" do
assert [%{best_friend: %{name: "me"}}] =
User
|> Ash.Query.filter(best_friend.name == "me")
|> Api.read!()
|> Api.load!(:best_friend)
end
test "aggregates can be loaded", %{me: me} do
Post
|> Ash.Query.load(:count_of_comments)

View file

@ -0,0 +1,50 @@
defmodule Ash.Test.Support.PolicyComplex.User.Relationships.BestFriend do
@moduledoc false
use Ash.Resource.ManualRelationship
require Ash.Query
def load(users, _, %{api: api, query: query}) do
user_ids = Enum.map(users, & &1.id)
friend_links =
Ash.Test.Support.PolicyComplex.FriendLink
|> Ash.Query.filter(source_id in ^user_ids or destination_id in ^user_ids)
|> api.read!()
all_relevant_user_ids =
friend_links
|> Enum.flat_map(fn %{source_id: source_id, destination_id: destination_id} ->
[source_id, destination_id]
end)
|> Enum.uniq()
all_relevant_users =
query
|> Ash.Query.filter(id in ^all_relevant_user_ids)
|> api.read!()
|> Map.new(&{&1.id, &1})
{:ok,
Map.new(users, fn user ->
best_friend =
friend_links
|> Enum.filter(fn %{source_id: source_id, destination_id: destination_id} ->
source_id == user.id || destination_id == user.id
end)
|> Enum.flat_map(fn %{source_id: source_id, destination_id: destination_id} ->
if source_id == user.id do
Map.get(all_relevant_users, destination_id)
else
Map.get(all_relevant_users, source_id)
end
|> List.wrap()
end)
|> Enum.uniq()
# obviously a weird heuristic for being someone's best friend 😆
|> Enum.sort_by(&String.jaro_distance(user.name, &1.name))
|> Enum.at(0)
{user.id, best_friend}
end)}
end
end

View file

@ -69,5 +69,9 @@ defmodule Ash.Test.Support.PolicyComplex.User do
has_many :friends, Ash.Test.Support.PolicyComplex.User do
manual Ash.Test.Support.PolicyComplex.User.Relationships.Friends
end
has_one :best_friend, Ash.Test.Support.PolicyComplex.User do
manual Ash.Test.Support.PolicyComplex.User.Relationships.BestFriend
end
end
end