In our application, the Recipe
model has many ingredients (many-to-many relationship implemented using :through
). There is a query to return all the recipes where at least one ingredient from the list is contained (using ILIKE
or SIMILAR TO
clause). I would like to pose two questions:
- What is the cleanest way to write the query which will return this in Rails 6 with ActiveRecord. Here is what we ended up with
ingredients_clause = '%(' + params[:ingredients].map { |i| i.downcase }.join("|") + ')%'
recipes = recipes.where("LOWER(ingredients.name) SIMILAR TO ?", ingredients_clause)
Note that recipes
is already created before this point.
However, this is a bit dirty solution. I also tried to use ILIKE = any(array['ing1', 'ing2',..]) with the following:
ingredients_clause = params[:ingredients].map { |i| "'%#{i}%'" }.join(", ")
recipes = recipes.where("ingredients.name ILIKE ANY(ARRAY[?])", ingredients_clause)
This won't work since ?
automatically adds single quotes so it would be
ILIKE ANY (ARRAY[''ing1', 'ing2', 'ing3''])
which is of course wrong.
Here, ?
is used to sanitise parameters for SQL query, so avoid possible SQL injection attacks. That is why I don't want to write a plain query formed from params.
Is there any better way to do this?
- What is the best approach to order results by the number of ingredients that are matched? For example, if I search for all recipes that contains ingredients
ing1
anding2
it should return those which contains both before those which contains only one ingredient.
Thanks in advance