EctoSearcher is an attempt to bring dynamicly built queries (hello Ransack) to the world of Ecto.
Add ecto_searcher
to your mix.ex deps:
def deps do
[
{:ecto_searcher, "~> 0.2.1"}
]
end
This package is build for sql queries and is tested with PostgreSQL. Every other usage may not work.
Obviously, EctoSearcher works on top of ecto schemas and ecto repos. It consumes ecto query and adds conditions built from input.
Searching with EctoSearcher.Searcher.search/5
and sorting EctoSearcher.Sorter.sort/5
could be used separately or together.
To search use EctoSearcher.Searcher.search/4
or EctoSearcher.Searcher.search/5
:
Basic usage:
defmodule TotallyNotAPhoenixController do
def not_some_controller_method() do
base_query = Ecto.Query.from(MyMegaModel)
search = %{"name_eq" => "Donald Trump", "description_cont" => "My president"}
query = EctoSearcher.Searcher.search(base_query, MyMegaModel, search)
MySuperApp.Repo.all(query)
end
end
To sort use EctoSearcher.Sorter.sort/3
or EctoSearcher.Sorter.sort/5
:
defmodule TotallyNotAPhoenixController do
def not_some_controller_method() do
base_query = Ecto.Query.from(MyMegaModel)
sort = %{"field" => "name", "order" => "desc"}
query = EctoSearcher.Sorter.sort(base_query, MyMegaModel, sort)
MySuperApp.Repo.all(query)
end
end
In case you need to implement custom field queries or custom matchers you can implement custom Mapping (using EctoSearcher.Mapping
behaviour):
defmodule MySuperApp.CustomMapping do
use EctoSearcher.Mapping
def matchers do
custom_matchers = %{
"not_eq" => fn field, value -> Query.dynamic([q], ^field != ^value) end
}
## No magic, just plain data manipulation
Map.merge(
custom_matchers,
EctoSearcher.Mapping.Default.matchers()
)
end
def fields do
%{
datetime_field_as_date: %{
query: Query.dynamic([q], fragment("?::date", q.custom_field)),
type: :date
}
}
end
end
And use it in EctoSearcher.Searcher.search/5
or EctoSearcher.Sorter.sort/5
:
defmodule TotallyNotAPhoenixContext do
import Ecto.Query
require Ecto.Query
def not_some_context_method() do
search = %{
"name_eq" => "Donald Trump",
"datetime_as_date_gteq" => "2016-11-08", "datetime_as_date_lteq" => "2018-08-28",
"description_not_eq" => "Not my president"
}
sort = %{
"field" => "datetime_as_date_gteq",
"order" => "desc"
}
base_query = from(q in MyMegaModel, where: [q.id < 1984])
query =
base_query
|> EctoSearcher.Searcher.search(MyMegaModel, search, MySuperApp.CustomMapping)
|> EctoSearcher.Searcher.search(base_query, MyMegaModel, sort, MySuperApp.CustomMapping)
|> MySuperApp.Repo.all()
end
end
EctoSearcher.Searcher.search/5
and EctoSearcher.Sorter.sort/5
looks up fields in mapping first, then looks up fields in schema.