I have a list of fields, and I want to take that list and dynamically create an Ecto query from it.
I think I can use Enum.reduce
, but I'm not sure how to get the pieces to work together.
Each field in the list would use an ilike
and be cast to a text, something like:
def lookup(search_term) do
fields = [:id, :title, :body]
query =
from p in Post,
where: build_query(fields, search_term)
select: p
Repo.all(query)
end
def build_query(fields, search_term) do
Enum.reduce(fields, fn(field) ->
fragment("CAST(? AS text) ILIKE ?", field(t, ^field), ^search_term))
end)
end
ecto is built with macros and from/2
does actually expand its whole arguments.
One might define their own macros as described here but in your particular case, it’s enough to compose a query.
To compose a query, one reduces a query object on additional ‘actions’.
The code below should work
@fields ~w|title body|a
def lookup(search_term) do
query = from p in Post # prepare the accumulator
# and then reduce the query to compose
query = build_query(query, @fields, search_term)
# add additional tuning
query = from p in query, select: p.id
Repo.all(query)
end
def build_query(query, fields, search_term) do
Enum.reduce(fields, query, fn field, query ->
from q in query,
where: fragment("CAST(? AS text) ILIKE ?", field(q, ^field), ^search_term)
end)
end