Flop (Flop v0.8.0) View Source
Flop is a helper library for filtering, ordering and pagination with Ecto.
Usage
Derive Flop.Schema
in your Ecto schemas.
defmodule Pet do
use Ecto.Schema
@derive {Flop.Schema,
filterable: [:name, :species], sortable: [:name, :age]}
schema "pets" do
field :name, :string
field :age, :integer
field :species, :string
field :social_security_number, :string
end
end
Validate a parameter map to get a Flop.t/0
struct with Flop.validate/1
.
Add the Flop.t/0
to a Ecto.Queryable.t/0
with Flop.query/2
.
iex> params = %{"order_by" => ["name", "age"], "limit" => 5}
iex> {:ok, flop} = Flop.validate(params, for: Flop.Pet)
{:ok,
%Flop{
filters: [],
limit: 5,
offset: 0,
order_by: [:name, :age],
order_directions: nil,
page: nil,
page_size: nil
}}
iex> Flop.Pet |> Flop.query(flop)
#Ecto.Query<from p0 in Flop.Pet, order_by: [asc: p0.name, asc: p0.age], limit: ^5, offset: ^0>
Use Flop.validate_and_run/3
, Flop.validate_and_run!/3
, Flop.run/3
,
Flop.all/3
or Flop.meta/3
to query the database. Also consult the
readme for more details.
Link to this section Summary
Types
Represents the supported order direction values.
Represents the query parameters for filtering, ordering and pagination.
Functions
Applies the given Flop to the given queryable and returns all matchings entries.
Returns the total count of entries matching the filter conditions of the Flop.
Applies the filter
parameter of a Flop.t/0
to an Ecto.Queryable.t/0
.
Returns meta information for the given query and flop that can be used for building the pagination links.
Applies the order_by
and order_directions
parameters of a Flop.t/0
to an Ecto.Queryable.t/0
.
Applies the pagination parameters of a Flop.t/0
to an
Ecto.Queryable.t/0
.
Adds clauses for filtering, ordering and pagination to a
Ecto.Queryable.t/0
.
Applies the given Flop to the given queryable, retrieves the data and the meta data.
Validates a Flop.t/0
.
Same as Flop.validate/2
, but raises an Ecto.InvalidChangesetError
if the
parameters are invalid.
Validates the given flop parameters and retrieves the data and meta data on success.
Same as Flop.validate_and_run/3
, but raises on error.
Link to this section Types
Specs
order_direction() :: :asc | :asc_nulls_first | :asc_nulls_last | :desc | :desc_nulls_first | :desc_nulls_last
Represents the supported order direction values.
Specs
t() :: %Flop{ after: String.t() | nil, before: String.t() | nil, filters: [Flop.Filter.t()] | nil, first: pos_integer() | nil, last: pos_integer() | nil, limit: pos_integer() | nil, offset: non_neg_integer() | nil, order_by: [atom() | String.t()] | nil, order_directions: [order_direction()] | nil, page: pos_integer() | nil, page_size: pos_integer() | nil }
Represents the query parameters for filtering, ordering and pagination.
Fields
after
: Used for cursor-based pagination. Must be used withfirst
.before
: Used for cursor-based pagination. Must be used withlast
.limit
,offset
: Used for pagination. May not be used together withpage
andpage_size
.first
Used for cursor-based pagination. Can be used alone to begin pagination or withafter
last
Used for cursor-based pagination. Must be used withbefore
page
,page_size
: Used for pagination. May not be used together withlimit
andoffset
.order_by
: List of fields to order by. Fields can be restricted by derivingFlop.Schema
in your Ecto schema.order_directions
: List of order directions applied to the fields defined inorder_by
. If empty or the list is shorter than theorder_by
list,:asc
will be used as a default for each missing order direction.filters
: List of filters, seeFlop.Filter.t/0
.
Link to this section Functions
Specs
all(Ecto.Queryable.t(), t(), keyword()) :: [any()]
Applies the given Flop to the given queryable and returns all matchings entries.
iex> Flop.all(Flop.Pet, %Flop{}, repo: Flop.Repo)
[]
You can also configure a default repo in your config files:
config :flop, repo: MyApp.Repo
This allows you to omit the third argument:
iex> Flop.all(Flop.Pet, %Flop{})
[]
Specs
count(Ecto.Queryable.t(), t(), keyword()) :: non_neg_integer()
Returns the total count of entries matching the filter conditions of the Flop.
The pagination and ordering option are disregarded.
iex> Flop.count(Flop.Pet, %Flop{}, repo: Flop.Repo)
0
You can also configure a default repo in your config files:
config :flop, repo: MyApp.Repo
This allows you to omit the third argument:
iex> Flop.count(Flop.Pet, %Flop{})
0
Specs
filter(Ecto.Queryable.t(), t()) :: Ecto.Queryable.t()
Applies the filter
parameter of a Flop.t/0
to an Ecto.Queryable.t/0
.
Used by Flop.query/2
.
Specs
meta(Ecto.Queryable.t() | [any()], t(), keyword()) :: Flop.Meta.t()
Returns meta information for the given query and flop that can be used for building the pagination links.
iex> Flop.meta(Flop.Pet, %Flop{limit: 10}, repo: Flop.Repo)
%Flop.Meta{
current_offset: 0,
current_page: 1,
end_cursor: nil,
flop: %Flop{limit: 10},
has_next_page?: false,
has_previous_page?: false,
next_offset: nil,
next_page: nil,
page_size: 10,
previous_offset: nil,
previous_page: nil,
start_cursor: nil,
total_count: 0,
total_pages: 0
}
The function returns both the current offset and the current page, regardless
of the pagination type. If the offset lies in between pages, the current page
number is rounded up. This means that it is possible that the values for
current_page
and next_page
can be identical. This can only occur if you
use offset/limit based pagination with arbitrary offsets, but in that case,
you will use the previous_offset
, current_offset
and next_offset
values
to render the pagination links anyway, so this shouldn't be a problem.
Specs
order_by(Ecto.Queryable.t(), t()) :: Ecto.Queryable.t()
Applies the order_by
and order_directions
parameters of a Flop.t/0
to an Ecto.Queryable.t/0
.
Used by Flop.query/2
.
Specs
paginate(Ecto.Queryable.t(), t()) :: Ecto.Queryable.t()
Applies the pagination parameters of a Flop.t/0
to an
Ecto.Queryable.t/0
.
The function supports both offset
/limit
based pagination and
page
/page_size
based pagination.
If you validated the Flop.t/0
with Flop.validate/1
before, you can be
sure that the given Flop.t/0
only has pagination parameters set for one
pagination method. If you pass an unvalidated Flop.t/0
that has
pagination parameters set for multiple pagination methods, this function
will arbitrarily only apply one of the pagination methods.
Used by Flop.query/2
.
Specs
query(Ecto.Queryable.t(), t()) :: Ecto.Queryable.t()
Adds clauses for filtering, ordering and pagination to a
Ecto.Queryable.t/0
.
The parameters are represented by the Flop.t/0
type. Any nil
values
will be ignored.
Examples
iex> flop = %Flop{limit: 10, offset: 19}
iex> Flop.query(Flop.Pet, flop)
#Ecto.Query<from p0 in Flop.Pet, limit: ^10, offset: ^19>
Or enhance an already defined query:
iex> require Ecto.Query
iex> flop = %Flop{limit: 10}
iex> Flop.Pet |> Ecto.Query.where(species: "dog") |> Flop.query(flop)
#Ecto.Query<from p0 in Flop.Pet, where: p0.species == "dog", limit: ^10>
Specs
run(Ecto.Queryable.t(), t(), keyword()) :: {[any()], Flop.Meta.t()}
Applies the given Flop to the given queryable, retrieves the data and the meta data.
This function does not validate the given flop parameters. You can validate
the parameters with Flop.validate/2
or Flop.validate!/2
, or you can use
Flop.validate_and_run/3
or Flop.validate_and_run!/3
instead of this
function.
iex> {data, meta} = Flop.run(Flop.Pet, %Flop{})
iex> data == []
true
iex> match?(%Flop.Meta{}, meta)
true
Specs
validate(t() | map(), keyword()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
Validates a Flop.t/0
.
Examples
iex> params = %{"limit" => 10, "offset" => 0, "texture" => "fluffy"}
iex> Flop.validate(params)
{:ok,
%Flop{
filters: [],
limit: 10,
offset: 0,
order_by: nil,
order_directions: nil,
page: nil,
page_size: nil
}}
iex> flop = %Flop{offset: -1}
iex> {:error, changeset} = Flop.validate(flop)
iex> changeset.valid?
false
iex> changeset.errors
[
offset: {"must be greater than or equal to %{number}",
[validation: :number, kind: :greater_than_or_equal_to, number: 0]}
]
It also makes sure that only one pagination method is used.
iex> params = %{limit: 10, offset: 0, page: 5, page_size: 10}
iex> {:error, changeset} = Flop.validate(params)
iex> changeset.valid?
false
iex> changeset.errors
[limit: {"cannot combine multiple pagination types", []}]
If you derived Flop.Schema
in your Ecto schema to define the filterable
and sortable fields, you can pass the module name to the function to validate
that only allowed fields are used. The function will also apply any default
values set for the schema.
iex> params = %{"order_by" => ["species"]}
iex> {:error, changeset} = Flop.validate(params, for: Flop.Pet)
iex> changeset.valid?
false
iex> [order_by: {msg, [_, {_, enum}]}] = changeset.errors
iex> msg
"has an invalid entry"
iex> enum
[:name, :age]
Note that currently, trying to use an existing field that is not allowed as
seen above will result in the error message has an invalid entry
, while
trying to use a field name that does not exist in the schema (or more
precisely: a field name that doesn't exist as an atom) will result in
the error message is invalid
. This might change in the future.
Specs
Same as Flop.validate/2
, but raises an Ecto.InvalidChangesetError
if the
parameters are invalid.
Specs
validate_and_run(Ecto.Queryable.t(), map() | t(), keyword()) :: {:ok, {[any()], Flop.Meta.t()}} | {:error, Ecto.Changeset.t()}
Validates the given flop parameters and retrieves the data and meta data on success.
iex> {:ok, {[], %Flop.Meta{}}} =
...> Flop.validate_and_run(Flop.Pet, %Flop{}, for: Flop.Pet)
iex> {:error, %Ecto.Changeset{} = changeset} =
...> Flop.validate_and_run(Flop.Pet, %Flop{limit: -1})
iex> changeset.errors
[
limit: {"must be greater than %{number}",
[validation: :number, kind: :greater_than, number: 0]}
]
Options
for
: Passed toFlop.validate/2
.repo
: TheEcto.Repo
module. Required if no default repo is configured.get_cursor_value_func
: An arity-2 function to be used to retrieve an unencoded cursor value from a query result item and theorder_by
fields. Defaults toFlop.get_cursor_value_from_map/2
.
Specs
validate_and_run!(Ecto.Queryable.t(), map() | t(), keyword()) :: {[any()], Flop.Meta.t()}
Same as Flop.validate_and_run/3
, but raises on error.