Rumamge.Ecto v1.3.0-rc.0 Rummage.Ecto.CustomHooks.KeysetPaginate View Source
Rummage.Ecto.CustomHooks.KeysetPaginate
is an example of a Custom Hook that
comes with Rummage.Ecto
.
This module uses keyset
pagination to add a pagination query expression
on top a given Ecto.Queryable
.
For more information on Keyset Pagination, check this article
NOTE: This module doesn’t return a list of entries, but a Ecto.Query.t
.
This module uses
Rummage.Ecto.Hook
.
ABOUT:
Arguments:
This Hook expects a queryable
(an Ecto.Queryable
) and
paginate_params
(a Map
). The map should be in the format:
%{per_page: 10, page: 1, last_seen_pk: 10, pk: :id}
Details:
per_page
: Specifies the entries in each page.page
: Specifies thepage
number.last_seen_pk
: Specifies the primary_key value of last_seen entry, This hook uses this entry instead of offset.pk
: Specifies what’s theprimary_key
for the entries being paginated. Cannot benil
For example, if we want to paginate products (primary_key = :id), we would do the following:
Rummage.Ecto.CustomHooks.KeysetPaginate.run(Product,
%{per_page: 10, page: 1, last_seen_pk: 10, pk: :id})
When to Use KeysetPaginate?
- Keyset Pagination is mainly here to make pagination faster for complex
pages. It is recommended that you use
Rummage.Ecto.Hooks.Paginate
for a simple pagination operation, as this module has a lot of assumptions and it’s own ordering on top of the given query.
NOTE: It is not recommended to use this with the native sort hook
ASSUMPTIONS/NOTES:
- This Hook assumes that the querried
Ecto.Schema
has aprimary_key
. - This Hook also orders the query by ascending
primary_key
USAGE
alias Rummage.Ecto.CustomHooks.KeysetPaginate
queryable = KeysetPaginate.run(Parent,
%{per_page: 10, page: 1, last_seen_pk: 10, pk: :id})
This module can be used by overriding the default module. This can be done in the following ways:
In the Rummage.Ecto
call:
Rummage.Ecto.rummage(queryable, rummage,
paginate: Rummage.Ecto.CustomHooks.KeysetPaginate)
OR
Globally for all models in config.exs
:
config :my_app,
Rummage.Ecto,
paginate: Rummage.Ecto.CustomHooks.KeysetPaginate
OR
When using
Rummage.Ecto with an Ecto.Schema
:
defmodule MySchema do
use Rummage.Ecto, repo: SomeRepo,
paginate: Rummage.Ecto.CustomHooks.KeysetPaginate
end
Link to this section Summary
Functions
Callback implementation for Rummage.Ecto.Hook.format_params/2
This is the callback implementation of Rummage.Ecto.Hook.run/2
Link to this section Functions
format_params(Ecto.Query.t(), map(), keyword()) :: map()
format_params(Ecto.Query.t(), map(), keyword()) :: map()
Callback implementation for Rummage.Ecto.Hook.format_params/2
.
This function takes an Ecto.Query.t
or queryable
, paginate_params
which
will be passed to the run/2
function, but also takes a list of options,
opts
.
The function expects opts
to include a repo
key which points to the
Ecto.Repo
which will be used to calculate the total_count
and max_page
for this paginate hook module.
Examples
When a repo
isn’t passed in opts
it gives an error:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Category
iex> KeysetPaginate.format_params(Category, %{per_page: 1, page: 1}, [])
** (RuntimeError) Expected key `repo` in `opts`, got []
When paginate_params
given aren’t valid, it uses defaults to populate params:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Category
iex> Ecto.Adapters.SQL.Sandbox.checkout(Rummage.Ecto.Repo)
iex> KeysetPaginate.format_params(Category, %{}, [repo: Rummage.Ecto.Repo])
%{max_page: 0, page: 1, per_page: 10, total_count: 0, pk: :id,
last_seen_pk: 0}
When paginate_params
and opts
given are valid:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Category
iex> paginate_params = %{
...> per_page: 1,
...> page: 1
...> }
iex> repo = Rummage.Ecto.Repo
iex> Ecto.Adapters.SQL.Sandbox.checkout(repo)
iex> KeysetPaginate.format_params(Category, paginate_params, [repo: repo])
%{max_page: 0, last_seen_pk: 0, page: 1,
per_page: 1, total_count: 0, pk: :id}
When paginate_params
and opts
given are valid:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Category
iex> paginate_params = %{
...> per_page: 1,
...> page: 1
...> }
iex> repo = Rummage.Ecto.Repo
iex> Ecto.Adapters.SQL.Sandbox.checkout(repo)
iex> repo.insert!(%Category{category_name: "name"})
iex> repo.insert!(%Category{category_name: "name2"})
iex> KeysetPaginate.format_params(Category, paginate_params, [repo: repo])
%{max_page: 2, last_seen_pk: 0, page: 1,
per_page: 1, total_count: 2, pk: :id}
When paginate_params
and opts
given are valid and when the queryable
passed has a primary_key
defaulted to id
.
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Category
iex> paginate_params = %{
...> per_page: 1,
...> page: 1
...> }
iex> repo = Rummage.Ecto.Repo
iex> Ecto.Adapters.SQL.Sandbox.checkout(repo)
iex> repo.insert!(%Category{category_name: "name"})
iex> repo.insert!(%Category{category_name: "name2"})
iex> KeysetPaginate.format_params(Category, paginate_params, [repo: repo])
%{max_page: 2, last_seen_pk: 0, page: 1,
per_page: 1, total_count: 2, pk: :id}
When paginate_params
and opts
given are valid and when the queryable
passed has a custom primary_key
.
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> alias Rummage.Ecto.Item
iex> paginate_params = %{
...> per_page: 1,
...> page: 2
...> }
iex> repo = Rummage.Ecto.Repo
iex> Ecto.Adapters.SQL.Sandbox.checkout(repo)
iex> repo.insert!(%Item{item_id: 5})
iex> repo.insert!(%Item{item_id: 6})
iex> KeysetPaginate.format_params(Item, paginate_params, [repo: repo])
%{max_page: 2, last_seen_pk: 1, page: 2,
per_page: 1, total_count: 2, pk: :item_id}
run(Ecto.Query.t(), map()) :: Ecto.Query.t()
run(Ecto.Query.t(), map()) :: Ecto.Query.t()
This is the callback implementation of Rummage.Ecto.Hook.run/2
.
Builds a paginate Ecto.Query.t
on top of a given Ecto.Query.t
variable
with given params
.
Besides an Ecto.Query.t
an Ecto.Schema
module can also be passed as it
implements Ecto.Queryable
Params is a Map
which is expected to have the keys per_page, page, last_seen_pk, pk
.
If an expected key isn’t given, a Runtime Error
is raised.
Examples
When an empty map is passed as params
:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> KeysetPaginate.run(Parent, %{})
** (RuntimeError) Error in params, No values given for keys: per_page, page, last_seen_pk, pk
When a non-empty map is passed as params
, but with a missing key:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> KeysetPaginate.run(Parent, %{per_page: 10})
** (RuntimeError) Error in params, No values given for keys: page, last_seen_pk, pk
When a valid map of params is passed with an Ecto.Schema
module:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> params = %{per_page: 10, page: 1, last_seen_pk: 0, pk: :id}
iex> KeysetPaginate.run(Rummage.Ecto.Product, params)
#Ecto.Query<from p in Rummage.Ecto.Product, where: p.id > ^0, limit: ^10>
When the queryable
passed is an Ecto.Query
variable:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> import Ecto.Query
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> params = %{per_page: 10, page: 1, last_seen_pk: 0, pk: :id}
iex> KeysetPaginate.run(queryable, params)
#Ecto.Query<from p in "products", where: p.id > ^0, limit: ^10>
More examples:
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> import Ecto.Query
iex> params = %{per_page: 5, page: 5, last_seen_pk: 25, pk: :id}
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> KeysetPaginate.run(queryable, params)
#Ecto.Query<from p in "products", where: p.id > ^25, limit: ^5>
iex> alias Rummage.Ecto.CustomHooks.KeysetPaginate
iex> import Ecto.Query
iex> params = %{per_page: 5, page: 1, last_seen_pk: 0, pk: :some_id}
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> KeysetPaginate.run(queryable, params)
#Ecto.Query<from p in "products", where: p.some_id > ^0, limit: ^5>