Skip to content

Instantly share code, notes, and snippets.

@rosswilson
Created February 21, 2018 12:08
Show Gist options
  • Save rosswilson/691aeff6931cae91b1de520752fcc74c to your computer and use it in GitHub Desktop.
Save rosswilson/691aeff6931cae91b1de520752fcc74c to your computer and use it in GitHub Desktop.
Example of pagination using Ecto
defmodule MyApp.Pagination do
@moduledoc """
Takes an Ecto query and applies limit and offset style pagination.
"""
import Ecto.Query
defstruct [:entries, :page_number, :page_size, :total_pages]
@doc """
Takes an Ecto query and some params, returns a new query with pagination applied.
Valid params are `page` and/or `page_size`. These values will be converted to
positive integers, with min/max values, and fallback defaults.
## Examples
iex> MyApp.Things.Thing |> MyApp.Pagination.page(%{"page" => "3", "page_size" => 15})
#Ecto.Query<from a in MyApp.Things.Thing, limit: ^15, offset: ^30>
"""
def page(query, params) when is_map(params) do
page_number =
params
|> get_integer_with_default("page", 1)
|> max(1)
page_size =
params
|> get_integer_with_default("page_size", 10)
|> min(20)
|> max(1)
page(query, page_number, page_size)
end
@doc """
Takes an Ecto query, a `page_number`, and a `page_size`, returns a new query with pagination applied.
## Examples
iex> MyApp.Things.Thing |> MyApp.Pagination.page(3, 15)
#Ecto.Query<from a in MyApp.Things.Thing, limit: ^15, offset: ^30>
"""
def page(query, page_number, page_size) do
offset = page_size * (page_number - 1)
query
|> limit(^page_size)
|> offset(^offset)
end
defp get_integer_with_default(map, key, default) do
with {:ok, fetched} <- Map.fetch(map, key),
{:ok, integer} <- to_int(fetched) do
integer
else
_ -> default
end
end
defp to_int(i) when is_integer(i), do: {:ok, i}
defp to_int(s) when is_binary(s) do
case Integer.parse(s) do
{i, _} -> {:ok, i}
:error -> :error
end
end
defp to_int(_), do: :error
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment