首页 > 解决方案 > 如何在Rails中使用AND而不是OR来查询has_and_belongs_to?

问题描述

这可能是一个简单的问题,但我正在努力。这是我正在尝试做的事情:

我有一门课,我们称之为CarHABTM 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])

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

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

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

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

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

注意:给定汽车的特征数量是可变的,传入的特征列表的数量也是可变的。我只想将传入的功能列表与功能相结合,并且只返回具有基于功能列表的所有功能的汽车。

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

标签: ruby-on-railsjoinactiverecordhas-and-belongs-to-many

解决方案


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

然后你可以这样查询:

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

  2. Car JOIN Featureby ,然后使用@>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.


推荐阅读