问题
The Ecto documentation shows how to do interpolation values. But I need dynamic fields in my queries. I have dozens of fields and write queries for each of them does not seem cohesive.
defmodule Hedone.SearchController do
use Hedone.Web, :controller
alias Hedone.User
def idade(conn, %{"idade+" => maior, "idade-" => menor, "campo" => campo}) do
IO.inspect campo
query = from u in User, where: u.campo > ^maior and u.campo < ^menor, select: u.name
pesquisa = Repo.all query
IO.inspect pesquisa
text conn, "Works"
end
end
This controller generates the following error:
** (Ecto.QueryError) web/controllers/search.ex:8: field `Hedone.User.campo` in `where` does not exist in the schema in query:
from u in Hedone.User,
where: u.campo > ^18 and u.campo < ^40,
select: u.name
Automatically translated.
回答1:
I'm assuming campo
contains a string with the name of the field you want to use, e.g. "age"
. You can use field in the query for this:
def idade(conn, %{"idade+" => maior, "idade-" => menor, "campo" => campo}) do
campo = String.to_existing_atom(campo)
query = from u in User, where: field(u, ^campo) > ^maior and field(u, ^campo) < ^menor, select: u.name
pesquisa = Repo.all query
IO.inspect pesquisa
text conn, "Works"
end
field
expects the field to be an atom, so I've used String.to_existing_atom
to safely convert a string to atom. Since you already must have defined the field in the schema of the model, String.to_existing_atom
will not fail for any valid field name.
回答2:
I am posting this as an example of another approach to the problem. One might use macros to handle this transformation since macros are being resolved in compile time:
defmacro idade(conn, formula, campo: binding) do
q = formula |> String.replace "campo", binding
quote do
from u in User, where: unquote(q), select: u.name
|> Repo.all
text conn, "Works"
end
end
and call it like:
idade(nil, "u.campo > ^maior and u.campo < ^menor", campo: "age")
来源:https://stackoverflow.com/questions/39687581/how-do-interpolation-fields-in-ecto-queries