First here is what my Absinthe object ended up looking like:
object :user do
field(:id, :id)
field(:name, :string)
field :total_sold, :integer do
resolve(fn user, _, _ ->
batch({MyApp.Accounts, :total_sold}, user.id, fn batch_results ->
{:ok, Map.get(batch_results, user.id)}
end)
end)
end
end
Notice how I call my MyApp.Accounts.total_sold
function by using {MyApp.Accounts, :total_sold}
notation.
The second param of batch is the field I will send my total_sold
function. If I use user.name
, my function would have received something like: ["Foo", "Bar", "Baz"]
. I needed the ids, so I used user.id
.
Finally batch_results
has the return of MyApp.Accounts.total_sold
. Let’s look at that.
I don’t quite like this because it makes my context aware it’s being called from a Graphql schema, but I can live with this for now. Open to suggestions.
The user_ids
has the collection of all the user ids fetched. Then I can group and count the sold listings.
def total_sold(_, user_ids) do
query =
from u in User,
join: listing in assoc(u, :listings),
where: listing.status == :sold,
where: u.id in ^user_ids,
group_by: u.id,
select: %{user_id: u.id, sold_listings: count(listing)}
listings = Repo.all(query)
Map.new(listings, fn listing -> {listing.user_id, listing.sold_listings} end)
end
The important bit is the final Map.new
.
I am ultimately returning something like:
%{
"43c8421e-69e8-4769-aa46-15229e97dc99" => 1,
"651da703-bdb6-4625-9fdb-ffc4b2a701ab" => 1
}
Where the user.id is the Map key, the total sold counts are the values.
So let’s go back up a level to my object definition.
object :user do
field(:id, :id)
field(:name, :string)
field :total_sold, :integer do
resolve(fn user, _, _ ->
batch({MyApp.Accounts, :total_sold}, user.id, fn batch_results ->
{:ok, Map.get(batch_results, user.id)}
end)
end)
end
end
Now that Map.get
makes sense, it’s fetching the user.id key value, which is… the total sold count! Boom we’re in business!
Hope this helps and I know it will help me 2 years from now when I totally forget this haha