# `Flop`
[🔗](https://github.com/woylie/flop/blob/0.26.4/lib/flop.ex#L1)

Flop is a helper library for filtering, ordering and pagination with Ecto.

## Usage

The simplest way of using this library is just to use
`Flop.validate_and_run/3` and `Flop.validate_and_run!/3`. Both functions
take a queryable and a parameter map, validate the parameters, run the query
and return the query results and the meta information.

    iex> Flop.Repo.insert_all(MyApp.Pet, [
    ...>   %{name: "Harry", age: 4, species: "C. lupus"},
    ...>   %{name: "Maggie", age: 1, species: "O. cuniculus"},
    ...>   %{name: "Patty", age: 2, species: "C. aegagrus"}
    ...> ])
    iex> params = %{order_by: ["name", "age"], page: 1, page_size: 2}
    iex> {:ok, {results, meta}} =
    ...>   Flop.validate_and_run(
    ...>     MyApp.Pet,
    ...>     params,
    ...>     repo: Flop.Repo
    ...>   )
    iex> Enum.map(results, & &1.name)
    ["Harry", "Maggie"]
    iex> meta.total_count
    3
    iex> meta.total_pages
    2
    iex> meta.has_next_page?
    true

Under the hood, these functions just call `Flop.validate/2` and `Flop.run/3`,
which in turn calls `Flop.all/3` and `Flop.meta/3`. If you need finer control
about if and when to execute each step, you can call those functions directly.

See `Flop.Meta` for descriptions of the meta fields.

## Global configuration

You can set some global options like the default Ecto repo via the application
environment. All global options can be overridden by passing them directly to
the functions or configuring the options for a schema module via
`Flop.Schema`.

    import Config

    config :flop, repo: MyApp.Repo

See `t:Flop.option/0` for a description of all available options.

## Config modules

Instead of setting global options in the application environment, you can also
create a Flop config module. This is especially useful in an umbrella
application, or if you have multiple Repos.

```elixir
defmodule MyApp.Flop do
  use Flop, repo: MyApp.Repo, default_limit: 25
end
```

> #### `use Flop` {: .info}
>
> When you `use Flop`, the Flop module will define wrapper functions around
> all of the `Flop` functions that take a query, the Flop parameters, and
> options as arguments. The options passed to `use Flop` will be used as
> default options in all the wrapper functions, but you can still override
> them.

The wrapped functions are:

- `Flop.all/3`
- `Flop.count/3`
- `Flop.filter/3`
- `Flop.meta/3`
- `Flop.order_by/3`
- `Flop.paginate/3`
- `Flop.query/3`
- `Flop.run/3`
- `Flop.validate_and_run/3`
- `Flop.validate_and_run!/3`

So instead of using `Flop.validate_and_run/3`, you would call
`MyApp.Flop.validate_and_run/3`.

If you have both a config module and a global application config, Flop will
fall back to the application config if an option is not set.

See `t:Flop.option/0` for a description of all available options.

## Schema options

You can set some options for a schema by deriving `Flop.Schema`. The options
are evaluated at the validation step.

    defmodule Pet do
      use Ecto.Schema

      @derive {Flop.Schema,
               filterable: [:name, :species],
               sortable: [:name, :age],
               default_limit: 20,
               max_limit: 100}

      schema "pets" do
        field :name, :string
        field :age, :integer
        field :species, :string
        field :social_security_number, :string
      end
    end

You need to pass the schema to `Flop.validate/2` or any function that
includes the validation step with the `:for` option.

    iex> params = %{"order_by" => ["name", "age"], "limit" => 5}
    iex> {:ok, flop} = Flop.validate(params, for: MyApp.Pet)
    iex> flop.limit
    5

    iex> params = %{"order_by" => ["name", "age"], "limit" => 10_000}
    iex> {:error, meta} = Flop.validate(params, for: MyApp.Pet)
    iex> [limit: [{msg, _}]] = meta.errors
    iex> msg
    "must be less than or equal to %{number}"

    iex> params = %{"order_by" => ["name", "age"], "limit" => 10_000}
    iex> {:error, %Flop.Meta{} = meta} =
    ...>   Flop.validate_and_run(
    ...>     MyApp.Pet,
    ...>     params,
    ...>     for: MyApp.Pet
    ...>   )
    iex> [limit: [{msg, _}]] = meta.errors
    iex> msg
    "must be less than or equal to %{number}"

## Ordering

To add an ordering clause to a query, you need to set the `:order_by` and
optionally the `:order_directions` parameter. `:order_by` should be the list
of fields, while `:order_directions` is a list of `t:Flop.order_direction/0`.
`:order_by` and `:order_directions` are zipped when generating the `ORDER BY`
clause. If no order directions are given, `:asc` is used as default.

    iex> params = %{
    ...>   "order_by" => ["name", "age"],
    ...>   "order_directions" => ["asc", "desc"]
    ...> }
    iex> {:ok, flop} = Flop.validate(params)
    iex> flop.order_by
    [:name, :age]
    iex> flop.order_directions
    [:asc, :desc]

Flop uses these two fields instead of a keyword list, so that the order
instructions can be easily passed in a query string.

## Pagination

For queries using `OFFSET` and `LIMIT`, you have the choice between
page-based pagination parameters:

    %{page: 5, page_size: 20}

and offset-based pagination parameters:

    %{offset: 100, limit: 20}

For cursor-based pagination, you can either use `:first`/`:after` or
`:last`/`:before`. You also need to pass the `:order_by` parameter or set a
default order for the schema via `Flop.Schema`.

    iex> Flop.Repo.insert_all(MyApp.Pet, [
    ...>   %{name: "Harry", age: 4, species: "C. lupus"},
    ...>   %{name: "Maggie", age: 1, species: "O. cuniculus"},
    ...>   %{name: "Patty", age: 2, species: "C. aegagrus"}
    ...> ])
    iex>
    iex> # forward (first/after)
    iex>
    iex> params = %{first: 2, order_by: [:species, :name]}
    iex> {:ok, {results, meta}} = Flop.validate_and_run(MyApp.Pet, params)
    iex> Enum.map(results, & &1.name)
    ["Patty", "Harry"]
    iex> meta.has_next_page?
    true
    iex> end_cursor = meta.end_cursor
    "g3QAAAACdwRuYW1lbQAAAAVIYXJyeXcHc3BlY2llc20AAAAIQy4gbHVwdXM="
    iex> params = %{first: 2, after: end_cursor, order_by: [:species, :name]}
    iex> {:ok, {results, meta}} = Flop.validate_and_run(MyApp.Pet, params)
    iex> Enum.map(results, & &1.name)
    ["Maggie"]
    iex> meta.has_next_page?
    false
    iex>
    iex> # backward (last/before)
    iex>
    iex> params = %{last: 2, order_by: [:species, :name]}
    iex> {:ok, {results, meta}} = Flop.validate_and_run(MyApp.Pet, params)
    iex> Enum.map(results, & &1.name)
    ["Harry", "Maggie"]
    iex> meta.has_previous_page?
    true
    iex> start_cursor = meta.start_cursor
    "g3QAAAACdwRuYW1lbQAAAAVIYXJyeXcHc3BlY2llc20AAAAIQy4gbHVwdXM="
    iex> params = %{last: 2, before: start_cursor, order_by: [:species, :name]}
    iex> {:ok, {results, meta}} = Flop.validate_and_run(MyApp.Pet, params)
    iex> Enum.map(results, & &1.name)
    ["Patty"]
    iex> meta.has_previous_page?
    false

By default, it is assumed that the query result is a list of maps or structs.
If your query returns a different data structure, you can pass the
`:cursor_value_func` option to retrieve the cursor values. See
`t:Flop.option/0` and `Flop.Cursor` for more information.

You can restrict which pagination types are available. See `t:Flop.option/0`
for details.

## Filters

Filters can be passed as a list of maps. It is recommended to define the
filterable fields for a schema using `Flop.Schema`.

    iex> Flop.Repo.insert_all(MyApp.Pet, [
    ...>   %{name: "Harry", age: 4, species: "C. lupus"},
    ...>   %{name: "Maggie", age: 1, species: "O. cuniculus"},
    ...>   %{name: "Patty", age: 2, species: "C. aegagrus"}
    ...> ])
    iex>
    iex> params = %{filters: [%{field: :name, op: :like, value: "Mag"}]}
    iex> {:ok, {results, meta}} = Flop.validate_and_run(MyApp.Pet, params)
    iex> meta.total_count
    1
    iex> [pet] = results
    iex> pet.name
    "Maggie"

See `t:Flop.Filter.op/0` for a list of all available filter operators.

## GraphQL and Relay

The parameters used for cursor-based pagination follow the Relay
specification, so you can just pass the arguments you get from the client on
to Flop.

`Flop.Relay` can convert the query results returned by
`Flop.validate_and_run/3` into `Edges` and `PageInfo` formats required for
Relay connections.

For example, if you have a context module like this:

    defmodule MyApp.Flora
      import Ecto.query, warn: false

      alias MyApp.Flora.Plant

      def list_plants_by_continent(%Continent{} = continent, %{} = args) do
        Plant
        |> where(continent_id: ^continent.id)
        |> Flop.validate_and_run(args, for: Plant)
      end
    end

Then your Absinthe resolver for the `plants` connection may look something
like this:

    def list_plants(args, %{source: %Continent{} = continent}) do
      with {:ok, result} <-
             Flora.list_plants_by_continent(continent, args) do
        {:ok, Flop.Relay.connection_from_result(result)}
      end
    end

# `aliases`
*since 0.18.0* 

```elixir
@spec aliases(t(), module()) :: [atom()]
```

Returns the names of the alias fields that are required for the order clause
of the given Flop.

The second argument is the schema module that derives `Flop.Schema`.

For example, your schema module might define an alias field called
`:pet_count`.

    @derive {
      Flop.Schema,
      filterable: [],
      sortable: [:name, :pet_count],
      adapter_opts: [
        alias_fields: [:pet_count]
      ]
    }

If you pass a Flop that orders by the `:pet_count` field, the returned list
will include the `:pet_count` alias.

    iex> aliases(%Flop{order_by: [:name, :pet_count]}, MyApp.Owner)
    [:pet_count]

If on the other hand only normal fields are used in the `order_by` parameter,
an empty list will be returned.

    iex> aliases(%Flop{order_by: [:name]}, MyApp.Owner)
    []

You can use this to dynamically build the select clause needed for the query.

For more information about alias fields, refer to the module documentation of
`Flop.Schema`.

# `all`
*since 0.6.0* 

```elixir
@spec all(Ecto.Queryable.t(), t(), [option()]) :: [any()]
```

Applies the given Flop to the given queryable and returns all matchings
entries.

    iex> Flop.all(MyApp.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(MyApp.Pet, %Flop{}, for: MyApp.Pet)
    []

Note that when using cursor-based pagination, the applied limit will be
`first + 1` or `last + 1`. The extra record is removed by `Flop.run/3`, but
not by this function.

This function will apply the `default_limit` of the given schema or repo
unless you provide a custom limit or pass `default_limit: false` as an option.

    iex> Flop.all(MyApp.Pet, %Flop{}, for: MyApp.Pet, default_limit: false)
    []

Also note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

# `count`
*since 0.6.0* 

```elixir
@spec count(Ecto.Queryable.t(), t(), [option()]) :: 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(MyApp.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(MyApp.Pet, %Flop{})
    0

You can override the default query by passing the `:count_query` option. This
doesn't make a lot of sense when you use `count/3` directly, but allows you to
optimize the count query when you use one of the `run/3`,
`validate_and_run/3` and `validate_and_run!/3` functions.

    query = join(Pet, :left, [p], o in assoc(p, :owner))
    count_query = Pet
    count(query, %Flop{}, count_query: count_query, for: Pet)

The filter parameters of the given Flop are applied to the custom count query.

If for some reason you already have the count, you can pass it as the `:count`
option.

    count(query, %Flop{}, count: 42, for: Pet)

If you pass both the `:count` and the `:count_query` options, the `:count`
option will take precedence.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

Note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

# `filter`

```elixir
@spec filter(Ecto.Queryable.t(), t(), [option()]) :: Ecto.Queryable.t()
```

Applies the `filter` parameter of a `t:Flop.t/0` to an `t:Ecto.Queryable.t/0`.

Used by `Flop.query/2`.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

Note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

# `meta`
*since 0.6.0* 

```elixir
@spec meta(Ecto.Queryable.t() | [any()], t(), [option()]) :: Flop.Meta.t()
```

Returns meta information for the given query and flop that can be used for
building the pagination links.

    iex> Flop.meta(MyApp.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,
      opts: [repo: Flop.Repo],
      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.

Unless cursor-based pagination is used, this function will run a query to
figure get the total count of matching records.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

# `named_bindings`
*since 0.16.0* 

```elixir
@spec named_bindings(t(), module(), keyword()) :: [atom()]
```

Returns the names of the bindings that are required for the filters and order
clauses of the given Flop.

The second argument is the schema module that derives `Flop.Schema`.

For example, your schema module might define a join field called `:owner_age`.

    @derive {
      Flop.Schema,
      filterable: [:name, :owner_age],
      sortable: [:name, :owner_age],
      adapter_opts: [
        join_fields: [
          owner_age: [
            binding: :owner,
            field: :age
          ]
        ]
      ]
    }

If you pass a Flop with a filter on the `:owner_age` field, the returned list
will include the `:owner` binding.

    iex> named_bindings(
    ...>   %Flop{
    ...>     filters: [%Flop.Filter{field: :owner_age, op: :==, value: 5}]
    ...>   },
    ...>   MyApp.Pet
    ...> )
    [:owner]

If on the other hand only normal fields or compound fields are used in the
filter and order options, or if the filter values are nil, an empty list will
be returned.

    iex> named_bindings(
    ...>   %Flop{
    ...>     filters: [
    ...>       %Flop.Filter{field: :name, op: :==, value: "George"},
    ...>       %Flop.Filter{field: :owner_age, op: :==, value: nil}
    ...>     ]
    ...>   },
    ...>   MyApp.Pet
    ...> )
    []

If a join field is part of a compound field, it will be returned.

    iex> named_bindings(
    ...>   %Flop{
    ...>     filters: [
    ...>       %Flop.Filter{field: :pet_and_owner_name, op: :==, value: "Mae"}
    ...>     ]
    ...>   },
    ...>   MyApp.Pet
    ...> )
    [:owner]

For custom fields, you can set the `:bindings` option when you derive the
`Flop.Schema` protocol.

You can use this to dynamically build the join clauses needed for the query.
See also `Flop.with_named_bindings/4`.

For more information about join fields, refer to the module documentation of
`Flop.Schema`.

## Options

- `:order` - If `false`, only bindings needed for filtering are included.
  Defaults to `true`.

# `order_by`

```elixir
@spec order_by(Ecto.Queryable.t(), t(), [option()]) :: Ecto.Queryable.t()
```

Applies the `order_by` and `order_directions` parameters of a `t:Flop.t/0`
to an `t:Ecto.Queryable.t/0`.

Used by `Flop.query/2`.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

Note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

# `paginate`

```elixir
@spec paginate(Ecto.Queryable.t(), t(), [option()]) :: Ecto.Queryable.t()
```

Applies the pagination parameters of a `t:Flop.t/0` to an
`t:Ecto.Queryable.t/0`.

The function supports both `offset`/`limit` based pagination and
`page`/`page_size` based pagination.

If you validated the `t:Flop.t/0` with `Flop.validate/1` before, you can be
sure that the given `t:Flop.t/0` only has pagination parameters set for one
pagination method. If you pass an unvalidated `t: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`.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

Note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

# `query`

```elixir
@spec query(Ecto.Queryable.t(), t(), [option()]) :: Ecto.Queryable.t()
```

Adds clauses for filtering, ordering and pagination to a
`t:Ecto.Queryable.t/0`.

The parameters are represented by the `t:Flop.t/0` type. Any `nil` values
will be ignored.

This function does _not_ validate or apply default parameters to the given
Flop struct. Be sure to validate any user-generated parameters with
`validate/2` or `validate!/2` before passing them to this function.

## Examples

    iex> flop = %Flop{limit: 10, offset: 19}
    iex> Flop.query(MyApp.Pet, flop)
    #Ecto.Query<from p0 in MyApp.Pet, limit: ^10, offset: ^19>

Or enhance an already defined query:

    iex> require Ecto.Query
    iex> flop = %Flop{limit: 10}
    iex> q = Ecto.Query.where(MyApp.Pet, species: "dog")
    iex> Flop.query(q, flop, for: MyApp.Pet)
    #Ecto.Query<from p0 in MyApp.Pet, where: p0.species == "dog", limit: ^10>

Note that when using cursor-based pagination, the applied limit will be
`first + 1` or `last + 1`. The extra record is removed by `Flop.run/3`.

Also note that you will need to pass the `for` option in order for Flop to be
able to find your join, compound, alias and custom field configuration.

# `run`
*since 0.6.0* 

```elixir
@spec run(Ecto.Queryable.t(), t(), [option()]) :: {[any()], Flop.Meta.t()}
```

Applies the given Flop to the given queryable, retrieves the data and the
meta data.

This function does _not_ validate or apply default parameters to 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.

Note that you will need to pass the `for` option to both to `validate/2`,
`validate!/2` and `run/3`. Otherwise, Flop would not know how to find your
field configuration (join fields, alias fields, custom fields, compound
fields).

    iex> opts = [for: MyApp.Pet]
    iex> flop = Flop.validate!(%{}, opts)
    iex> {data, meta} = Flop.run(MyApp.Pet, flop, opts)
    iex> data == []
    true
    iex> match?(%Flop.Meta{}, meta)
    true

See the documentation for `Flop.validate_and_run/3` for supported options.

# `validate`

```elixir
@spec validate(t() | map(), [option()]) :: {:ok, t()} | {:error, Flop.Meta.t()}
```

Validates a `t: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, %Flop.Meta{} = meta} = Flop.validate(flop)
    iex> meta.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, %Flop.Meta{} = meta} = Flop.validate(params)
    iex> meta.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, %Flop.Meta{} = meta} = Flop.validate(params, for: MyApp.Pet)
    iex> [order_by: [{msg, [_, {_, enum}]}]] = meta.errors
    iex> msg
    "has an invalid entry"
    iex> enum
    [:name, :age, :owner_name, :owner_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.

# `validate!`
*since 0.5.0* 

```elixir
@spec validate!(t() | map(), [option()]) :: t()
```

Same as `Flop.validate/2`, but raises an `Ecto.InvalidChangesetError` if the
parameters are invalid.

# `validate_and_run`
*since 0.6.0* 

```elixir
@spec validate_and_run(Ecto.Queryable.t(), map() | t(), [option()]) ::
  {:ok, {[any()], Flop.Meta.t()}} | {:error, Flop.Meta.t()}
```

Validates the given flop parameters and retrieves the data and meta data on
success.

    iex> {:ok, {[], %Flop.Meta{}}} =
    ...>   Flop.validate_and_run(MyApp.Pet, %Flop{}, for: MyApp.Pet)
    iex> {:error, %Flop.Meta{} = meta} =
    ...>   Flop.validate_and_run(MyApp.Pet, %Flop{limit: -1})
    iex> meta.errors
    [
      limit: [
        {"must be greater than %{number}",
         [validation: :number, kind: :greater_than, number: 0]}
      ]
    ]

## Options

- `for`: Passed to `Flop.validate/2`. Also used to look up the join, alias,
  compound and custom field configuration.
- `repo`: The `Ecto.Repo` module. Required if no default repo is configured.
- `cursor_value_func`: An arity-2 function to be used to retrieve an
  unencoded cursor value from a query result item and the `order_by` fields.
  Defaults to `Flop.Cursor.get_cursor_from_node/2`.
- `count_query`: Lets you override the base query for counting, e.g. if you
  don't want to include unnecessary joins. The filter parameters are applied
  to the given query. See also `Flop.count/3`.

# `validate_and_run!`
*since 0.6.0* 

```elixir
@spec validate_and_run!(Ecto.Queryable.t(), map() | t(), [option()]) ::
  {[any()], Flop.Meta.t()}
```

Same as `Flop.validate_and_run/3`, but raises on error.

# `with_named_bindings`
*since 0.19.0* 

```elixir
@spec with_named_bindings(
  Ecto.Queryable.t(),
  t(),
  (Ecto.Queryable.t(), atom() -&gt; Ecto.Queryable.t()),
  keyword()
) :: Ecto.Queryable.t()
```

Applies a callback function to a query for all named bindings that are
necessary for the given `Flop` parameters.

The callback function must accept a queryable and the name of the binding and
return a query that includes the given binding. This is the same as
`Ecto.Query.with_named_binding/3`, except that the callback function _must_
take the second argument.

## Options

- `:for` (required) - The schema module that derives `Flop.Schema`.
- `:order` - If `false`, only bindings needed for filtering are included.
  Defaults to `true`.

## Example

    def list_pets(params) do
      opts = [for: Pet]

      with {:ok, flop} <- Flop.validate(params, opts) do
        Pet
        |> Flop.with_named_bindings(flop, &join_pet_assocs/2, opts)
        |> Flop.run(flop, opts)
      end
    end

    defp join_pet_assocs(query, :owner) do
      join(query, [p], o in assoc(p, :owner), as: :owner)
    end

    defp join_pet_assocs(query, :toys) do
      join(query, [p], t in assoc(p, :toys), as: :toys)
    end

Since the callback function has the same arguments and return value as the one
passed to `Ecto.Query.with_named_binding/3`, you can reuse the function to add
any other bindings you may need for the query besides the ones for Flop
filters.

This also means you can use `Ecto.Query.with_named_binding/3` to recursively
add bindings in case you need intermediate joins to get to a nested
association.

    def list_owners(params) do
      opts = [for: Owner]

      with {:ok, flop} <- Flop.validate(params, opts) do
        Pet
        |> Flop.with_named_bindings(flop, &join_owner_assocs/2, opts)
        |> Flop.run(flop, opts)
      end
    end

    defp join_owner_assocs(query, :pets) do
      join(query, [o], p in assoc(o, :pets), as: :pets)
    end

    defp join_owner_assocs(query, :toys) do
      query
      |> Ecto.Query.with_named_binding(:pets, &join_owner_assocs/2)
      |> join(query, [pets: p], t in assoc(p, :toys), as: :toys)
    end

# `current_order`
*since 0.15.0* 

```elixir
@spec current_order(t(), atom()) :: order_direction() | nil
```

Returns the current order direction for the given field.

## Examples

    iex> flop = %Flop{order_by: [:name, :age], order_directions: [:desc]}
    iex> current_order(flop, :name)
    :desc
    iex> current_order(flop, :age)
    :asc
    iex> current_order(flop, :species)
    nil

# `map_to_filter_params`
*since 0.14.0* 

```elixir
@spec map_to_filter_params(
  map(),
  keyword()
) :: [map()]
```

Converts a map of filter conditions into a list of Flop filter params.

The default operator is `:==`. `nil` values are excluded from the result.

    iex> params = map_to_filter_params(
    ...>   %{name: "George", age: 8, species: nil}
    ...> )
    iex> Enum.sort(params)
    [
      %{field: :age, op: :==, value: 8},
      %{field: :name, op: :==, value: "George"}
    ]

    iex> params = map_to_filter_params(
    ...>   %{"name" => "George", "age" => 8, "cat" => true}
    ...>  )
    iex> Enum.sort(params)
    [
      %{"field" => "age", "op" => :==, "value" => 8},
      %{"field" => "cat", "op" => :==, "value" => true},
      %{"field" => "name", "op" => :==, "value" => "George"}
    ]

You can optionally pass a mapping from field names to operators as a map
with atom keys.

    iex> params = map_to_filter_params(
    ...>   %{name: "George", age: 8, species: nil},
    ...>   operators: %{name: :ilike_and}
    ...> )
    iex> Enum.sort(params)
    [
      %{field: :age, op: :==, value: 8},
      %{field: :name, op: :ilike_and, value: "George"}
    ]

    iex> map_to_filter_params(
    ...>   %{"name" => "George", "age" => 8, "cat" => true},
    ...>   operators: %{name: :ilike_and, age: :<=}
    ...> )
    [
      %{"field" => "age", "op" => :<=, "value" => 8},
      %{"field" => "cat", "op" => :==, "value" => true},
      %{"field" => "name", "op" => :ilike_and, "value" => "George"}
    ]

You can also pass a map to rename fields.

    iex> params = map_to_filter_params(
    ...>   %{s: "George", age: 8, species: nil},
    ...>   rename: %{s: :name}
    ...> )
    iex> Enum.sort(params)
    [
      %{field: :age, op: :==, value: 8},
      %{field: :name, op: :==, value: "George"}
    ]

    iex> map_to_filter_params(
    ...>   %{"s" => "George", "cat" => true},
    ...>   rename: %{s: :name, cat: :dog}
    ...> )
    [
      %{"field" => "dog", "op" => :==, "value" => true},
      %{"field" => "name", "op" => :==, "value" => "George"}
    ]

If both a rename option and an operator are set for a field, the operator
option needs to use the new field name.

    iex> map_to_filter_params(
    ...>   %{n: "George"},
    ...>   rename: %{n: :name},
    ...>   operators: %{name: :ilike_or}
    ...> )
    [%{field: :name, op: :ilike_or, value: "George"}]

See also `Flop.Filter.new/2`.

# `nest_filters`
*since 0.15.0* 

```elixir
@spec nest_filters(map(), [atom() | String.t()], keyword()) :: map()
```

Converts key/value filter parameters at the root of a map, converts them into
a list of filter parameter maps and nests them under the `:filters` key.

This is useful in cases where you get some or all filter parameters as
key/value pairs instead of a full map with operators, for example when you
expose certain filters with fixed operators on an API, or if you want to
reflect some or all filters in the URL as path parameters or simple query
parameters (e.g. `/posts/{tag}` or `/posts?s=searchterm`).

The given map should have either string keys or atom keys. Passing a map with
mixed keys will lead to unexpected results and will cause an Ecto error when
the return value is passed to one of the validation functions.

The second argument is a list of fields as atoms.

The `opts` argument is passed to `map_to_filter_params/2`.

The function returns a map that eventually needs to be passed to one of the
Flop validation functions (any `Flop.validate*` function) before it can be
used to make a query.

## Examples

Map with atom keys

    iex> nest_filters(%{name: "Peter", page_size: 10}, [:name])
    %{filters: [%{field: :name, op: :==, value: "Peter"}], page_size: 10}

Map with string keys

    iex> nest_filters(%{"name" => "Peter"}, [:name])
    %{"filters" => [%{"field" => "name", "op" => :==, "value" =>  "Peter"}]}

Specifying operators

    iex> nest_filters(%{name: "Peter"}, [:name], operators: %{name: :!=})
    %{filters: [%{field: :name, op: :!=, value: "Peter"}]}

Renaming fields

    iex> nest_filters(%{nombre: "Peter", page_size: 10}, [:nombre],
    ...>   rename: %{nombre: :name}
    ...> )
    %{filters: [%{field: :name, op: :==, value: "Peter"}], page_size: 10}

    iex> nest_filters(%{"nombre" => "Peter"}, [:nombre],
    ...>   rename: %{nombre: :name}
    ...> )
    %{"filters" => [%{"field" => "name", "op" => :==, "value" =>  "Peter"}]}

    iex> nest_filters(%{"nombre" => "Peter"}, [:nombre],
    ...>   rename: %{nombre: :name},
    ...>   operators: %{name: :like}
    ...> )
    %{"filters" => [%{"field" => "name", "op" => :like, "value" =>  "Peter"}]}

If the map already has a `filters` key, the extracted filters are added to
the existing filters.

    iex> nest_filters(%{name: "Peter", filters: [%{field: "age", op: ">", value: 25}]}, [:name])
    %{filters: [%{field: "age", op: ">", value: 25}, %{field: :name, op: :==, value: "Peter"}]}

    iex> nest_filters(%{"name" => "Peter", "filters" => [%{"field" => "age", "op" => ">", "value" => 25}]}, [:name])
    %{"filters" => [%{"field" => "age", "op" => ">", "value" => 25}, %{"field" => "name", "op" => :==, "value" => "Peter"}]}

If the existing filters are formatted as a map with integer indexes as keys as
produced by a form, the map is converted to a list first.

    iex> nest_filters(%{name: "Peter", filters: %{"0" => %{field: "age", op: ">", value: 25}}}, [:name])
    %{filters: [%{field: "age", op: ">", value: 25}, %{field: :name, op: :==, value: "Peter"}]}

    iex> nest_filters(%{"name" => "Peter", "filters" => %{"0" => %{"field" => "age", "op" => ">", "value" => 25}}}, [:name])
    %{"filters" => [%{"field" => "age", "op" => ">", "value" => 25}, %{"field" => "name", "op" => :==, "value" => "Peter"}]}

# `push_order`
*since 0.10.0* 

```elixir
@spec push_order(t(), atom() | String.t(), keyword()) :: t()
```

Updates the `order_by` and `order_directions` values of a `Flop` struct.

- If the field is not in the current `order_by` value, it will be prepended to
  the list. By default, the order direction for the field will be set to
  `:asc`.
- If the field is already at the front of the `order_by` list, the order
  direction will be reversed.
- If the field is already in the list, but not at the front, it will be moved
  to the front and the order direction will be set to `:asc` (or the custom
  asc direction supplied in the `:directions` option).
- If the `:directions` option——a 2-element tuple——is passed, the first and
  second elements will be used as custom sort declarations for ascending and
  descending, respectively.

## Examples

    iex> flop = push_order(%Flop{}, :name)
    iex> flop.order_by
    [:name]
    iex> flop.order_directions
    [:asc]
    iex> flop = push_order(flop, :age)
    iex> flop.order_by
    [:age, :name]
    iex> flop.order_directions
    [:asc, :asc]
    iex> flop = push_order(flop, :age)
    iex> flop.order_by
    [:age, :name]
    iex> flop.order_directions
    [:desc, :asc]
    iex> flop = push_order(flop, :species)
    iex> flop.order_by
    [:species, :age, :name]
    iex> flop.order_directions
    [:asc, :desc, :asc]
    iex> flop = push_order(flop, :age)
    iex> flop.order_by
    [:age, :species, :name]
    iex> flop.order_directions
    [:asc, :asc, :asc]

By default, the function toggles between `:asc` and `:desc`. You can override
this with the `:directions` option.

    iex> directions = {:asc_nulls_last, :desc_nulls_last}
    iex> flop = push_order(%Flop{}, :ttfb, directions: directions)
    iex> flop.order_by
    [:ttfb]
    iex> flop.order_directions
    [:asc_nulls_last]
    iex> flop = push_order(flop, :ttfb, directions: directions)
    iex> flop.order_by
    [:ttfb]
    iex> flop.order_directions
    [:desc_nulls_last]

This also allows you to sort in descending order initially.

    iex> directions = {:desc, :asc}
    iex> flop = push_order(%Flop{}, :ttfb, directions: directions)
    iex> flop.order_by
    [:ttfb]
    iex> flop.order_directions
    [:desc]
    iex> flop = push_order(flop, :ttfb, directions: directions)
    iex> flop.order_by
    [:ttfb]
    iex> flop.order_directions
    [:asc]

If a string is passed as the second argument, it will be converted to an atom
using `String.to_existing_atom/1`. If the atom does not exist, the `Flop`
struct will be returned unchanged.

    iex> flop = push_order(%Flop{}, "name")
    iex> flop.order_by
    [:name]
    iex> flop = push_order(%Flop{}, "this_atom_does_not_exist")
    iex> flop.order_by
    nil

Since the pagination cursor depends on the sort order, the `:before` and
`:after` parameters are reset.

    iex> push_order(%Flop{order_by: [:id], after: "ABC"}, :name)
    %Flop{order_by: [:name, :id], order_directions: [:asc], after: nil}
    iex> push_order(%Flop{order_by: [:id], before: "DEF"}, :name)
    %Flop{order_by: [:name, :id], order_directions: [:asc], before: nil}

# `reset_cursors`
*since 0.15.0* 

```elixir
@spec reset_cursors(t()) :: t()
```

Removes the `after` and `before` cursors from a Flop struct.

## Example

    iex> reset_cursors(%Flop{after: "A"})
    %Flop{}

    iex> reset_cursors(%Flop{before: "A"})
    %Flop{}

# `reset_filters`
*since 0.15.0* 

```elixir
@spec reset_filters(t()) :: t()
```

Removes all filters from a Flop struct.

## Example

    iex> reset_filters(%Flop{filters: [
    ...>   %Flop.Filter{field: :name, value: "Jim"}
    ...> ]})
    %Flop{filters: []}

# `reset_order`
*since 0.15.0* 

```elixir
@spec reset_order(t()) :: t()
```

Removes the order parameters from a Flop struct.

## Example

    iex> reset_order(%Flop{order_by: [:name], order_directions: [:asc]})
    %Flop{order_by: nil, order_directions: nil}

# `set_cursor`
*since 0.15.0* 

```elixir
@spec set_cursor(Flop.Meta.t(), :previous | :next) :: t()
```

Takes a `Flop.Meta` struct and returns a `Flop` struct with updated cursor
pagination params for going to either the previous or the next page.

See `to_previous_cursor/1` and `to_next_cursor/1` for details.

## Examples

    iex> set_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_previous_page?: true, start_cursor: "b"
    ...>   },
    ...>   :previous
    ...> )
    %Flop{last: 5, before: "b"}

    iex> set_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_next_page?: true, end_cursor: "b"
    ...>   },
    ...>   :next
    ...> )
    %Flop{first: 5, after: "b"}

# `set_offset`
*since 0.15.0* 

```elixir
@spec set_offset(t(), non_neg_integer() | binary()) :: t()
```

Sets the offset value of a `Flop` struct while also removing/converting
pagination parameters for other pagination types.

    iex> set_offset(%Flop{limit: 10, offset: 10}, 20)
    %Flop{offset: 20, limit: 10}

    iex> set_offset(%Flop{page: 5, page_size: 10}, 20)
    %Flop{limit: 10, offset: 20, page: nil, page_size: nil}

    iex> set_offset(%Flop{limit: 10, offset: 10}, "20")
    %Flop{offset: 20, limit: 10}

The offset will not be allowed to go below 0.

    iex> set_offset(%Flop{}, -5)
    %Flop{offset: 0}

# `set_page`
*since 0.12.0* 

```elixir
@spec set_page(t(), pos_integer() | binary()) :: t()
```

Sets the page value of a `Flop` struct while also removing/converting
pagination parameters for other pagination types.

    iex> set_page(%Flop{page: 2, page_size: 10}, 6)
    %Flop{page: 6, page_size: 10}

    iex> set_page(%Flop{limit: 10, offset: 20}, 8)
    %Flop{limit: nil, offset: nil, page: 8, page_size: 10}

    iex> set_page(%Flop{page: 2, page_size: 10}, "6")
    %Flop{page: 6, page_size: 10}

The page number will not be allowed to go below 1.

    iex> set_page(%Flop{}, -5)
    %Flop{page: 1}

# `to_next_cursor`
*since 0.15.0* 

```elixir
@spec to_next_cursor(Flop.Meta.t()) :: t()
```

Takes a `Flop.Meta` struct and returns a `Flop` struct with updated cursor
pagination params for going to the next page.

## Examples

    iex> to_next_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_next_page?: true, end_cursor: "b"
    ...>   }
    ...> )
    %Flop{first: 5, after: "b"}

    iex> to_next_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{last: 5, before: "b"},
    ...>     has_next_page?: true, end_cursor: "a"
    ...>   }
    ...> )
    %Flop{first: 5, after: "a"}

If there is no next page, the `Flop` struct is returned unchanged.

    iex> to_next_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_next_page?: false, start_cursor: "b"
    ...>   }
    ...> )
    %Flop{first: 5, after: "a"}

It can happen that `has_next_page?` is `true`, but no end cursor is set,
for example if the user is on the second page and switches to the previous
page right after the only item on the first page is deleted. Since the result
set is empty and there is no end cursor in this case, this function sets the
new `after` parameter to `nil`. By doing this, the user is sent to the first
page without skipping any items.

    iex> to_next_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_next_page?: true, end_cursor: nil
    ...>   }
    ...> )
    %Flop{first: 5, after: nil}

    iex> to_next_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{last: 5, before: "b"},
    ...>     has_next_page?: true, end_cursor: nil
    ...>   }
    ...> )
    %Flop{first: 5, after: nil}

# `to_next_offset`
*since 0.15.0* 

```elixir
@spec to_next_offset(t(), non_neg_integer() | nil) :: t()
```

Sets the offset of a Flop struct to the next page depending on the limit.

If the total count is given as the second argument, the offset will not be
increased if the last page has already been reached. You can get the total
count from the `Flop.Meta` struct. If the Flop has an offset beyond the total
count, the offset will be set to the last page.

## Examples

    iex> to_next_offset(%Flop{offset: 10, limit: 5})
    %Flop{offset: 15, limit: 5}

    iex> to_next_offset(%Flop{offset: 15, limit: 5}, 21)
    %Flop{offset: 20, limit: 5}

    iex> to_next_offset(%Flop{offset: 15, limit: 5}, 20)
    %Flop{offset: 15, limit: 5}

    iex> to_next_offset(%Flop{offset: 28, limit: 5}, 22)
    %Flop{offset: 20, limit: 5}

    iex> to_next_offset(%Flop{offset: -5, limit: 20})
    %Flop{offset: 0, limit: 20}

# `to_next_page`
*since 0.15.0* 

```elixir
@spec to_next_page(t(), non_neg_integer() | nil) :: t()
```

Sets the page of a Flop struct to the next page.

If the total number of pages is given as the second argument, the page number
will not be increased if the last page has already been reached. You can get
the total number of pages from the `Flop.Meta` struct.

## Examples

    iex> to_next_page(%Flop{page: 5})
    %Flop{page: 6}

    iex> to_next_page(%Flop{page: 5}, 6)
    %Flop{page: 6}

    iex> to_next_page(%Flop{page: 6}, 6)
    %Flop{page: 6}

    iex> to_next_page(%Flop{page: 7}, 6)
    %Flop{page: 6}

    iex> to_next_page(%Flop{page: -5})
    %Flop{page: 1}

# `to_previous_cursor`
*since 0.15.0* 

```elixir
@spec to_previous_cursor(Flop.Meta.t()) :: t()
```

Takes a `Flop.Meta` struct and returns a `Flop` struct with updated cursor
pagination params for going to the previous page.

## Examples

    iex> to_previous_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_previous_page?: true, start_cursor: "b"
    ...>   }
    ...> )
    %Flop{last: 5, before: "b"}

    iex> to_previous_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{last: 5, before: "b"},
    ...>     has_previous_page?: true, start_cursor: "a"
    ...>   }
    ...> )
    %Flop{last: 5, before: "a"}

If there is no previous page, the `Flop` struct is returned unchanged.

    iex> to_previous_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "b"},
    ...>     has_previous_page?: false, start_cursor: "a"
    ...>   }
    ...> )
    %Flop{first: 5, after: "b"}

It can happen that `has_previous_page?` is `true`, but no start cursor is set,
for example if the user is on the next-to-last page and switches to the next
page right after the only item on the last page is deleted. Since the result
set would be empty and there is no start cursor in this case, this function
sets the new `before` parameter to `nil`. By doing this, the user is sent to
the actual last page without skipping any items.

    iex> to_previous_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{first: 5, after: "a"},
    ...>     has_previous_page?: true, start_cursor: nil
    ...>   }
    ...> )
    %Flop{last: 5, before: nil}

    iex> to_previous_cursor(
    ...>   %Flop.Meta{
    ...>     flop: %Flop{last: 5, before: "b"},
    ...>     has_previous_page?: true, start_cursor: nil
    ...>   }
    ...> )
    %Flop{last: 5, before: nil}

# `to_previous_offset`
*since 0.15.0* 

```elixir
@spec to_previous_offset(t()) :: t()
```

Sets the offset of a Flop struct to the page depending on the limit.

## Examples

    iex> to_previous_offset(%Flop{offset: 20, limit: 10})
    %Flop{offset: 10, limit: 10}

    iex> to_previous_offset(%Flop{offset: 5, limit: 10})
    %Flop{offset: 0, limit: 10}

    iex> to_previous_offset(%Flop{offset: 0, limit: 10})
    %Flop{offset: 0, limit: 10}

    iex> to_previous_offset(%Flop{offset: -2, limit: 10})
    %Flop{offset: 0, limit: 10}

# `to_previous_page`
*since 0.15.0* 

```elixir
@spec to_previous_page(t()) :: t()
```

Sets the page of a Flop struct to the previous page, but not less than 1.

## Examples

    iex> to_previous_page(%Flop{page: 5})
    %Flop{page: 4}

    iex> to_previous_page(%Flop{page: 1})
    %Flop{page: 1}

    iex> to_previous_page(%Flop{page: -2})
    %Flop{page: 1}

# `unnest_filters`
*since 0.20.0* 

```elixir
@spec unnest_filters(t(), [atom()], keyword()) :: map()
```

Takes a Flop, converts it to a map and unnests the filters for the given
fields.

This is the reverse operation of `nest_filters/3`, with the caveat that the
result of `nest_filters/3` needs to be validated to convert it to a `Flop`
struct before it can be passed back to `unnest_filters/3`.

The function returns a map with `nil` values removed.

## Examples

    iex> unnest_filters(
    ...>   %Flop{
    ...>     filters: [%Flop.Filter{field: :name, op: :==, value: "Peter"}],
    ...>     page_size: 10
    ...>   },
    ...>   [:name]
    ...> )
    %{name: "Peter", page_size: 10}

    iex> unnest_filters(
    ...>   %Flop{
    ...>     filters: [%Flop.Filter{field: :name, op: :==, value: nil}],
    ...>     page_size: 10
    ...>   },
    ...>   [:name]
    ...> )
    %{page_size: 10}

To rename fields, you can pass a map, where the keys are the field names in
the unnested map and the values are the field names in the `Flop.Filter`
struct. You can pass the exact same rename map to `nest_filters/3` and
`unnest_filters/3` to revert the nesting/unnesting.

    iex> unnest_filters(
    ...>   %Flop{
    ...>     filters: [%Flop.Filter{field: :name, op: :==, value: "Peter"}],
    ...>     page_size: 10
    ...>   },
    ...>   [:name],
    ...>   rename: %{nombre: :name}
    ...> )
    %{nombre: "Peter", page_size: 10}

# `get_option`
*since 0.11.0* 

```elixir
@spec get_option(atom(), [option()], any()) :: any()
```

Returns the option with the given key.

The look-up order is:

1. the keyword list passed as the second argument
2. the schema module that derives `Flop.Schema`, if the passed list includes
   the `:for` option
3. the backend module with `use Flop`
4. the application environment
5. the default passed as the last argument

# `adapter_option`

```elixir
@type adapter_option() :: {:repo, module()} | {:query_opts, Keyword.t()}
```

Options specific to the Ecto adapter.

- `:repo` - The Ecto Repo module used for database queries.
- `:query_opts` - Options passed to the `Ecto.Repo` query functions. Refer to
  the Ecto documentation for `c:Ecto.Repo.all/2`, `c:Ecto.Repo.aggregate/3`,
  and the ["Shared Options"](https://hexdocs.pm/ecto/Ecto.Repo.html#module-shared-options)
  section of `Ecto.Repo`.

# `default_order`

```elixir
@type default_order() :: %{
  :order_by =&gt; [atom()],
  optional(:order_directions) =&gt; [order_direction()]
}
```

A map with the order fields and the order directions.

Each atom in `:order_by` corresponds to a field by which the data is ordered.
The list of order fields and order directions is zipped when the parameters
are applied. If no order directions are given or the list of order directions
is shorter than the list of order fields, `:asc` is used as a default order
direction.

# `option`

```elixir
@type option() ::
  {:cursor_value_func, (any(), [atom()] -&gt; map())}
  | {:default_limit, pos_integer() | false}
  | {:default_order, default_order()}
  | {:default_pagination_type, pagination_type() | false}
  | {:filtering, boolean()}
  | {:for, module()}
  | {:max_limit, pos_integer() | false}
  | {:count_query, Ecto.Queryable.t()}
  | {:count, integer()}
  | {:ordering, boolean()}
  | {:pagination, boolean()}
  | {:pagination_types, [pagination_type()]}
  | {:replace_invalid_params, boolean()}
  | {:extra_opts, Keyword.t()}
  | {:adapter_opts, adapter_option()}
  | adapter_option()
  | private_option()
```

These options can be passed to most functions or configured via the
application environment.

## Options

### General

- `:for` - The Ecto schema module for validation and query building.
  `Flop.Schema` must be derived for this module.
- `:cursor_value_func` - A function used to extract the cursor value from a
  record. It takes the record and the list of fields used in the `ORDER BY`
  clause as arguments, and returns a map with the order fields as keys and the
  corresponding record values as values. Default is
  `Flop.Cursor.get_cursor_from_node/2`.
- `:replace_invalid_params` - If set to `true`, invalid parameters are
  replaced with default values or removed instead of causing errors. Default
  is `false`.

### Defaults

- `:default_limit` - The default limit for queries. Used when no specific
  limit is set in the parameters or schema.  Set to `false` to not set any
  default limit. Default is `50`.
- `:default_order` - The default ordering for a query when no order is
  specified in the parameters, or if ordering is disabled. Can be set in the
  schema or in the options passed to the query functions.
- `:default_pagination_type` - The default pagination type when it cannot be
  inferred from the parameters.
- `:max_limit` - The maximum limit for queries. Used when no maximum limit is
  set in the parameters or schema. Set to `false` to not set any maximum
  limit. Default is `1000`.

### Modifying counts

- `:count_query` - A separate base query for counting. Can only be passed as
  an option to one of the query functions. See `Flop.validate_and_run/3` and
  `Flop.count/3`.
- `:count` - A precomputed count. Useful when the count is already known.

### Disabling features

- `:filtering` (boolean) - Enables or disables filtering. When set to `false`,
  filter parameters are ignored.
- `:ordering` (boolean) - Enables or disables ordering. When set to `false`,
  order parameters are ignored, but the default order is still applied.
- `:pagination` (boolean) - Enables or disables pagination. When set to
  `false`, pagination parameters are ignored.
- `:pagination_types` - The allowed pagination types. Parameters for
  disallowed pagination types will not be cast. By default, all types are
  allowed. See also `t:Flop.pagination_type/0`.

### Additional options

- `:extra_opts` (keyword list) - Extra options for custom fields.
- `:adapter_opts` - Adapter-specific options. For backward compatibility,
  options for the Ecto adapter can be set directly at the root level.

## Look-up order

Options are looked up in the following order:

1. Function arguments
2. Schema-level options
3. Module-level options in the config (backend) module
4. Global options in the application environment
5. Library defaults

# `order_direction`

```elixir
@type order_direction() ::
  :asc
  | :asc_nulls_first
  | :asc_nulls_last
  | :desc
  | :desc_nulls_first
  | :desc_nulls_last
```

Represents the supported order direction values.

# `pagination_type`

```elixir
@type pagination_type() :: :offset | :page | :first | :last
```

Represents the pagination type.

- `:offset` - offset-based pagination using the `offset` and `limit` parameters
- `:page` - offset-based pagination using the `page` and `page_size` parameters
- `:first` - cursor-based pagination using the `first` and `after` parameters
- `:last` - cursor-based pagination using the `last` and `before` parameters

# `t`

```elixir
@type t() :: %Flop{
  after: String.t() | nil,
  before: String.t() | nil,
  decoded_cursor: map() | 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 with `first` or a
  default limit.
- `before`: Used for cursor-based pagination. Must be used with `last` or a
  default limit.
- `decoded_cursor`: Used internally to hold on to the decoded cursor between
  validation and query execution. Value is discarded when meta is built.
- `limit`, `offset`: Used for offset-based pagination.
- `first`: Used for cursor-based pagination. Can be used alone to begin
  pagination from the start or in conjunction with `after`.
- `last`: Used for cursor-based pagination.
- `page`, `page_size`: Used for offset-based pagination as an alternative to
  `offset` and `limit`.
- `order_by`: List of fields to order by. Fields can be restricted by
  deriving `Flop.Schema` in your Ecto schema.
- `order_directions`: List of order directions applied to the fields defined
  in `order_by`. If empty or the list is shorter than the `order_by` list,
  `:asc` will be used as a default for each missing order direction.
- `filters`: List of filters, see `t:Flop.Filter.t/0`. Each `Filter.t()`
  represents a filter operation on a specific field.

Note: Pagination fields are mutually exclusive.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
