使用动态 ransacker 进行分面搜索

Facet search with dynamic ransackers

我正在尝试使用 Rails 创建一个通用产品目录应用程序,为了让不同类型的产品具有不同的属性,我将产品属性抽象到它们自己的 table 中,并带有 link table 在产品和存储值的 属性 之间。

-------------   --------------------
|products   |   |product_properties|   ------------
|-----------|   |------------------|   |properties|
|name       |---|value             |---|----------|
|description|   |product_id        |   |name      |
|etc...     |   |property_id       |   ------------
-------------   --------------------

例如,产品的宽度可能为 属性(将存储在 属性 table 中,以便重复使用),而宽度的值将被存储在 product_properties table 中记录了 link 产品的 属性。

这很好用,但我需要在产品模型中实现可分面搜索,并选择使用 ransack。所以要找到宽度大于 30 的所有产品,我必须做

Product.ransack(product_properties_property_name_eq: 'width', product_properties_value_gt: 30).result

这又可以正常工作,但我更愿意 'ransack' 使用 属性 名称

Product.ransack(width_gt: 30).result

是否有任何方法可以动态创建掠夺者(或替代方案)以允许我执行此操作?我试过使用 method_missing 但这让我很困惑。我正在考虑使用属性中的所有名称值在模型上创建范围 table 但我想我会先征求一些建议。

更新

我尝试在产品模型上实施一系列自定义洗劫程序

class Product < ActiveRecord::Base
  Property.pluck(:name, :id).each do |name, id|
    ransacker name, formatter: -> (value) { value.to_s.downcase } do
      product_properties = Arel::Table.new(:product_properties)
      product_properties[:value]
    end
  end
end

这让我越来越接近我能感觉到的答案。我还应该在这里做什么?

这很完美。这里的陷阱是 Arel::Nodes.build_quoted。我最初把它排除在外,我不会得到 errors/warning 的回报,但我同样也不会得到任何结果,这让我很困惑。这显然只有在使用 Rails 4.2+ (Arel 6.0+) 时才有必要。

Property.pluck(:id, :name).each do |id, name|
  product_properties = Arel::Table.new(:product_properties)

  ransacker name.to_sym, formatter: -> (value) { value.to_s.downcase } do
    Arel::Nodes::InfixOperation.new('AND',
      Arel::Nodes::InfixOperation.new('=',
        product_properties[:property_id], Arel::Nodes.build_quoted(id)
      ),
      product_properties[:value]
    )
  end
end

要实际使用它,我需要明确地将 product_properties table 加入查询

Product.joins(:product_properties).ransack(width_gt: 30)

正如 ransack 文档所述,一些人在使用 ransackers 时遇到的困难并非源于 Ransack,而是因为不了解 Arel。这绝对是这里的情况。