Где ALL IN, не любые, а ровно все элементы совпадают

class Recipe < ActiveRecord::Base
  has_many :ingredients
end

class Recipe::Ingredient < ActiveRecord::Base
  belongs_to :product
  # also has `require_in_filter` boolean attribute
end

Я хочу получить все рецепты по идентификаторам продуктов, где все эти продукты находятся в ингредиентах с require_in_filter = true. Например, в рецепте есть 2 ингредиента: оба с require_in_filter = true и они ссылаются на продукты с идентификаторами 2 и 3.

  • Запрос рецептов с продуктами (2,3) ДОЛЖЕН вернуть этот рецепт.
  • Запрос рецептов с продуктами (2,3,17) НЕ ДОЛЖЕН возвращать этот рецепт.
  • Запрос рецептов с продуктами (2) НЕ ДОЛЖЕН возвращать этот рецепт.

Что я пробовал до сих пор:

Recipe.joins(:ingredients)
.where('recipe_ingredients.require_in_filter = ? AND recipe_ingredients.product_id IN (?)', true, [3,4])
.distinct

Но этот запрос возвращает рецепты с ЛЮБЫМ идентификатором продукта (3 или 4 в данном случае). Но мне нужны только рецепты со ВСЕМИ этими продуктами.

Я нашел аналогичный вопрос PostgreSQL, где все в массиве, но не помогает.

Recipe.joins(:ingredients)
.select('COUNT(*) AS count, recipes.*')
.where('recipe_ingredients.require_in_filter = ? AND recipe_ingredients.product_id IN (?)', true, [3,4])
.group('recipes.id').having('count(*) = ?', 2)

всегда возвращать пустой массив

Этот сырой sql

select recipes.*
from recipes 
join recipe_ingredients
on recipe_ingredients.recipe_id = recipes.id
where not exists(
    select * from recipe_ingredients ri 
    where ri.recipe_id = recipes.id
    and ri.product_id not in(2,3,17)        
)
and recipe_ingredients.require_in_filter = true
group by recipes.id

возвращает рецепт с 2 и 3 в качестве единственных обязательных продуктов, но этого не должно быть, потому что 17 также необходимо.

Моих знаний SQL недостаточно для выполнения этой задачи


person Oleg Antonyan    schedule 05.11.2015    source источник


Ответы (1)


Ваш второй запрос не работает, потому что вы GROUP выполняете recipes.id, когда, если я правильно понимаю, вы хотите запросить количество различных product_id, поэтому попробуйте GROUPing следующим образом:

Recipe.joins(:ingredients)
.where('recipe_ingredients.require_in_filter = ? AND recipe_ingredients.product_id IN (?)', true, [3,4])
.group('recipe_ingredients.product_id').having('COUNT(*) = ?', 2)
person eirikir    schedule 05.11.2015
comment
Выдает ошибку: PG::GroupingError: ERROR: column "recipes.id" must appear in the GROUP BY clause or be used in an aggregate function - person Oleg Antonyan; 06.11.2015