0

我得到了一个带有 has_many Types 表和几个范围的产品模型:

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)时一切顺利,但一次有两个范围(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.v 建议,解释如下

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
4

1 回答 1

0

如果您有数据库背景,这将非常明显地说明为什么您无法根据您编写范围的方式找到具有多种类型的产品。

从这些范围写入的数据库查询将使行相乘,以确保具有多种类型的产品对于每种类型都有不同的行。结合两个范围时的查询正在编写

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(这将在您的数据库上更轻)并将其映射到一个数组中,该数组将被转储到 product_ids 数组中。在遍历每个类型名称之后,它将找到在每个数组中相交的所有产品,从而得到传入所有类型的产品。

于 2015-09-02T13:54:17.310 回答