kylevsteger

kylevsteger

Dataloader on Join Table Association

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

Marked As Solved

fuelen

fuelen

Hello!
Personally I would go with another graphql schema.
:friend object is context-specific. It has an id of the user, but different status depending on parent record. This is error-prone, because GraphQL client on frontend side MAY use simple caching by id field only, so other users may borrow friends.
To avoid this you have to use an id from the friends table. But it would be simpler to expose :user association:

object :user do
  field :id, non_null(:string)
  ...
  field :friends, list_of(:friend) do
    arg :status, :friend_status
    # resolve using :friends_assoc
  end
end

object :friend do
  field :id, non_null(:string)
  field :status, :friend_status
  field :user, :user
  field :friend, :user
end

Where Next?

Popular in Questions Top

albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Mooodi
Given a string, how can I get access to its character by index? Enum.at("my_string", 2) doesn't work. Or rather, not char, but a substr...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

Other popular topics Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 45766 226
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

We're in Beta

About us Mission Statement