【问题标题】:Rails: How to implement search through associationRails:如何通过关联实现搜索
【发布时间】:2012-05-02 19:11:29
【问题描述】:

原始问题

我的应用处理“机器”。机器有规格,写着尽可能多的“规格”。这些规格中的每一个都包含对“Spec_Field”的引用(诸如“重量”、“高度”、“容量”等字段)和一个值。

Class Machine
  has_many :specs
  has_many :spec_fields, :through => :specs

Class Spec
  belongs_to :machine
  belongs_to :spec_field

我想要做的是在每台机器的#show 操作中有一个搜索功能,用户在其中检查他想要匹配的规格(“是的,长度 50 是我需要的,容量 500 是完美,我不在乎宽度”),然后点击“查找相似”,然后会找到所有具有相似规格的机器。

我假设我需要一个搜索模型,类似于以下截屏视频: http://railscasts.com/episodes/111-advanced-search-form-revised?autoplay=true

在机器的#show 操作中,我需要检查这台机器的规格,并将它们添加到“搜索”实例中。那么这是否意味着“规范”也需要属于“搜索”?

我只是想不通如何在我的案例中组织所有内容。任何帮助表示赞赏!

数据库架构

机器:

t.string   "title"
t.text     "description"
t.datetime "created_at",                      :null => false
t.datetime "updated_at",                      :null => false
t.string   "status"
t.integer  "category_id"
t.string   "manufacturer"
t.string   "model"
t.string   "serial"
t.string   "year"
t.string   "location"

规格:

t.integer  "machine_id"
t.integer  "field_id"
t.text     "value"
t.float    "base_value"
t.integer  "unit_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false

规范字段:

t.string  "label"
t.integer "base_units"

似乎有效的解决方案

我添加了一个使用 SQL“IN”和“SELECT”的“With Spec”范围

 def self.with_spec(field_id,value)
    min_value = value.to_f * 0.8
    max_value = value.to_f * 1.2
    where("machines.id IN (SELECT machine_id FROM specs WHERE field_id = ? AND value > ? AND value < ?)",field_id,min_value,max_value)
  end

【问题讨论】:

标签: ruby-on-rails search associations has-many


【解决方案1】:

我会构建我所谓的“过滤器范围”;如果您使用适当命名的键传递哈希,它们将减少结果集。如果传入的是 nil 或空白,则忽略该条件。

然后我定义了一个类搜索方法,它结合了所有过滤器搜索,如下所示:

Class Spec
  belongs_to :machine
  belongs_to :spec_field

  # filter scopes 
  scope :with_machine,  lambda{|*term| where('machine_id = ?',term) if term.present? }
  scope :with_length,   lambda{|*term| where('length = ?',term)     if term.present? }
  scope :with_capacity, lambda{|*term| where('capacity = ?',term)   if term.present? }
  scope :with_width,    lambda{|*term| where('width = ?',term)      if term.present? }

  def self.filter_search(options)
    with_machine(  options[:machine]  ).
    with_length(   options[:length]   ).
    with_capacity( options[:capacity] ).
    with_width(    options[:width]    )
  end

end

现在您只需要构建一个具有适当命名输入的表单:

<%= text_field_tag 'search[machine]', @machine.id %>
<%= text_field_tag 'search[length]' %>
<%= text_field_tag 'search[capacity]' %>
<%= text_field_tag 'search[width]' %>
<%= submit_tag %>

并在控制器上调用它:

@specs = Spec.filter_search(params[:search]).all

另一种方法是设置全文搜索引擎,并以几乎相同的方式构建您的搜索方法。

【讨论】:

  • 这看起来很有趣,但是“Spec”模型没有“length”、“capacity”等字段,而是通过“field_id”指向另一个表(“Spec_Fields”)。这是为了允许无限数量的规范字段,并允许用户创建自己的规范字段。
  • @ef-n 如果您从 schema.rb 发布相关表可能会有所帮助
【解决方案2】:

我认为 gem meta_search 可以帮助您并简化您的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多