What is the proper way of building a dynamic Ecto query where the fields to be compared are determined dynamically? E.g. suppose we have a table with 4 fields (“a”, “b”, “c”, “d”), and the build_query function receives a candidate record which is a map optionally containing values for each field. If a field is found in the record parameter, I’d like to include it in the “where” expression of the query. However, it’s not clear how to define the field reference in the query dynamically (i.e. “q.^f = ^v”) below:
def build_query(record) do
where =
Enum.filter(["a", "b", "c", "d"], & Map.get(record, &1) != nil)
|> Enum.reduce(false, fn(x, acc) ->
v = Map.get(record, x)
f = String.to_existing_atom(x)
dynamic([q], q.^f == ^v or ^acc)
end)
from q in SomeTable, ^where
end
The closest solution I can think of is to do this:
def build_query(record) do
build = fn
("a", val, acc) -> dynamic([q], q.a == ^val or ^acc)
("b", val, acc) -> dynamic([q], q.b == ^val or ^acc)
("c", val, acc) -> dynamic([q], q.c == ^val or ^acc)
("d", val, acc) -> dynamic([q], q.d == ^val or ^acc)
end
where = Enum.filter(["a", "b", "c", "d"], & Map.get(record, &1) != nil)
|> Enum.reduce(false, fn (x, acc) -> build(x, record[x], acc) end)
from q in SomeTable, ^where
end
but I wonder if there’s a way to get rid of the need to explicitly code all the choices in the lambda above?