How do you guys do implement search?
For searching, sorting, and filtering, I use Inquisitor with plain old Ecto queries. Inquisitor is a composable query builder. This approach gets me very far.
First, install Inquisitor and define a search module in your context:
defmodule Obramax.Estimates.CartFilters do
use Inquisitor
import Ecto.Query, warn: false
def build_query(query, "term", value, _conn) when is_present(value) do
value = String.trim(value)
search_query = "%" <> value <> "%"
from cart in query,
where: fragment("address ->> 'recipient_cpf' LIKE ?", ^search_query),
or_where: fragment("address ->> 'recipient_cnpj' LIKE ?", ^search_query),
or_where: like(cart.reference_number, ^search_query)
end
end
Pass the params map to the search module from your controller. If any of the functions you define in the search module match the params pattern, the function will be executed and the query will be composed. Your index action might look something like this:
def index(conn, params) do
page =
Cart
|> CartFilters.build_query(conn, params)
|> Estimates.list_carts(params)
render(conn, "index.html", page: page)
end
Finally, forward the query that comes back from the search module to the context. The context function might look like this:
def list_carts(query \\ Cart, params \\ %{}) do
from(c in query,
preload: [:owner, [line_items: :product]])
|> Repo.paginate(params)
end
For pagination, I use Scrivener. The pagination is carried out in the context module.