View Source EctoModel.Queryable behaviour (EctoModel v0.0.1)
A behaviour for defining a query/2
callback that can be used as an easy-to-use and fluent DSL for building
Ecto queries in a consistent manner across different schemas.
Usage
Define a schema module and use the
EctoModel.Queryable
behaviour. By default, this is all you have to do unless you wish to customize the behaviour further.defmodule MyApp.User do use Ecto.Schema use EctoModel.Queryable schema "users" do field(:name, :string) field(:email, :string) field(:inserted_at, :utc_datetime) field(:updated_at, :utc_datetime) end end (iex)> MyApp.Repo.exists?(MyApp.User.query(name: "John", email: nil)) true (iex)> MyApp.Repo.exists?(MyApp.User.query(email: nil, inserted_at: {:>=, ~U[2021-01-01 00:00:00Z]})) false
Implement the
base_query/0
optional callback if you want all queries, by default, to inherit a standard set of filters. This is useful for implementing soft deletes, for example.def base_query, do: from(x in __MODULE__, where: is_nil(x.deleted_at))
Implement the
query/2
callback to apply filters to the query. This is where you can extend the default supported filters or add custom filters that are specific to your schema.When you use the
EctoModel.Queryable
behaviour, you get a default implementation of thequery/2
callback that looks like the following:def query(base_query \ base_query(), filters) do Enum.reduce(filters, base_query, &apply_filter(&2, &1)) end
If you want to add new logic while still inheriting the default behaviour, you can do so by ensuring a clause exists within your
Enum.reduce/3
implementation that matches against any pattern and delegates to the default behaviour provided byEctoModel.Queryable.apply_filter/2
.
Supported Filters
By default, all non-embedded fields should be supported by the default apply_filter/2
implementation for the following
operators:
- Equality (
==
) viafield: value
- Inclusion (
in
) viafield: [value1, value2]
- Exclusion (
not in
) viafield: {:not, [value1, value2]}
- Greater than (
>
) viafield: {:gt, value} or field: {:>, value}
- Greater than or equal to (
>=
) viafield: {:gte, value} or field: {:>=, value}
- Less than (
<
) viafield: {:lt, value} or field: {:<, value}
- Less than or equal to (
<=
) viafield: {:lte, value} or field: {:<=, value}
- Is null (
nil
) viafield: nil
- Is not null (
not nil
) viafield: {:not, nil}
- Like (
like %value&
) viafield: ~r/value/
- Case insensitive Like (
ilike %value&
) viafield: ~r/value/i
Additionally, while not filters in the traditional sense, the following options are also supported:
- Preloading (
preload
) viapreload: :association or preload: [:association1, :association2]
- Limiting (
limit
) vialimit: 10
- Offsetting (
offset
) viaoffset: 10
- Ordering (
order_by
) viaorder_by: :field or order_by: {:desc, :field} or order_by: [:field1, :field2]
Summary
Functions
List of default filters that can be used in a schema's query/2
callback
Returns true a given module implements the Queryable
behaviour
Callbacks
@callback base_query() :: Ecto.Queryable.t()
@callback query(Ecto.Queryable.t(), Keyword.t()) :: Ecto.Queryable.t()
Functions
@spec apply_filter( Ecto.Queryable.t(), {field :: atom(), value :: term()} ) :: Ecto.Queryable.t()
List of default filters that can be used in a schema's query/2
callback
Returns true a given module implements the Queryable
behaviour