A minimalistic keyset pagination library for Ecto queries
N.B.: you probably don't want to use Fob. Fob makes a number of assumptions about the queries passed to it, such as:
- each schema will have only one primary key
- each query must be ordered by at least the schema's primary key
- the primary key is always the final ordering condition
- associations are untested and probably do not work
- (left) joins are known to work though
Fob works primarily by composing Ecto queries with Ecto.Query.where/3
and
Ecto.Query.limit/2
. In general, the tracking of page breaks is left to any
consuming library/application, but Fob provides a convenience tool which it
uses internally for easy testing: Fob.Cursor
.
A Fob.Cursor
is a similar idea to a standard elixir Stream
except that it
does not represent an active connection to a resource (some but not all Elixir
Stream
s open a process or port and therefore represent a connection to
resource. This disallows some functionality which is desirable for Fob, such
as Stream.split/2
).
With the cursor, you may walk through the pages of a dataset with
Fob.Cursor.next/1
and take multiple pages with Fob.Cursor.split/2
iex> alias Fob.Cursor
iex> import Ecto.Query, only: [order_by: 2]
iex> cursor = Cursor.new(order_by(MySchema, asc: :id), MyApp.Repo, _initial_pagination = nil, _page_size = 3)
#Fob.Cursor<...>
iex> {_records, cursor} = Cursor.next(cursor)
{[%{id: 1}, %{id: 2}, %{id: 3}], #Fob.Cursor<...>}
iex> {_records, cursor} = Cursor.next(cursor)
{[%{id: 4}, %{id: 5}, %{id: 6}], #Fob.Cursor<...>}
iex> {_records, cursor} = Cursor.split(cursor, 2)
{[%{id: 7}, %{id: 8}, %{id: 9}, %{id: 10}, %{id: 11}, %{id: 12}], #Fob.Cursor<...>}
Why build an incomplete and minimal keyset pagination library when there are already workable implementations in Paginator and Quarto? Fob was originally cut out of another library called Haste. In our setup, Haste satisfies all of the above assumptions.
def deps do
[
{:fob, "~> 1.0"}
]
end
Check out the docs here: https://hexdocs.pm/fob