Functions for encoding, decoding and extracting cursor values.
Cursor size
Because cursors arrive as unvalidated request parameters that need to be
decoded before they can be applied, the decode functions reject cursors
larger than 8 KB before decoding them. A cursor contains the order
field values of a single record, so the default is far above what Flop itself
produces. If your application produces unusually large cursor values, you can
increase the maximum size either globally via application environment:
config :flop, max_cursor_size: 16_384Or by passing the option directly to the decode functions:
Flop.Cursor.decode("g3QAAAABZAACaWRiAAACDg==", max_cursor_size: 16_384)The option can also be passed to any of the validate functions, or to
use Flop.
Summary
Functions
Decodes a cursor value.
Same as Flop.Cursor.decode/1, but raises an error if the cursor is invalid.
Encodes a cursor value.
Takes a tuple with the node and the edge and the order_by field list and
returns the cursor value derived from the edge map.
Takes a tuple with the node and the edge and the order_by field list and
returns the cursor value derived from the node map.
Retrieves the start and end cursors from a query result.
Functions
Decodes a cursor value.
Returns :error if the cursor cannot be decoded or the decoded term is not a
map with atom keys.
iex> Flop.Cursor.decode("g3QAAAABZAACaWRiAAACDg==")
{:ok, %{id: 526}}
iex> Flop.Cursor.decode("AAAH")
:error
iex> f = fn a -> a + 1 end
iex> cursor = Flop.Cursor.encode(%{a: f})
iex> Flop.Cursor.decode(cursor)
:error
iex> cursor = Flop.Cursor.encode(a: "b")
iex> Flop.Cursor.decode(cursor)
:error
iex> cursor = Flop.Cursor.encode(%{"a" => "b"})
iex> Flop.Cursor.decode(cursor)
:errorTrying to decode a cursor that contains non-existent atoms also results in an error.
iex> Flop.Cursor.decode("g3QAAAABZAAGYmFybmV5ZAAGcnViYmVs")
:errorOptions
:max_cursor_size- The maximum cursor size in bytes that is accepted. Defaults to8192.
Cursors larger than the configured maximum size as well as compressed terms are rejected without being decoded. See the module documentation for details.
Same as Flop.Cursor.decode/1, but raises an error if the cursor is invalid.
iex> Flop.Cursor.decode!("g3QAAAABZAACaWRiAAACDg==")
%{id: 526}
Encodes a cursor value.
Flop.Cursor.encode(%{email: "peter@mail", name: "Peter"})
"g3QAAAACdwRuYW1lbQAAAAVQZXRlcncFZW1haWxtAAAACnBldGVyQG1haWw="
Takes a tuple with the node and the edge and the order_by field list and
returns the cursor value derived from the edge map.
If a map is passed instead of a tuple, it retrieves the cursor value from that map.
This function can be used for the :cursor_value_func option. See also
Flop.Cursor.get_cursor_from_node/2.
iex> record = %{id: 20, name: "George", age: 62}
iex> edge = %{id: 25, relation: "sibling"}
iex>
iex> Flop.Cursor.get_cursor_from_edge({record, edge}, [:id])
%{id: 25}
iex> Flop.Cursor.get_cursor_from_edge({record, edge}, [:id, :relation])
%{id: 25, relation: "sibling"}
iex> Flop.Cursor.get_cursor_from_edge(record, [:id])
%{id: 20}If the edge is a struct that derives Flop.Schema, join and compound fields
are resolved according to the configuration.
iex> record = %{id: 25, relation: "sibling"}
iex> edge = %MyApp.Pet{
...> name: "George",
...> owner: %MyApp.Owner{name: "Carl"}
...> }
iex>
iex> Flop.Cursor.get_cursor_from_edge({record, edge}, [:owner_name])
%{owner_name: "Carl"}
iex> Flop.Cursor.get_cursor_from_edge(edge, [:owner_name])
%{owner_name: "Carl"}
iex> record = %{id: 25, relation: "sibling"}
iex> edge = %MyApp.Pet{
...> given_name: "George",
...> family_name: "Gooney"
...> }
iex> Flop.Cursor.get_cursor_from_edge({record, edge}, [:full_name])
%{full_name: "Gooney George"}
iex> Flop.Cursor.get_cursor_from_edge(edge, [:full_name])
%{full_name: "Gooney George"}
Takes a tuple with the node and the edge and the order_by field list and
returns the cursor value derived from the node map.
If a map is passed instead of a tuple, it retrieves the cursor value from that map.
This function is used as a default if no :cursor_value_func option is
set. See also Flop.Cursor.get_cursor_from_edge/2.
iex> record = %{id: 20, name: "George", age: 62}
iex> edge = %{id: 25, relation: "sibling"}
iex>
iex> Flop.Cursor.get_cursor_from_node({record, edge}, [:id])
%{id: 20}
iex> Flop.Cursor.get_cursor_from_node({record, edge}, [:id, :name])
%{id: 20, name: "George"}
iex> Flop.Cursor.get_cursor_from_node(record, [:id])
%{id: 20}If the node is a struct that derives Flop.Schema, join and compound fields
are resolved according to the configuration.
iex> record = %MyApp.Pet{
...> name: "George",
...> owner: %MyApp.Owner{name: "Carl"}
...> }
iex> edge = %{id: 25, relation: "sibling"}
iex>
iex> Flop.Cursor.get_cursor_from_node({record, edge}, [:owner_name])
%{owner_name: "Carl"}
iex> Flop.Cursor.get_cursor_from_node(record, [:owner_name])
%{owner_name: "Carl"}
iex> record = %MyApp.Pet{
...> given_name: "George",
...> family_name: "Gooney"
...> }
iex> edge = %{id: 25, relation: "sibling"}
iex> Flop.Cursor.get_cursor_from_node({record, edge}, [:full_name])
%{full_name: "Gooney George"}
iex> Flop.Cursor.get_cursor_from_node(record, [:full_name])
%{full_name: "Gooney George"}
@spec get_cursors([any()], [atom()], [Flop.option()]) :: {binary(), binary()} | {nil, nil}
Retrieves the start and end cursors from a query result.
iex> results = [%{name: "Mary"}, %{name: "Paul"}, %{name: "Peter"}]
iex> order_by = [:name]
iex>
iex> {start_cursor, end_cursor} =
...> Flop.Cursor.get_cursors(results, order_by)
{"g3QAAAABdwRuYW1lbQAAAARNYXJ5", "g3QAAAABdwRuYW1lbQAAAAVQZXRlcg=="}
iex>
iex> Flop.Cursor.decode(start_cursor)
{:ok, %{name: "Mary"}}
iex> Flop.Cursor.decode(end_cursor)
{:ok, %{name: "Peter"}}If the result set is empty, the cursor values will be nil.
iex> Flop.Cursor.get_cursors([], [:id])
{nil, nil}The default function to retrieve the cursor value from the query result is
Flop.Cursor.get_cursor_from_node/2, which expects the query result to be a
map or a 2-tuple. You can set the cursor_value_func option to use
another function. Flop also comes with Flop.Cursor.get_cursor_from_edge/2.
If the records in the result set are not maps, you can define a custom cursor value function like this:
iex> results = [{"Mary", 1936}, {"Paul", 1937}, {"Peter", 1938}]
iex> cursor_func = fn {name, year}, order_fields ->
...> Enum.into(order_fields, %{}, fn
...> :name -> {:name, name}
...> :year -> {:year, year}
...> end)
...> end
iex> opts = [cursor_value_func: cursor_func]
iex>
iex> {start_cursor, end_cursor} =
...> Flop.Cursor.get_cursors(results, [:name, :year], opts)
{"g3QAAAACdwRuYW1lbQAAAARNYXJ5dwR5ZWFyYgAAB5A=",
"g3QAAAACdwRuYW1lbQAAAAVQZXRlcncEeWVhcmIAAAeS"}
iex>
iex> Flop.Cursor.decode(start_cursor)
{:ok, %{name: "Mary", year: 1936}}
iex> Flop.Cursor.decode(end_cursor)
{:ok, %{name: "Peter", year: 1938}}