itopiz

itopiz

How to run the Absinthe dataloader only once for all types implementing an interface?

I have an interface in my schema:

interface :authentication_source do
  field(:id, non_null(:id))
  field(:name, non_null(:string))
  field(:slug, non_null(:string))
  field(:user_authentications, list_of(non_null(:user_authentication)))

  resolve_type(fn
    %OidcAuthenticationSource{}, _ -> :oidc_authentication_source
    %MicrosoftAuthenticationSource{}, _ -> :microsoft_authentication_source
  end)
end

as well as 2 nodes types that implement that interface:

node object(:oidc_authentication_source, id_fetcher: &authentication_source_id_fetcher/2) do
  field(:name, non_null(:string))
  field(:slug, non_null(:string))
  field(:metadata_document, :string)
  field(:client_id, :string)
  field(:client_secret, :string)
  field(:user_authentications, list_of(non_null(:user_authentication))) do
    resolve(dataloader(Accounts))
  end

  interface(:authentication_source)
end

node object(:microsoft_authentication_source, id_fetcher: &authentication_source_id_fetcher/2) do
  field(:name, non_null(:string))
  field(:slug, non_null(:string))
  field(:tenant_id, non_null(:string))
  field(:client_id, non_null(:string))
  field(:client_secret, non_null(:string))
  field(:user_authentications, list_of(non_null(:user_authentication))) do
    resolve(dataloader(Accounts))
  end

  interface(:authentication_source)
end

The Accounts source is using Dataloader.Ecto and when I run this GraphQL query:

query authenticationSources {
  authenticationSources {
    __typename
    id
    name
    slug
    ... on OidcAuthenticationSource {
      metadataDocument
      clientId
      clientSecret
    }
    ... on MicrosoftAuthenticationSource {
      tenantId
      clientId
      clientSecret
    }
    userAuthentications {
      externalUserId
      user {
        id
        email
        displayName
      }
    }
  }
}

I observe that 2 identical SQL queries are executed to load the :user_authentications:

  • 1 query with all the IDs of the authentication sources of type :oidc_authentication_source
  • 1 query with all the IDs of the authentication sources of type :microsoft_authentication_source

The dataloader is very helpful but still it is triggering 1 SQL query per type implementing my interface.

I have re-read the following documentations but I cannot find a way to reduce this to a single query :

Is it possible?

First Post!

itopiz

itopiz

I discovered that using a key function seems to do the trick:

defp dataloader_user_authentications(authentication_source, _args, _info) do
  %{
    batch: {{:many, UserAuthentication}, %{}},
    item: [authentication_source_dbid: authentication_source.authentication_source_dbid]
  }
end

node object(:oidc_authentication_source, id_fetcher: &authentication_source_id_fetcher/2) do
  field(:name, non_null(:string))
  field(:slug, non_null(:string))
  field(:metadata_document, :string)
  field(:client_id, :string)
  field(:client_secret, :string)
  field(:user_authentications, list_of(non_null(:user_authentication))) do
    resolve(dataloader(Accounts, &dataloader_user_authentications/3))
  end

  interface(:authentication_source)
end

node object(:microsoft_authentication_source, id_fetcher: &authentication_source_id_fetcher/2) do
  field(:name, non_null(:string))
  field(:slug, non_null(:string))
  field(:tenant_id, non_null(:string))
  field(:client_id, non_null(:string))
  field(:client_secret, non_null(:string))
  field(:user_authentications, list_of(non_null(:user_authentication))) do
    resolve(dataloader(Accounts, &dataloader_user_authentications/3))
  end

  interface(:authentication_source)
end

Now a single SQL query is sent. The SQL query looks almost the same as before except it does not duplicate authentication_source_dbid at the end of SELECT and its does not use authentication_source_dbid in ORDER BY.

This was not obvious from the documentation.

@benwilson512 Does that mean it is simply an undocumented feature or it is something that could disappear in future release? Could this behavior be added in the simpler form resolve(dataloader(Accounts)) as well?

Where Next?

Popular in Questions Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
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
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
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
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement