README.md

# Ecto.Filters

Provides a consistent way to compose ecto query expressions.

## Installation

```elixir
def deps do
  [
    {:ecto_filters, "~> 0.3.0"}
  ]
end
```

## Usage

```elixir
defmodule Posts do
  use Ecto.Filters
  alias MyProject.{Post, Repo}

  filter(:comment_body, fn query, value ->
    query
    |> join(:left, [p], c in assoc(p, :comments), as: :comments)
    |> where([comments: comments], ilike(comments.body, ^value))
  end)

  @doc """
  Returns the list of posts

  ## Examples

      iex> list_posts(%{"search" => %{"comment_body" => "ecto_filters"}})
      [%Post{}, ...]

      iex> list_posts(%{search: %{comment_body: "ecto_filters"}})
      [%Post{}, ...]

      iex> list_posts(search: [comment_body: "ecto_filters"])
      [%Post{}, ...]

  """
  def list_posts(params) do
    Post
    |> apply_filters(params)
    |> Repo.all()
  end
end
```

When defining a new filter, Ecto.Filters writes a new public function to your context.

So the two examples below are equivalent to each other

```elixir
  filter(:title, &where(&1, title: ^&2))

  def filter_by(query, :title, value) do
    where(query, title: ^value)
  end
```

Knowing that allows the reuse of filters defined by the filter macro in custom functions. For example...

```elixir
  filter(:published, &where(&1, published: ^&2))

  def get_published_post!(id) do
    Post
    |> filter_by(:published, true)
    |> Repo.get!(id)
  end
```

### Options for `apply_filters/2`

- `:key` - The key to use for passing filters. The default is `filters`.
- `:ignore_bad_filters` - Whether or not an exception should be raised when using a
  filter that has not been defined.

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/ecto_filters](https://hexdocs.pm/ecto_filters).