Skip to content

Instantly share code, notes, and snippets.

@parallel588
Forked from edipox/jsonapi_paginator.ex
Created May 16, 2022 17:24
Show Gist options
  • Select an option

  • Save parallel588/5e469d0f9a6305c6e471427d9e56faa8 to your computer and use it in GitHub Desktop.

Select an option

Save parallel588/5e469d0f9a6305c6e471427d9e56faa8 to your computer and use it in GitHub Desktop.
elixir scrivener handle slow couting example
defmodule JsonApi.Paginator do
# ...
# ...
alias SomeProject.Repo
import Ecto.Query
@doc """
Helper function used to paginate index queries
"""
@spec paginate(conn :: Plug.Conn.t(), page_params :: map, query :: module) :: Plug.Conn.t()
def paginate(conn, page_params, query) do
page_number = validate_int(page_params["number"], 1, -1)
page_size = validate_int(page_params["size"], default_page_size(), maximum_page_size())
handle_pagination(conn, page_number, page_size, query)
end
# Handle invalid page params
@spec handle_pagination(conn :: Plug.Conn.t(), page :: number | atom, page_size :: number | atom, query :: module) :: Plug.Conn.t()
defp handle_pagination(conn, :error, _page_size, _query) do
Response.send_error_resp(conn, :bad_request, "Invalid page value", "page[number] has to be positive number.")
end
defp handle_pagination(conn, _page, :error, _query) do
Response.send_error_resp(conn, :bad_request, "Invalid page value", "page[size] has to be positive number (maximum value is #{maximum_page_size()}).")
end
# Paginate
defp handle_pagination(conn, page, page_size, query) do
config = %Scrivener.Config{
module: Repo,
page_number: page, page_size: page_size,
options: [total_entries: total_entries(conn, query)]
}
query |> Scrivener.paginate(config)
end
# Retrieves the estimated record count
@spec total_entries(conn :: Plug.Conn.t(), query :: module) :: number
defp total_entries(_conn, query) do
table_name = table_name(query)
{:ok, result} = Ecto.Adapters.SQL.query(Repo, "SELECT reltuples::BIGINT AS estimate FROM pg_class WHERE relname=$1", [table_name])
result.rows |> List.first |> List.first
end
# Determines the table name based on a given query
@spec table_name(query :: module) :: module
defp table_name(query) do
module(query).__schema__(:source)
end
# Retrieves the model module from a given query
@spec module(module :: module) :: module
defp module(%{from: {_table, module}}), do: module
defp module(module), do: module
# ...
# ...
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment