You're seeing just the function apply, go back to Filtery module for more information.
Link to this function

apply(query, filters, opts \\ [])

View Source


apply(Ecto.Queriable.t(), map() | keyword(), keyword()) ::

Filtery help you to build the query using a syntax which is similar to Mongo like this:

filter = %{
  status: "active",
  email: {:not, nil},
  role: ["admin", "moderator"]
Filtery.apply(User, filter)

The result is a query like this:

from(u in User, where: u.status == "active" and not is_nil( and u.role in ["admin", "moderator"])


  • You can use <field>: <value> expressions to specify the equality condition and query operator expressions.
  <field1>: <value1>,
  <field2>: { <operator>, <value> },
  • All fields which field name starts with _ is ignored.

Notes: all operator belows are reserved keywords and cannot be used as field name

Supported operator

:eqMatches values that are equal to a specified value.
:gtMatches values that are greater than a specified value.
:gteMatches values that are greater than or equal to a specified value.
:inMatches any of the values specified in an array.
:ltMatches values that are less than a specified value.
:lteMatches values that are less than or equal to a specified value.
:neMatches all values that are not equal to a specified value.
:ninMatches none of the values specified in an array.

Logical operator

:andJoins query clauses with a logical AND returns all documents that match the conditions of both clauses.
:notInverts the effect of a query expression and returns documents that do not match the query expression.
:orJoins query clauses with a logical OR returns all documents that match the conditions of either clause.

AND operator

By default, if a map or keyword list is given, Filtery will join all field condition of that map using AND

Filtery.apply(User, %{status: "active", age: {:gt, 20}})

# same with
Filtery.apply(User, %{and:
                      %{status: "active", age: {:gt, 20}}

# same with
Filtery.apply(User, %{and:
                      [status: "active", age: {:gt, 20]}

# same with
from(u in User, where: u.status == "active" and u.age > 20)

OR operator

The :or operator performs a logical OR operation on an array of two or more <expressions>

Filtery.apply(Product, %{or: %{
                           price: {:gt, 20},
                           category: "sport"

NOT operator

Performs a logical NOT operation on the specified <operator-expression>

Syntax: %{ field: %{ not: <operator-expression> } }

Filtery.apply(Product, %{or: %{
                           price: {:gt, 20},
                           category: {:not: "sport"}

Filtery.apply(Product, %{or: %{
                           price: {:not, {:gt, 20}},
                           category: "sport"

3. Extra operator

Filtery provides some more useful operators to work with text and range.

:betweenMatches values > lower bound and < upper bound
:ibetweenMatches values >= lower bound and <=upper bound
like, containsMatch values which contains specific value
ilike, icontainsCase insensitive version of like
hasFor array type column, Matches array which has specific value


  • between | ibetween

    Syntax: field: {:between, [lower_value, upper_value]}

Check NULL and skip nil filter

By default is a value in the filter is nil, Filtery applies is_nil to check NULL value. You can tell Filtery to ignore all nil field by passing skip_nil: true to the options

Filtery.apply(query, filter, skip_nil: true)

In that case, if you want to check field which is NULL or NOT NULL you use :is_nil instead of nil when passing value to the filter:

Filter.apply(query, %{email: :is_nil}, skip_nil: true)

Define your operators

You can extend Filtery and define your own operator. For example, here I define a new operatory equal

defmodule MyFiltery do
  use Filtery.Base

  def filter(column, {:equal, value}) do
    dynamic([q], field(q, ^column) == ^value)

To support a filter, you must follow this spec

@spec filter(column::atom(), {operator::atom(), value::any()}) :: Ecto.Query.dynamic()

Within the body of filter/2 function using dynamic to compose your condtion and return a dynamic

V. Joining tables

Filtery defines a special operator ref to join table

Syntax: <field>: {:ref, <qualifier>, <filter on joined table>}

If qualifier is skipped, then :inner join is used by default.

query = Filtery.apply(Post, %{comments: {:ref, %{
                                    approved: true,
                                    content: {:like, "filtery"}

And then you can use Name binding to do further query

query = where(query, [comments: c], c.published_at > ^xday_ago)


By default Filtery join using :inner qualifier. You can use one of `:inner, :left, :right, :cross, :full, :inner_lateral or :left_lateral qualifier as defined by Ecto. ### You can filter with nested ref elixir Filtery.apply(Post, %{comments: {:ref, %{ approved: true, user: {:ref, %{ name: {:like, "Tom"} }} }}}) ### Important Notes on ref operator - Field name must be the association name in your schema because Filtery use assoc to build join query. In the above example, Post schema must define association has_many: :comments, Comment - Not allow 2 ref with same name because the name is used as alias :as in join query, so it can only use one.