Sifter (Sifter v0.2.0)
View SourceSifter is a query filtering library for Elixir that converts search syntax from frontend applications into Ecto queries with full-text search support.
Overview
Sifter enables frontend JavaScript clients to build flexible queries that can be sent as simple strings to your backend API. It automatically handles field validation, type casting, association joins, and PostgreSQL full-text search while providing security through field allow-lists.
Basic Usage
# Simple field filtering
{query, meta} = Sifter.filter!(Post, "status:published priority>3",
schema: Post,
allowed_fields: ["status", "priority"]
)
# With full-text search
{query, meta} = Sifter.filter!(Post, "machine learning status:published",
schema: Post,
allowed_fields: ["status"],
search_fields: ["title", "content"], # Full-text search fields
search_strategy: {:tsquery, "english"} # Full-text search strategy
)
posts = Repo.all(query)Query Syntax
Sifter supports a rich query syntax:
- Field predicates:
status:published,priority>5,createdAt<='2024-01-01' - Boolean logic:
status:draft OR status:review,published AND priority>3 - Lists:
status IN (draft, published),tag NOT IN (spam, test),labels ALL (urgent, backend) - Wildcards:
title:data*,email:*@example.com - Full-text search: Any unqualified terms search configured text fields
Configuration
The main configuration options:
:schema- The Ecto schema module (required if not inferrable from query):allowed_fields- Field allow-list with optional aliases:search_fields- Fields to full-text search for unqualified terms:search_strategy- How to perform full-text search (:ilike,{:tsquery, "config"}, etc.):unknown_field- How to handle unknown fields (:ignore,:warn,:error)
Result Metadata
Every query returns metadata about the filtering operation:
meta = %{
uses_full_text?: true, # Whether full-text search was used
added_select_fields: [:search_rank], # Fields added to SELECT
recommended_order: [search_rank: :desc], # Suggested ordering
warnings: [] # Any warnings generated
}
Summary
Types
Functions
@spec filter(Ecto.Queryable.t(), String.t(), opts()) :: {:ok, Ecto.Query.t(), meta()} | {:error, Sifter.Error.t()}
@spec filter!(Ecto.Queryable.t(), String.t(), opts()) :: {Ecto.Query.t(), meta()}
@spec to_sql(Ecto.Queryable.t(), String.t(), module(), opts()) :: {:ok, String.t(), [term()], meta()} | {:error, Sifter.Error.t()}