通过 rails 中 has_many 关联到模型的多个连接记录 ID 搜索模型

Search for model by multiple join record ids associated to model by has_many in rails

我的产品模型设置如下:

class Product < ActiveRecord::Base
  has_many :product_atts, :dependent => :destroy
  has_many :atts, :through => :product_atts
  has_many :variants, :class_name => "Product", :foreign_key => "parent_id", :dependent => :destroy
end

我想搜索与多个属性关联的产品。

我想这也许行得通:

Product.joins(:product_atts).where(parent_id: params[:product_id]).where(product_atts: {att_id: [5,7]})

但这似乎不符合我的要求。这是 ID 或 ID.

所以我尝试了以下方法:

Product.joins(:product_atts).where(parent_id: 3).where(product_atts: {att_id:  5}).where(product_atts: {att_id:  7})

但这也不起作用,它 returns 0 个结果。

所以我的问题是如何通过传入相同模型类型的多个连接模型的属性来查找模型?

解决方案:

 att_ids = params[:att_ids] #This is an array of attribute ids
 product = Product.find(params[:product_id]) #This is the parent product
 scope = att_ids.reduce(product.variants) do |relation, att_id|
   relation.where('EXISTS (SELECT 1 FROM product_atts WHERE product_id=products.id AND att_id=?)', att_id)
 end

 product_variant = scope.first

这是一个看似简单的请求,由于 SQL 的工作原理,实际上变得非常棘手。联接总是只是将 连接在一起,而您的 WHERE 子句一次只会查看 一个 行(因此您的期望是没有像您预期的那样工作 -- 一行不可能有同一列的两个值。

在处理原始 SQL 时,有很多方法可以解决这个问题,但在 Rails 中,我发现最简单(不是最有效)的方法是使用EXISTS 关键字。将其包装在一个可处理任意数量的所需 att_ids 的解决方案中,您将得到:

scope = att_ids_to_find.reduce(Product) do |relation, att_id|
  relation.where('EXISTS (SELECT 1 FROM product_atts WHERE parent_id=products.id AND att_id=?)', att_id)
end

products = scope.all

如果您不熟悉 reduce,那么它会采用 Product,然后为每个 att_id 添加一个额外的 where 子句。最终结果类似于 Product.where(...).where(...).where(...),但您不必为此担心太多。当与作用域和其他联接混合使用时,此解决方案也很有效。