Harrygr

Harrygr

Batch loading a field in absinthe with dataloader

I have an object in my Absinthe graphql schema that looks like this:

object :match do
    field(:id, non_null(:id))
    field(:opponent, non_null(:string))
    @desc "The number of votes that have been cast so far."
    field(:vote_count, non_null(:integer), resolve: &MatchResolver.get_vote_count/2)
    # etc...
end

I’m using a resolver for vote_count that performs an ecto query using the parent match. This would run into the n+1 query problem however if a list of matches is queried. It currently looks like this:

  def get_vote_count(_root, %{source: %Match{} = match}) do
    count = match |> Ecto.assoc(:votes) |> Repo.aggregate(:count, :id)

    {:ok, count}
  end

I’m already using dataloader to batch load child entities but I’m can’t seem to get a custom run_batch function to work when using the Absinthe.Resolution.Helpers.dataloader function provided by Absinthe.

What’s the recommended approach for implementing custom batch queries using dataloader/ecto? Can someone give an example, including the schema definition part?

Most Liked

mbuhot

mbuhot

This github issue has an example of using a custom batch function to perform an aggregation.

The key is the %{batch: _, item: _} map passed to the dataloader helper, which I haven’t found documented anywhere except that issue :man_shrugging:

def run_batch(_, query, :post_count, users, repo_opts) do
  user_ids = Enum.map(users, & &1.id)
  default_count = 0

  result =
    query
    |> where([p], p.user_id in ^user_ids)
    |> group_by([p], p.user_id)
    |> select([p], {p.user_id, count("*")})
    |> Repo.all(repo_opts)
    |> Map.new()

  for %{id: id} <- users do
    [Map.get(result, id, default_count)]
  end
end

# Fallback to original run_batch
def run_batch(queryable, query, col, inputs, repo_opts) do
  Dataloader.Ecto.run_batch(Repo, queryable, query, col, inputs, repo_opts)
end

Called from the GraphQL schema like:

field(:post_count, non_null(:integer) resolve dataloader(Posts, fn user, _args, _ ->
  %{batch: {{:one, Post}, %{}}, item: [post_count: user]}
end)

Where Next?

Popular in Questions Top

ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
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
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
Phillipp
Hey, I have a NanoPi-M3 and try to install Elixir on their Ubuntu image. I followed the Raspberry Pi installation instructions from the ...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
hpopp
To simplify some tasks at work, I wrote and published this package yesterday. It’s a simple macro that enables Access behaviour on struct...
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
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
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
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement