I have created a list of queries from a list of parameters that were passed in from a client:
[
#Ecto.Query<from v in Video, where: v.brand == ^"Cocacola">,
#Ecto.Query<from v in Video, where: v.type == ^"can">
]
However, I need to iterate through this list and compose a single query which is the accumulation of all of them.. (the input of the next query is the current query, etc..)
Would someone be able to point me in the right direction about how to do this. It would be greatly appreciated!
I understand I can compose the queries one by one. But I am getting params from the client and have a long list of fields (brands, type, ...etc) and do not want to make a separate query for each one.
Unless you open up the individual query structs and go through their underlying implementation, it is neither possible nor recommended to join queries in Ecto like this. Instead you should try to break them up and make them composable.
Ecto makes it very easy for you to compose queries together:
defmodule VideoQueries do
import Ecto.Query
def with_brand(query, brand) do
where(query, [v], v.brand == ^brand)
end
def with_type(query, type) do
where(query, [v], v.type == ^type)
end
def latest_first(query) do
order_by(query, desc: :inserted_at)
end
end
And you can call them together like this:
Video
|> VideoQueries.with_brand("Cocacola")
|> VideoQueries.with_type("can")
|> VideoQueries.latest_first
Now let's say you get a Map
or Keyword List
of query parameters and you want to apply them, you could still call them together by iterating over the keys/values at runtime. You could build a filter method that does that for you:
# Assuming args are a Keyword List or a Map with Atom keys
def filter(query, args) do
Enum.reduce(args, query, fn {k, v}, query ->
case k do
:brand -> with_brand(query, v)
:type -> with_type(query, v)
_ -> query
end
end)
end
And can dynamically compose queries like this:
user_input = %{brand: "Cocacola", type: "can"}
Video
|> VideoQueries.filter(user_input)
|> Repo.all
Further Readings: