Чтобы развернуть комментарий @ MrYoshiji Здесь мы можем сделать это более "безобидным" следующим образом:
ingredients_table = Ingredient.arel_table
ingredient_list1 = [5,6]
ingredient_list2 = [10,11]
condition = Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient_list1)]
).and(
Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient_list2)]
)
)
recipes = Recipe.joins(groups: {
steps: {
steps_ingredients_memberships: :ingredients
}
})
.group('recipes.id')
.having(condition)
Это приведет к SQL, похожему на связанныйответ: (Предоставлено @ ThorstenKettner ), например,
SELECT recipes.*
FROM recipes
JOIN groups ON groups.recipe_id = recipes.id
JOIN steps ON steps.group_id = groups.id
JOIN steps_ingredients_memberships ON steps_ingredients_memberships.step_id = steps.id
JOIN ingredients ON ingredients.id = steps_ingredients_memberships.ingredient_id
GROUP BY
recipes.id
HAVING
BOOL_OR(ingredients.id IN (5, 6)) AND
BOOL_OR(ingredients.id IN (10, 11))
Однако он позволяет гибко изменять ingredient_list1
и ingredient_list2
гораздо более эффективным образом, не заботясь о побеге илайк.
Кстати, чтобы ответить на ваш комментарий:
"Я поставил тег ruby-on-rails, потому что если есть решение в ruby-on-rails, то лучше, но яне думайте так ... "
arel
может собрать любой запрос, который вы можете себе представить (он также может собрать неверный / фрагментированный SQL).Если он действителен, SQL ActiveRecord
может запустить его, поэтому, если вы используете rails и у вас есть вопросы, обязательно включите тег ruby-on-rails , как вы это сделали.
Обновление (на основе вашего опубликованного решения), но с использованием arel
вместо конкатенации строк
scope :ingredients_filtering, lambda { |ingredients|
return unless ingredients
ingredients_table = Ingredient.arel_table
conditions = ingredients.map do |ingredient|
Arel::Nodes::NamedFunction.new(
'BOOL_OR',[ingredients_table[:id].in(ingredient.related_ids)]
)
end.reduce(&:and)
group(:id).having(conditions)
}