我得到了一个带有has_many Types表和几个作用域的Product模型:
class Product < ActiveRecord::Base
has_many :product_types
has_many :types, through: :product_types
scope :type1, -> { joins(:types).where(types: { name: "Type1" }) }
scope :type2, -> { joins(:types).where(types: { name: "Type2" }) }
end
当我尝试使用一个作用域(Product。type1(例如type1))一切正常,但是一次两个作用域(Product.type1.type2)返回一个空查询。是的,一个产品可以有多种类型。
最终目标是使用复选框形式按类型创建产品过滤器。当我检查type1和type2时,我希望看到同时具有type1和type1的所有产品。
乌利希期刊指南1
所以我试着做了几个查询,然后&他们是@aaron。v显示。我想在函数内部执行逻辑,所以:
def products_by_type_name(types)
all_types = types.each { |type| Product.joins(:types).where(types: { name: type }).distinct }
...
end
我的观点是遍历每种类型,收集所有产品,然后&它们在函数内部。问题是当我迭代时,每个循环返回字符串而不是哈希数组。
Product.joins(:types).where(types: { name: types }).distinct # returns array of hashes, it's okay.
types.each { |type| Product.joins(:types).where(types: { name: type }).distinct } # each loop returns string (Type name) instead of array of hashes.
我做错了什么?
解决方案1
由@aaron建议。
def self.by_type_name(types)
product_ids = []
types.each do |t|
product_ids << (joins(:types).where(types: { name: t }).distinct.select(:id).map(&:id))
end
find(product_ids.inject(:&))
end
解决方案2
在reddit上找到。在这个函数中,您将获取至少具有一个所需类型的所有产品,然后只对具有所需类型计数的产品进行分组。因此,您只能获得同时属于每种类型的产品。
def self.by_type_name(types)
joins(:types).where(types: { name: types }).distinct.group('products.id').having('count(*) = ?', types.each.count)
end
如果您有数据库背景,那么很明显您无法根据编写作用域的方式找到具有多种类型的产品。
从这些范围写入的数据库查询将行相乘,以确保具有多种类型的产品对每种类型都有不同的行。当你合并两个作用域时,你的查询是写
where `types`.name = "Type1" AND `types`.name = "Type2"
这种情况永远不会发生,因为在连接中,列不会以同一行的倍数添加。
你要做的是有一个方法,你可以传递一个类型名称数组来找到它
def self.by_type_name(types)
joins(:types).where(types: { name: types }).distinct
end
此方法本身可以接受一个名称,也可以接受要查找的类型的名称数组
如果您传递一个类型名称的数组,如["type1", "type2"],这将导致类似
的查询where `types`.name in ("type1", "type2")
然后你应该看到你期望的结果
更新为了修改您在查找具有所有类型的产品时所需要的方法,我决定这样做
def self.by_type_name(types)
product_ids = []
types.each do |t|
product_ids << (joins(:types).where(types: { name: t }).distinct.select(:id).map(&:id))
end
find(product_ids.inject(:&))
end
该方法需要一个类型名称的数组(即使它是一个类型,也要传递一个包含一个类型的数组)。它要做的是找到具有特定类型名称的所有产品,只选择产品id(在DB上比较轻)并将其映射到一个数组中,该数组将被转储到product_ids数组中。在循环遍历每个类型名之后,它将找到在每个数组中相交的所有产品,从而得到传递了所有类型的产品。