EctoFilter v0.2.0 EctoFilter behaviour 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

Link to this type

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

Link to this section Functions

Link to this function

filter(query, conditions) View Source
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
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
apply(
  query :: Ecto.Query.t(),
  condition :: condition(),
  type :: field_type(),
  context :: Ecto.Queriable.t()
) :: Ecto.Query.t()