dataloader v2.0.2 Dataloader.Ecto
Ecto source for Dataloader
This defines a schema and an implementation of the Dataloader.Source
protocol
for handling Ecto related batching.
A simple Ecto source only needs to know about your application’s Repo.
Basic Usage
source = Dataloader.Ecto.new(MyApp.Repo)
loader =
Dataloader.new
|> Dataloader.add_source(Accounts, source)
|> Dataloader.load(Accounts, User, 1)
|> Dataloader.load_many(Accounts, Organization, [4, 9])
|> Dataloader.run
organizations = Dataloader.get(loader, Accounts, Organization, [4,9])
loader =
loader
|> Dataloader.load_many(Accounts, :users, organizations)
|> Dataloader.run
Filtering / Ordering
Dataloader.new/2
can receive a 2 arity function that can be used to apply
broad ordering and filtering rules, as well as handle parameters
source = Dataloader.Ecto.new(MyApp.Repo, query: &Accounts.query/2)
loader =
Dataloader.new
|> Dataloader.add_source(Accounts, source)
When we call load/4
we can pass in a tuple as the batch key
loader
|> Dataloader.load(Accounts, {User, order: :name}, 1)
# or
loader
|> Dataloader.load_many(Accounts, {:users, order: :name}, organizations)
# this is still supported
loader
|> Dataloader.load(Accounts, User, 1)
# as is this
loader
|> Dataloader.load(:accounts, :user, :organization)
In all cases the Accounts.query
function would be:
def query(User, params) do
field = params[:order] || :id
from u in User, order_by: [asc: field(u, ^field)]
end
def query(queryable, _) do
queryable
end
If we query something that ends up using the User
schema, whether directly
or via association, the query/2
function will match on the first clause and
we can handle the params. If no params are supplied, the params arg defaults
to source.default_params
which itself defaults to %{}
.
default_params
is an extremely useful place to store values like the current user:
source = Dataloader.Ecto.new(MyApp.Repo, [
query: &Accounts.query/2,
default_params: %{current_user: current_user},
])
loader =
Dataloader.new
|> Dataloader.add_source(Accounts, source)
|> Dataloader.load_many(Accounts, Organization, ids)
|> Dataloader.run
# the query function
def query(Organization, %{current_user: user}) do
from o in Organization,
join: m in assoc(o, :memberships),
where: m.user_id == ^user.id
end
def query(queryable, _) do
queryable
end
In our query function we are pattern matching on the current user to make sure
that we are only able to lookup data in organizations that the user actually
has a membership in. Additional options you specify IE {Organization, %{order: :asc}}
are merged into the default.
Link to this section Summary
Functions
Create an Ecto Dataloader source
Default implementation for loading a batch. Handles looking up records by column
Link to this section Types
batch_fun() :: (Ecto.Queryable.t(), Ecto.Query.t(), [any()], Keyword.t() -> [any()])
t() :: %Dataloader.Ecto{batches: map(), default_params: map(), name: term(), options: Keyword.t(), query: query_fun(), repo: Ecto.Repo.t(), repo_opts: Keyword.t(), results: map(), run_batch: batch_fun()}
Link to this section Functions
Create an Ecto Dataloader source.
This module handles retrieving data from Ecto for dataloader. It requires a
valid Ecto Repo. It also accepts a repo_opts:
option which is handy for
applying options to any calls to Repo functions that this module makes.
For example, you can use this module in a multi-tenant context by using
the prefix
option:
Dataloader.Ecto.new(MyApp.Repo, repo_opts: [prefix: "tenant"])
Default implementation for loading a batch. Handles looking up records by column