absinthe-graphql/dataloader

Customize the association data with a callback similar to `run_batch/5`?

thojanssens opened this issue · 3 comments

run_batch/5 doesn't run for repo calls to load associations. In my case, I use the viewer pattern where everything is preloaded from the current user.

I want to alter the results of the associated data, i.e. to handle cursor-based pagination on a list.

In the Absinthe schema:

query do
  field :me, :user do
    resolve &Resolvers.Accounts.me/3
  end
end

def dataloader() do
  Dataloader.new()
  |> Dataloader.add_source(Business, Business.data())
  |> Dataloader.add_source(Transactions, Transactions.data())
end

def context(ctx) do
  Map.put(ctx, :loader, dataloader())
end
defmodule MyAppWeb.GraphQL.Resolvers.Accounts do
  def me(_, _, %{context: %{current_user: current_user}}) do
    {:ok, current_user}
  end

  def me(_, _, _) do
    {:ok, nil}
  end
end
object :user do
  field :id, non_null(:id)
  field :name, :string
  field :organizations, non_null(list_of(non_null(:organization))), resolve: dataloader(Business)
end

object :organization do
  field :id, non_null(:id)
  field :name, non_null(:string)

  field :pagninated_revenues, :pagninated_revenues do      # <- this is what I would like to achieve
    arg :pagination, :pagination_input
    resolve dataloader(Transactions, :revenues)     # <- customize the repo call in order to include the limit and cursor metadata (return :pagninated_revenues object)
  end
end

object :pagninated_revenues do
  field :pagination, non_null(:pagination) # holding cursor metadata
  field :revenues, non_null(list_of(non_null(:revenue)))
end

In my contexts I have the following:

def data() do
  Dataloader.Ecto.new(Repo, query: &query/2, run_batch: &run_batch/5)
end

def run_batch(queryable, query, col, inputs, repo_opts) do
  IO.inspect "=== run_batch ==="                                       # <- never printed
  Dataloader.Ecto.run_batch(Repo, queryable, query, col, inputs, repo_opts)
end

def query(queryable, _) do
  IO.inspect "=== query ==="
  queryable
end

Maybe my questioning sounds like the following discussion: #87, where I want to customize the resulting records of an association (I need to paginate). I still need to batch, where for example a user may have multiple organizations, and thus the paginated revenues can be from multiple organizations.

Hey @thojanssens the pagination input can be applied via query, you don't need to do so via the association.

At this time I'm not really sure how I'd even enable run_batch to work with associations. The associations rely ultimately on calling Repo.preload and that just has a totally different set of inputs and outputs from the current run_batch function.

Thank you so much for your help and this lib Ben!

I am closing this issue for now. Also don't understand how run_batch/5 could be executed on the query level in an Absinthe context. I understand how it works on the associations, but not on the queryable.

Hey @thojanssens what I mean by query/2 is that you can do this:

def query(Revenue, args) do
  Revenue
  |> maybe_add_pagination(args)
end

where maybe_add_pagination applies an order, limit, and so forth based on the args passed in. This will be applied whether using the association or queryable.