如何在 join table 而不是 OR / IN 查询中查找具有所有必需 has_many 关联的记录

How to find a record having all required has_many associations in join table instead of OR / IN query

我有一家餐厅 Table、一家美食 Table 和一个连接多对多的连接 table。我想找到一家与两种特定美食相匹配的餐厅,即找到连接 table 中列出的与美食 1 和美食 2 相关的餐厅。

我可以用 each 和 includes 一起破解一些东西,但感觉在使用 ActiveRecord 构建查询时一定缺少一些直接和明显的东西。

Restaurant
ID| Name
1 | McDonalds
2 | Taco Bell

Cuisine
ID| Style
1 | American
2 | Mexican 
3 | Fast Food 

Restaurant_Cuisines
ID | Restaurant_ID | Cuisine_ID
1  | 1             | 1
2  | 1             | 3
3  | 2             | 2
4  | 2             | 3

我希望能够查询既提供美式食品又提供快餐的餐厅,这样可以返回麦当劳而不是塔可钟,因为塔可钟提供快餐而不是美式。

假设您有外键的 ID,您可以使用 joins 并像这样传递连接 table 的 ID:

cuisine_ids = Cuisine.where(Style: ['American', 'Mexican']).pluck(:id)

restaurants = Restaurant.joins(:cuisines).where(cuisines: {id: cuisine_ids})

我可以想到以下查询,它可能不是最优化的解决方案,但它给出了正确的答案,并且可以指导获得优化答案的方向。

rest_ids = Restaurant_Cuisines.where(Cuisine_ID: 1).pluck(:Restaurant_ID) && Restaurant_Cuisines.where(Cuisine_ID: 3).pluck(:Restaurant_ID)
Restaurant.where(id: rest_ids)

如果需要泛化:

def specific_restaurant(cuisine_ids)
  ids = cuisine_ids.map { |id| Restaurant_ID.where(Cuisine_ID: id).pluck(:Restaurant_ID) }.reduce(:&)
  Restaurant.where(id: ids) if ids.present?
end

肯定是 N+1,其中 Ncuisine_ids,但如果 N 是 limited/few 也没有坏处。

更新 - 最后,单个查询!

def specific_restaurant(cuisine_ids)
  ids = RestaurantCuisine.where(cuisine_id: cuisine_ids).group(:restaurant_id).having("count(*) = #{cuisine_ids.count}").pluck(:restaurant_id)
  Restaurant.where(id: ids) if ids.present?
end