EctoFilter behaviour (EctoFilter v0.3.1) View Source

Aims in building database queries using data as filtering conditions.

Filtering conditions

Filtering conditions are represented as tuples consisting of field name, filtering rule and value to filter by. For example, {:first_name, :equal, "Bob"} will select all records with the "Bob" value of the :first_name field. You can use multiple conditions, in that case they will be combined in query.

Also, it is possible to build queries based on field values of associated models. This can be achieved by condition nesting: {:organization, nil, [{:name, :like, "acme"}]} will select only records with the :name field of their associated as the :organization entity matching with the "acme" value.

Operators

Every filtering condition is handled by the corresponding operator — a function which takes the Ecto query and the filtering condition and returns the query with the filtering condition applied.

Usually operators are defined as apply/4 callback implementations in the filter module.

EctoFilter includes default operators for field value comparison, array inclusion checking, matching values with pattern and filtering by values of the associated entities' fields.

Also, operators for dealing with PostgreSQL JSON/JSONB fields are available.

Extending

It is possible to implement custom filtering logic by defining custom filter modules. See examples below for more information.

Examples:

Basic filtering

iex> Repo.insert!(%User{first_name: "Bob"})
iex> Repo.insert!(%User{first_name: "Alice"})
iex> result =
...>   User
...>   |> EctoFilter.filter([])
...>   |> Repo.all()
iex> length(result)
2

iex> Repo.insert!(%User{email: "bob@example.com"})
iex> Repo.insert!(%User{email: "alice@example.com"})
iex> result =
...>   User
...>   |> EctoFilter.filter([{:email, :equal, "alice@example.com"}])
...>   |> Repo.all()
iex> length(result)
1
iex> hd(result).email
"alice@example.com"

Building custom filter

iex> defmodule CustomFilter do
...>   use EctoFilter
...>
...>   def apply(query, {:name, :full_text_search, value}, _, User) do
...>     where(
...>       query,
...>       [..., u],
...>       fragment("to_tsvector(concat_ws(' ', ?, ?)) @@ plainto_tsquery(?)", u.first_name, u.last_name, ^value)
...>     )
...>   end
...>
...>   def apply(query, condition, type, context), do: super(query, condition, type, context)
...> end
iex> Repo.insert!(%User{first_name: "Bob", last_name: "Doe"})
iex> Repo.insert!(%User{first_name: "Alice", last_name: "Roe"})
iex> result =
...>   User
...>   |> CustomFilter.filter([{:name, :full_text_search, "alice roe"}])
...>   |> Repo.all()
iex> length(result)
1
iex> hd(result).first_name
"Alice"

Link to this section Summary

Functions

Adds filtering conditions to the query.

Link to this section Types

Specs

condition() :: {field :: atom(), rule :: atom(), value :: any()}

Specs

field_type() :: Ecto.Type.t() | Ecto.Embedded.t() | Ecto.Association.t()

Link to this section Functions

Link to this function

filter(query, conditions)

View Source

Specs

filter(query :: Ecto.Query.t(), conditions :: [condition()]) :: Ecto.Query.t()

Adds filtering conditions to the query.

Link to this function

introspect(queryable, field)

View Source

Specs

introspect(queryable :: Ecto.Queryable.t(), field :: atom()) :: field_type()

Link to this section Callbacks

Link to this callback

apply(query, condition, type, context)

View Source

Specs

apply(
  query :: Ecto.Query.t(),
  condition :: condition(),
  type :: field_type(),
  context :: Ecto.Queriable.t()
) :: Ecto.Query.t()