Ruby on rails Rails查询有很多:通过有条件地使用多个ID

Ruby on rails Rails查询有很多:通过有条件地使用多个ID,ruby-on-rails,ruby,ruby-on-rails-4,Ruby On Rails,Ruby,Ruby On Rails 4,我正试图通过LocationFeature模型为具有位置和功能的网站构建一个过滤系统。基本上,它应该做的是根据特征ID的组合给我所有的位置 例如,如果我调用该方法: Location.find_by_features(1,3,4) 它应该只返回包含所有选定特征的位置。因此,如果某个位置具有特性_id[1,3,5],则不应返回该位置,但如果该位置具有[1,3,4,5],则应返回该位置。然而,目前它给了我一个有或的位置。因此,在本例中,它同时返回这两个,因为其中的每一个中都存在一些特性ID 以下是

我正试图通过LocationFeature模型为具有位置和功能的网站构建一个过滤系统。基本上,它应该做的是根据特征ID的组合给我所有的位置

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

Location.find_by_features(1,3,4)
它应该只返回包含所有选定特征的位置。因此,如果某个位置具有特性_id[1,3,5],则不应返回该位置,但如果该位置具有[1,3,4,5],则应返回该位置。然而,目前它给了我一个有的位置。因此,在本例中,它同时返回这两个,因为其中的每一个中都存在一些特性ID

以下是我的模型:

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: 9}, features: {id: 1})
但这只是给我所有的位置与功能_id为1


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

当您执行包含时,它会在内存中生成一个“伪表”,其中包含表a和表B的所有组合,在这种情况下,通过外键连接。(在本例中,已经包含了一个连接表(功能单元位置),使事情变得复杂。)

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

为此,我要做的是忽略features表:您只需查看联接表,
location\u features
,即可测试是否存在特定的
feature\u id
值。我们需要一个查询来比较这个表中的特征id和位置id

一种方法是获取特性,然后获取一个数组集合(如果关联了location_id(只调用联接表)),然后查看所有数组中都有哪些location id:(我已经重命名了您的方法,以便更具描述性)

注1:params中
*ids
中的星号表示它将参数列表(包括单个参数,类似于“一个列表”)转换为单个数组

注2:
inject
是一种方便的设备。它说“在数组的第一个和第二个元素之间,然后在这个和第三个元素的结果之间,然后在这个和第四个元素的结果之间,等等,直到结束。在这种情况下,我在每对元素(a和b)中的两个元素之间执行的代码是”&在处理数组时,它是“set intersection操作符”-这将只返回两对中的元素。当您浏览完执行此操作的数组列表时,只有所有数组中的元素将幸存下来。这些是与所有给定特征关联的位置ID


编辑:我确信有一种方法可以通过一个sql查询来实现这一点,可能使用
group\u concat
,其他人可能会很快发布:)

我会将其作为一组子查询来实现。如果您愿意,您实际上也可以将其作为作用域来执行

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)
}

它是哪个数据库管理系统?Mysql、postgres、sqlite等?谢谢,很好的解释!但是,如果我尝试这个方法,它会给我一个Feature.find_all_by_id的no-method错误-知道为什么吗?哦,对不起,这是rails版本的东西:
find_all_by_
在rails 4中已被弃用。替换
功能。用
功能通过\u id(id)
查找所有功能。其中([“id in(?”,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
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)
}