diff --git a/lib/paginator.ex b/lib/paginator.ex index 8370d53..06130a1 100644 --- a/lib/paginator.ex +++ b/lib/paginator.ex @@ -88,6 +88,9 @@ defmodule Paginator do * `:total_count_limit` - Running count queries on tables with a large number of records is expensive so it is capped by default. Can be set to `:infinity` in order to count all the records. Defaults to `10,000`. + * `:page_booleans` - populates `:has_next_page` and `:has_previous_page` booleans. + Always returns an `:after` and `:before` cursor (if available). This mimics + [relay style pagination](https://relay.dev/graphql/connections.htm#sec-undefined.PageInfo) ## Repo options @@ -187,7 +190,9 @@ defmodule Paginator do after: after_cursor(paginated_entries, sorted_entries, config), limit: config.limit, total_count: total_count, - total_count_cap_exceeded: total_count_cap_exceeded + total_count_cap_exceeded: total_count_cap_exceeded, + has_next_page: has_next_page(paginated_entries, sorted_entries, config), + has_previous_page: has_previous_page(paginated_entries, sorted_entries, config), } } end @@ -260,6 +265,10 @@ defmodule Paginator do first_or_nil(paginated_entries, config) end + defp before_cursor(paginated_entries, _sorted_entries, %Config{page_booleans: true} = config) do + first_or_nil(paginated_entries, config) + end + defp before_cursor(paginated_entries, sorted_entries, config) do if first_page?(sorted_entries, config) do nil @@ -283,6 +292,10 @@ defmodule Paginator do last_or_nil(paginated_entries, config) end + defp after_cursor(paginated_entries, _sorted_entries, %Config{page_booleans: true} = config) do + last_or_nil(paginated_entries, config) + end + defp after_cursor(paginated_entries, sorted_entries, config) do if last_page?(sorted_entries, config) do nil @@ -299,6 +312,44 @@ defmodule Paginator do end end + defp has_next_page(_paginated_entries, _sorted_entries, %Config{page_booleans: false}) do + nil + end + + defp has_next_page([], [], _config) do + false + end + + defp has_next_page(_paginated_entries, _sorted_entries, %Config{before: c_before}) + when not is_nil(c_before) do + true + end + + defp has_next_page(_paginated_entries, sorted_entries, config) do + !last_page?(sorted_entries, config) + end + + defp has_previous_page(_paginated_entries, _sorted_entries, %Config{page_booleans: false}) do + nil + end + + defp has_previous_page([], [], _config) do + false + end + + defp has_previous_page(_paginated_entries, _sorted_entries, %Config{after: nil, before: nil}) do + false + end + + defp has_previous_page(_paginated_entries, _sorted_entries, %Config{after: c_after}) + when not is_nil(c_after) do + true + end + + defp has_previous_page(_paginated_entries, sorted_entries, config) do + !first_page?(sorted_entries, config) + end + defp fetch_cursor_value(schema, %Config{ cursor_fields: cursor_fields, fetch_cursor_value_fun: fetch_cursor_value_fun diff --git a/lib/paginator/config.ex b/lib/paginator/config.ex index a4e57c0..677a15e 100644 --- a/lib/paginator/config.ex +++ b/lib/paginator/config.ex @@ -17,7 +17,8 @@ defmodule Paginator.Config do :limit, :maximum_limit, :sort_direction, - :total_count_limit + :total_count_limit, + :page_booleans ] @default_total_count_primary_key_field :id @@ -41,7 +42,8 @@ defmodule Paginator.Config do opts[:total_count_primary_key_field] || @default_total_count_primary_key_field, limit: limit(opts), sort_direction: opts[:sort_direction], - total_count_limit: opts[:total_count_limit] || @default_total_count_limit + total_count_limit: opts[:total_count_limit] || @default_total_count_limit, + page_booleans: opts[:page_booleans] || false } |> convert_deprecated_config() end diff --git a/lib/paginator/page/metadata.ex b/lib/paginator/page/metadata.ex index d4ccad2..56290d0 100644 --- a/lib/paginator/page/metadata.ex +++ b/lib/paginator/page/metadata.ex @@ -19,8 +19,10 @@ defmodule Paginator.Page.Metadata do before: opaque_cursor() | nil, limit: integer(), total_count: integer() | nil, - total_count_cap_exceeded: boolean() | nil + total_count_cap_exceeded: boolean() | nil, + has_next_page: boolean() | nil, + has_previous_page: boolean() | nil } - defstruct [:after, :before, :limit, :total_count, :total_count_cap_exceeded] + defstruct [:after, :before, :limit, :total_count, :total_count_cap_exceeded, :has_next_page, :has_previous_page] end