I’m struggling with precedence issues in an Ecto query with AND and OR.
I have a list of keyword lists: filtersArray = [[city: "Stockholm", activity: "Driving"], [city: "London", activity: "Eating"]]
and a specific user_id.
I’m trying to create a query that returns:
WHERE
( (city = "Stockholm" AND activity = "Driving") OR (city = "London" AND activity = "Eating") )
AND
user_id = 1
I’ve gotten to the following:
query = from a in Activity
filtersArray = [[city: "Stockholm", activity: "Driving"], [city: "London", activity: "Eating"]]
query = Enum.reduce(filtersArray, query, fn (filters, query) ->
Ecto.Query.from q in query, or_where: ^filters
end)
query = Ecto.Query.from q in query, where: q.user_id = 1
However, this doesn’t put the parentheses around the OR, leading to the following query:
WHERE
(city = "Stockholm" AND activity = "Driving") OR
(city = "London" AND activity = "Eating") AND
user_id = 1
which given precedence will return all results for (city = “Stockholm” AND activity = “Driving”) ignoring the requirement that user_id = 1
Thanks, unfortunately I have a relative condition as well (activity.done_at >= ^sometime), which I don’t think I can inject into each filters within filtersArray as it has to be an = condition?
I’ve tried to get the dynamic method to work, but I can’t figure out how to do it for my specific use case above.
It looks like this might be what you’re looking for?
d1 = dynamic([a], a.activity == "Eating" and a.city == "London")
d2 = dynamic([a], a.activity == "Driving" and a.city == "Stockholm")
d3 = dynamic([a], (^d1 or ^d2) and a.user_id == 1)
q = from a in Activity, select: a.id, where: ^d3
# SELECT a0."id" FROM "activities" AS a0 WHERE ((((a0."activity" = "Eating") AND (a0."city" = "London")) OR ((a0."activity" = "Driving") AND (a0."city" = "Stockholm"))) AND (a0."user_id" = 1)) []
Thank you so much for all your help! The filtersArray is of an unknown size however, and I can’t see how to do the (^d1 or ^d2 or ...) and a.user_id == 1) with a dynamic amount of ^dn clauses?