Я в свое время так и не нашел ответ на этот вопрос.
Задача была похожая: интернет-магазин, товары, фильтры товаров. При фильтрации товаров нужно выбирать товар у которого, помимо прочего, есть 2 фильтра И с id=1 И с id=2, например (Когда нужно выбрать, скажем, ноутбук у которого И 8ГБ памяти И 14 дюймов диагональ)
Был вариант с фасетным поиском с помощью полнотекстовых движков (sphinx, solr, elasticsearch и т.д.), но они бы были оверхедом в той ситуации.
Сейчас уже долгое время работает вот такой метод:
def filter(fvalues_ids)
grouped = Catalog::FilterValue.where(id: fvalues_ids.map(&:to_i)).to_group
query = []
grouped.each_value do |fvalues|
ids = fvalues.map(&:id).join(', ')
query << %(
SELECT catalog_products.id
FROM catalog_products
INNER JOIN
catalog_product_filter_values ON catalog_product_filter_values.catalog_product_id = catalog_products.id
WHERE
catalog_product_filter_values.catalog_filter_value_id IN (#{ids})
)
end
query = query.join("\nINTERSECT\n")
ids = ActiveRecord::Base.connection.execute(query).map{ |row| row['id'] }
where(id: ids)
end
Так, думаю, все понятно. Разве что метод #to_group - он группирует фильтры (например, 8ГБ оперативки и 4ГБ оперативки нужно сравнивать по ИЛИ, а 8ГБ оперативки и 14 дюймов экран - по И)
Решение выглядит костыльно, но более элегантного не нашел.