ecto_searcher v0.2.0 EctoSearcher View Source
EctoSearcher is an attempt to bring dynamicly built queries (hello Ransack) to the world of Ecto.
Installation
Add ecto_searcher
from github to your mix.ex deps:
def deps do
[
{:ecto_searcher, "~> 0.2.0"}
]
end
Notice
This package is build for sql queries and is tested with PostgreSQL. Every other usage may not work.
Usage (short)
EctoSearcher.Searcher
— use this to searchEctoSearcher.Sorter
— use this to sortEctoSearcher.Mapping
— use this to implement custom searchesEctoSearcher.Mapping.Default
— use this if you don't want to implement custom mapping
Usage
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.Searcher.sort/5
could be used separately or together.
Searching
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
Sorting
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
Custom fields and matchers
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" => "2019-09-11",
"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.