Default filter implementations for Patterns.Queryable modules.
apply_filter/2 turns common filter tuples into Ecto query expressions so a
Patterns.Queryable module can focus on custom filters and delegate the rest.
Query Modifiers
Query modifiers affect only the top-level query. They are ignored inside association filters.
Supported modifiers are:
{:distinct, value}{:limit, value}{:offset, value}{:preload, value}{:select, value}
Modifier values are forwarded to Ecto's query DSL, so Ecto syntax like
{:preload, :comments}, {:preload, [comments: :post]}, and
{:distinct, true} is supported. Atom selects are treated as fields on the
current binding, so {:select, :id} selects the current binding's id field.
Non-atom selects are passed through to Ecto as values. Distinct values are
forwarded to Ecto; use {:distinct, true} as the portable parent-deduplication
form for association filters.
Modifier behavior is delegated to Ecto and the configured adapter. SQL shape
and support can differ between adapters such as ecto_sqlite3 and PostgreSQL,
especially for non-boolean distinct values and complex preloads.
Field Comparators
Field comparators apply predicates to the current binding.
Supported comparators are:
{field, value}and{field, {:eq, value}}{field, nil}and{field, {:eq, nil}}{field, [value]}and{field, {:in, [value]}}{field, %Regex{}}and{field, {:like, %Regex{}}}{field, {:not, value}}{field, {:not, nil}}{field, {:not, [value]}}and{field, {:not_in, [value]}}{field, {:not, %Regex{}}}and{field, {:not_like, %Regex{}}}{field, {:gt, value}}{field, {:gte, value}}{field, {:lt, value}}{field, {:lte, value}}{field, {:like, pattern}}{field, {:not_like, pattern}}
Unsupported comparator tuples raise ArgumentError instead of silently
becoming equality comparisons against the tuple value.
Regex Values
Regex shorthand is LIKE shorthand
Regex values are shorthand for SQL LIKE patterns, not full database regex
predicates. They are converted by translating .* to % and . to _.
Unanchored regexes are padded with %; ^ and $ suppress leading and
trailing padding respectively.
The i regex option lower-cases the field and generated LIKE pattern.
Other regex options raise ArgumentError.
Matching semantics are still delegated to the database adapter and collation.
For example, plain SQL LIKE case-sensitivity can differ between SQLite and
PostgreSQL.
Literal % and _ are rejected in regex shorthand because they are SQL LIKE
wildcards. Escaped regex syntax is not supported.
Regex syntax that cannot be represented by this simple conversion raises
ArgumentError.
Association Comparators
Association comparators use {field, filters} where filters is a map or
keyword-shaped list and field is an Ecto association.
Empty filters like {:comments, []} and {:comments, %{}} still join the
association. When Patterns.Queryable adds a new inner join, they match
parents with at least one associated row. When an existing named binding is
reused, that binding's join semantics are preserved.
Treating map and keyword values as association filters is an implementation
detail. Future versions may support map equality for field types that can
encode and compare maps directly, such as PostgreSQL jsonb fields.
Association filters require a schema-backed query source because they inspect association metadata from the current scoped binding. Raw string sources and subquery sources cannot use association filters.
Join generation and association metadata are delegated to Ecto. Adapter SQL
output may differ, but the filter semantics are based on Ecto's assoc/2 join
behavior.
Nested association filters are resolved from the current scoped binding, so
{:comments, [post: [title: "Hello"]]} filters :post as an association of
the joined :comments binding.
Association filters use Ecto's assoc/2 join syntax and keep normal join
semantics. For has_many and many_to_many associations, one parent row is
returned for each matching associated row. Pass {:distinct, true} when parent
row uniqueness matters.
Named binding reuse
If the query already has a named binding for the association, that binding is
reused instead of adding another join. Reusing an existing binding preserves
that binding's join semantics, including left-join behavior. Named bindings are
query-global, so nested association filters can reuse an existing binding with
the same association name. Implement custom query/2 clauses when a nested
association path needs stricter binding control.
When the associated schema exports query/2, nested filters are delegated to
that function against the joined association binding. Otherwise, each nested
filter is applied directly to the joined association binding.
Binding resolution uses Patterns.Queryable.DSL.binding_schema/1, so nested
association filters are resolved from the current scoped binding rather than
always from the root query source.
Summary
Functions
Applies a single query filter.
Functions
@spec apply_filter( Ecto.Queryable.t(), {field :: atom(), value :: term()} ) :: Ecto.Queryable.t()
Applies a single query filter.
See the module documentation for supported filter shapes.