问题描述
我有一堂课,我们称之为 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
。
然后你可以这样查询:
-
使用ARRAY_AGG聚合函数返回一个
subquery
,每一行都有一个feature names
的所有car
,例如[{car_id: 1,names: ["Bumper","Brakes"]},...]
-
Car
JOIN
Feature
bysubquery
,然后使用 @> 运算符过滤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
。