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 :
- Dataloader — absinthe v1.11.0
- Absinthe.Resolution.Helpers — absinthe v1.11.0
- Dataloader — dataloader v2.0.2
- Dataloader.Ecto — dataloader v2.0.2
Is it possible?
First Post!
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?








