I ended up using Dataloader.KV
and it worked great.
Here’s my query:
{
applications(ids: [1, 2]) {
id
review {
total_count
assigned_count
submitted_count
}
}
}
Object schemas:
defmodule Document.Schema.Application do
use Absinthe.Schema.Notation
import Absinthe.Resolution.Helpers, only: [dataloader: 1]
object :application do
field :id, :id
field :review, :review, resolve: dataloader(:review)
end
end
defmodule Document.Schema.Review do
use Absinthe.Schema.Notation
object :review do
field :total_count, :integer
field :assigned_count, :integer
field :submitted_count, :integer
end
end
And dataloader:
defmodule Document.Dataloader.Review do
def new do
Dataloader.KV.new(&load/2)
end
def load(_batch_key, applications) do
user_ids = Enum.map(applications, &(&1.user_id))
total_counts = total_counts(user_ids)
assigned_counts = started_counts(user_ids)
submitted_counts = submitted_counts(user_ids)
Enum.reduce applications, %{}, fn application, acc ->
Map.put acc, application, %{
total_count: total_counts[application.user_id] || 0,
assigned_count: assigned_counts[application.user_id] || 0,
submitted_count: submitted_counts[application.user_id] || 0
}
end
end
defp total_counts(user_ids) do
import Ecto.Query
Schema.Reviewer.Review
|> select([r], {r.reviewer_user_id, count(r.id)})
|> where([r], r.reviewer_user_id in ^user_ids)
|> group_by([r], r.reviewer_user_id)
|> Datastores.Shard.all
|> Map.new
end
defp started_counts(user_ids) do
import Ecto.Query
Schema.Reviewer.Review
|> select([r], {r.reviewer_user_id, count(r.id)})
|> where([r], r.reviewer_user_id in ^user_ids)
|> where([r], r.state == 2)
|> group_by([r], r.reviewer_user_id)
|> Datastores.Shard.all
|> Map.new
end
defp submitted_counts(user_ids) do
import Ecto.Query
Schema.Reviewer.Review
|> select([r], {r.reviewer_user_id, count(r.id)})
|> join(:inner, [r], a in Schema.Application, r.reviewer_application_id == a.id)
|> join(:inner, [r, a], c in Schema.Category, a.category_id == c.id)
|> where([r, a, c], r.reviewer_user_id in ^user_ids)
|> where([r, a, c], c.state == "submitted")
|> group_by([r], r.reviewer_user_id)
|> Datastores.Shard.all
|> Map.new
end
end
Works great, no n+1 queries!
P.S. A little different from the distilled down example from previous posts, but this is actual working code!