# Flop adapter

`Refine.Flop` is an adapter for using [Flop](https://hex.pm/packages/flop) with Refine.

The adapter translates Flop's filter and pagination parameters into Refine search options.
Facets remain part of Refine's own options, while Flop handles field filters and offset pagination.

## Installation

Add Flop to your deps (check for the most recent version):

    {:flop, "~> 0.26"}

## Usage

If you are starting with Flop, follow the setup instructions to add a Flop schema to your Ecto schema.

For an application that already uses Flop, no changes are needed.

Searches are called with `Refine.Flop.search/3` - this is basically the `Refine.search/2` function with Flop parameters added.
The return value is equal to `Refine.search`, with the addition of key `flop_meta` with a `Flop.Meta` struct.

### Filtering

```elixir
flop_params = %{filters: [%{field: :title, op: :ilike, value: "ghost"}]}
Refine.Flop.search(config, flop_params, repo: Repo, for: Article)
```

In this example:

- `for` is a Flop option that builds a query for that schema.
- `repo` can be omitted when the repo is configured globally.

Flop parameters can also be passed as a string-keyed map, for example from URL parameters:

```elixir
flop_params = %{"filters" => [%{"field" => "title", "op" => :ilike, "value" =>  "ghost"}]}
Refine.Flop.search(config, flop_params, repo: Repo, for: Article)
```

### Filtering with a base query

A base query can be used together with Flop filters.

```elixir
query = from a in Article, where: a.draft == false
flop_params = %{filters: [%{field: :title, op: :ilike, value: "ghost"}]}
Refine.Flop.search(config, flop_params, query: query, repo: Repo)
```

In this example:

- `where` clauses from both sources will be combined.
- `for` can be omitted when using `query`.

### Filtering with facets

Pass facet options in the same way as with `Refine.search`.

```elixir
flop_params = %{filters: [%{field: :title, op: :ilike, value: "ghost"}]}
facets = %{"tag" => ["stories"]}

Refine.Flop.search(config, flop_params,
  facets: facets,
  repo: Repo,
  for: Article
)
```

### Sorting

Flop's `order_by` and `order_directions` are applied to the results, including Flop's null-ordering directions (for example `:asc_nulls_last`).
Sortable fields must be columns of the source table or fields included in the base query's `select`.


```elixir
flop_params = %{
  order_by: [:draft, :title],
  order_directions: [:desc, :asc]
}

Refine.Flop.search(config, flop_params,
  repo: Repo,
  for: Article
)
```

To override Flop's ordering, pass Refine's own `order_by` option - it replaces Flop's ordering entirely. See [`order_by`](faceted_search.md#order_by) for its format.

If `order_by` is both passed in Flop parameters and in Refine search options, the latter will override Flop's parameters.

### Generating Refine search options from Flop

Instead of passing Flop parameters directly to `Refine.Flop.search/3`, use `Refine.Flop.to_search_options/2` to inspect or modify the generated Refine search options before passing them to `Refine.search/2`.

This does not return the `Flop.Meta` struct.

```elixir
search_options = Refine.Flop.to_search_options(flop_params, repo: Repo, for: Article)
# Optionally modify search options
Refine.search(config, search_options)
```

## Notes

- Offset-based pagination is supported, in both of Flop's forms: `page/page_size` and `limit/offset`. Both are translated to Refine's `limit` and `offset`.
- Cursor-based pagination is not supported.
- Flop filters on joined fields require the joins to be present in the base query, the same as any Refine base query.
- If used, Flop option `for` must correspond to Refine's configured source table.
