BoxTurtle.Schema.Filters (BoxTurtle v0.1.0)

Copy Markdown View Source

Default filtering for BoxTurtle.Schema.Query, and a base for schema-specific filter modules.

Each filter is a {column, value} pair, combined with AND:

  • {:status, "active"} - status = 'active'
  • {:status, ["active", "pending"]} - status IN (...)
  • {:deleted_at, nil} - deleted_at IS NULL

String column names are accepted, but only if the atom already exists (String.to_existing_atom/1), so filters built from request params can't create atoms.

Custom filters

Use this module and add filter_with/2 clauses for named filters that aren't plain column comparisons. Each clause matches the filter at the head of the list and must pass the rest back to filter_with/2. Filters your clauses don't match fall through to the default behaviour, one filter at a time.

For a MyApp.Blog.Post schema with likes and published_at fields:

defmodule MyApp.Blog.Post.Filters do
  use BoxTurtle.Schema.Filters
  import Ecto.Query

  def filter_with(query, [{:published_after, %DateTime{} = date} | rest]) do
    query
    |> where([p], p.published_at > ^date)
    |> filter_with(rest)
  end

  def filter_with(query, [{:min_likes, likes} | rest]) do
    query
    |> where([p], p.likes >= ^likes)
    |> filter_with(rest)
  end
end

defmodule MyApp.Blog.Post.Query do
  use BoxTurtle.Schema.Query, repo: MyApp.Repo, filter_module: MyApp.Blog.Post.Filters
end

# Custom and plain column filters mix freely, in any order.
last_week = DateTime.add(DateTime.utc_now(), -7, :day)
MyApp.Blog.Post.Query.list!(filters: [published_after: last_week, status: "published", min_likes: 10])

Custom clauses see keys exactly as given, so match atoms and convert request params yourself. For more patterns (joins, subqueries, and closed allow-list modules for request params), see the reference modules in the repository's test/support/examples/schema.ex.

Summary

Functions

Applies a single {column, value} filter (see the module documentation) to the query.

Functions

apply_filter(query, arg)

@spec apply_filter(Ecto.Queryable.t(), {atom() | String.t(), term()}) ::
  Ecto.Queryable.t()

Applies a single {column, value} filter (see the module documentation) to the query.