Rails通过有条件地使用多个id查询has_many:

我正在尝试通过LocationFeature模型为具有位置和function的网站构建过滤系统。 基本上它应该做的是基于特征ID的组合给我所有位置。

例如,如果我调用该方法:

Location.find_by_features(1,3,4) 

它应该只返回具有所有选定function的位置。 因此,如果某个位置具有feature_ids [1,3,5],则不应返回,但如果它具有[1,3,4,5]则应该返回。 但是,目前它给我的地点有其中任何一个。 因此,在此示例中,它返回两者,因为每个feature_id中都存在一些feature_ids。

这是我的模特:

 class Location < ActiveRecord::Base has_many :location_features, dependent: :destroy has_many :features, through: :location_features def self.find_by_features(*ids) includes(:features).where(features: {id: ids}) end end class LocationFeature < ActiveRecord::Base belongs_to :location belongs_to :feature end class Feature < ActiveRecord::Base has_many :location_features, dependent: :destroy has_many :locations, through: :location_features end 

显然,这段代码并没有按照我想要的方式运行,我只是无法理解它。 我也尝试过这样的事情:

 Location.includes(:features).where('features.id = 5 AND features.id = 9').references(:features) 

但它什么也没有回报。 使用OR而不是AND再次给我。 我也尝试过:

 Location.includes(:features).where(features: {id: 9}, features: {id: 1}) 

但这只是给了我feature_id为1的所有位置。

查询匹配所有请求function的位置的最佳方法是什么?

我会把它做成一组子查询。 如果您愿意,您实际上也可以将其作为范围。

 scope :has_all_features, ->(*feature_ids) { where( ( ["locations.id in (select location_id from location_features where feature_id=?)"] * feature_ids.count).join(' and '), *feature_ids) } 

当你做一个include时,它在内存中创建一个“伪表”,它包含表A和表B的所有组合,在这种情况下连接在foreign_key上。 (在这种情况下,已经包含了一个连接表(feature_locations),使事情变得复杂。)

此表中没有任何行满足条件features.id = 9 AND features.id = 1 。 每行只有一个features.id值。

我要做的就是忘记function表:你只需要查看连接表location_features ,来测试特定feature_id值的存在。 我们需要一个查询来比较此表中的feature_id和location_id。

一种方法是获取function,然后获取一组数组(如果关联的location_ids(只调用连接表)),然后查看所有数组中的位置ID :(我已将您的方法重命名为更具描述性)

 #in Location def self.having_all_feature_ids(*ids) location_ids = Feature.find_all_by_id(ids).map(&:location_ids).inject{|a,b| a & b} self.find(location_ids) end 

注1:params中*ids中的星号表示它将一个参数列表(包括一个参数,就像一个“list of one”)转换为一个数组。

注2: inject是一个方便的设备。 它说“在数组中的第一个和第二个元素之间执行此代码,然后在此结果与第三个元素之间,然后是第四个元素的结果,等等,直到结束。在这种情况下,我在每对(a和b)中的两个元素之间做的代码是“&”,当处理数组时,它是“集合交集运算符” – 这将只返回两对中的元素。你已经完成了这样做的数组列表,只有在所有数组中的元素才能存活下来。这些是与所有给定特征相关联的位置ID。

编辑:我敢肯定有一种方法可以使用单个SQL查询 – 可能使用group_concat – 其他人可能会很快发布:)