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"}
]
endThis 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
endTo 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
endIn 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
endAnd 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
endEctoSearcher.Searcher.search/5 and EctoSearcher.Sorter.sort/5 looks up fields in mapping first, then looks up fields in schema.