如何使用 AND 跨连接而不是 OR 对 Rails 中的 has_and_belongs_to 进行查询?

问题描述

这可能是一个简单的问题,但我很挣扎。这是我想要做的:

我有一堂课,我们称之为 Car 那个 HABTM Features。所以:

class Car < ApplicationRecord
  has_and_belongs_to_many :features
end

class Feature < ApplicationRecord
  has_and_belongs_to_many :cars
end

假设我创建了以下数据:

bumper = Feature.create(name: "Bumper")
windshield = Feature.create(name: "Windshield")
Car.create(name: "Toyota",features: [bumper,windshield])

所以现在我有一个具有两个功能的汽车实例。

假设有人传入(例如通过网络表单)一个功能列表。我想做一个查询,只查找具有传入的所有功能的汽车。

因此,如果传入的特征列表是:["Bumper","Brakes"],那么它不应该找到那辆“Toyota”汽车。但是,如果功能列表是 ["Bumper"] 它应该,或者如果功能列表是 ["Windshield","Bumper"] 它应该。

这显然不起作用,因为它是一个“或”: Car.joins(:features).where(features: {name: ["Bumper","Brakes"]}) 这将返回“Toyota”汽车,因为“Bumper”与连接到该汽车的功能相匹配。在这种情况下,我希望它做的是返回 [](没有汽车)。

如何为这种情况设置 Rails 查询

注意:对于给定的汽车,特征的数量是可变的,传入的特征列表的数量也是可变的。我只想将传入的特征列表与特征进行 AND 运算,并且只返回具有基于特征列表的所有特征的汽车。

我知道这可能很容易,但我似乎无法弄清楚。

解决方法

让我们从一个更简单的案例开始,Feature 有一个 car_id 字段,Car has_many features

然后你可以这样查询:

  1. 使用ARRAY_AGG聚合函数返回一个subquery,每一行都有一个feature names的所有car,例如[{car_id: 1,names: ["Bumper","Brakes"]},...]

  2. Car JOIN Feature by subquery,然后使用 @> 运算符过滤 cars

# Example code,may need to tweak a bit
sql = Feature.select(car_id,ARRAY_AGG(name) as names)
             .group(:car_id)
             .order(:car_id)
             .to_sql

Car.joins("INNER JOIN (#{sql}) AS features ON cars.id = features.user_id")
   .where("features.names @> ARRAY[?]::varchar[]",names)

就您而言,您使用了 has_and_belongs_to_many,因此您需要稍微调整一下以使用 joining table,例如car_features