Hey all! I’m having trouble with Absinthe and Dataloader when trying to resolve friends
of a user
.
I was able to load the friends
without much issue, but now I’m trying to add a status
to the response, which lives on the join table.
The last chunk in the code below is where I’m struggling. Any insight is much appreciated! Thanks!
# User Ecto Schema
@primary_key {:id, :binary_id, autogenerate: true}
schema "users" do
...
has_many :friends_assoc, Friend, foreign_key: :user_id
has_many :reverse_friends_assoc, Friend, foreign_key: :friend_id
has_many :friends, through: [:friends_assoc, :friend]
has_many :reverse_friends, through: [:reverse_friends_assoc, :user]
end
# Friend Ecto Schema
@primary_key {:id, :binary_id, autogenerate: true}
schema "friends" do
field :status, FriendStatus
belongs_to :user, User, type: :binary_id
belongs_to :friend, User, type: :binary_id
end
# User Absinthe Schema
object :user do
field :id, non_null(:string)
...
field :friends, list_of(:friend) do
arg :status, :friend_status
# This ultimately points to: `Dataloader.Ecto.new(Repo, query: fn _, args -> generate_query(args) end)`
resolve dataloader(User)
end
end
# Friend Absinthe Schema
object :friend do
field :id, :string
field :first_name, :string
field :last_name, :string
# This comes back as `null` on the GraphQL response
# because it's not on the Ecto User schema
# but I'm not sure how to remedy the situation
field :status, :friend_status
end
UPDATE
I wound up going with a custom Dataloader.KV
that points to this function
@doc """
Queries the database for Users and loads their friends.
"""
@spec get_friends_with_status([Ecto.UUID.t()], map()) :: map()
def get_friends_with_status(ids, %{status: status} = _args) do
query = from u in User,
left_join: fa in assoc(u, :friends_assoc),
left_join: fr in assoc(fa, :friend),
where: u.id in ^ids,
where: fa.status == ^status,
select: %{
user_id: u.id,
friend: %{
first_name: fr.first_name,
last_name: fr.last_name,
email: fr.email,
id: fr.id,
status: fa.status
}
}
id_map = Map.new(ids, & {&1, []})
query
|> Repo.all()
|> Enum.reduce(id_map, fn %{user_id: user_id, friend: friend}, acc ->
Map.update(acc, user_id, [friend], fn
friends -> [friend | friends]
end)
end)
end