# Fob

![Actions CI](

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

## Usage

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
`` and take multiple pages with `Fob.Cursor.split/2`

iex> alias Fob.Cursor
iex> import Ecto.Query, only: [order_by: 2]
iex> cursor =, asc: :id), MyApp.Repo, _initial_pagination = nil, _page_size = 3)
iex> {_records, cursor} =
{[%{id: 1}, %{id: 2}, %{id: 3}], #Fob.Cursor<...>}
iex> {_records, 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?

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.

## Installation

def deps do
    {:fob, "~> 0.5"}

Check out the docs here: