在Ruby on Rails 3中组合first_name和last_name列的最佳方法?

时间:2012-02-24 23:41:44

标签: sql ruby-on-rails ruby

我的User模型中有一个方法:

def self.search(search)
  where('last_name LIKE ?', "%#{search}%")
end

但是,我的用户能够在同一查询中搜索first_name和last_name会很好。

我正在考虑像这样创建一个virtual attribute

def full_name
  [first_name, last_name].join(' ')
end

但这在数据库级别上是否有效。或者有更快的方法来检索搜索结果吗?

感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

您的示例中的虚拟属性只是类方法,并且不能被类似于查找的ActiveRecord方法用于查询数据库。

检索搜索结果的最简单方法是修改搜索方法:

def self.search(search)
  q = "%#{query}%"
  where("first_name + ' ' + last_name LIKE ? OR last_name + ' ' + first_name LIKE ?", [q, q])
end

其中varchar连接语法与您选择的数据库兼容(在我的示例中为MS SQL)。

答案 1 :(得分:0)

在您的示例中,搜索功能仍将在SQL级别运行。

因此,要按照您的示例,您的搜索代码可能是:

def self.search_full_name(query)
  q = "%#{query}%"
  where('last_name LIKE ? OR first_name LIKE ?', [q, q])
end

注意 - 这些类型的LIKE查询,因为它们在前缀上有一个通配符,在大型数据集上会很慢,即使它们被索引。

答案 2 :(得分:0)

可以实现的一种方法是通过标记(拆分)搜索查询并为每个标记创建一个条件:

  def self.search(query)
    conds  = []
    params = {}
    query.split.each_with_index do |token, index|
      conds.push "first_name LIKE :t#{index} OR last_name LIKE :t#{index}"
      params[:"t#{index}"] = "%#{token}%"
    end

    where(conds.join(" OR "), params)
  end

还要确保防止SQL注入攻击。

但是,最好使用全文搜索工具,例如ElasticSearch及其名为Tire的Ruby gem来处理搜索。

编辑:修正了代码。

答案 3 :(得分:0)

可以使用范围来处理复杂模式,这是我正在处理的一个项目的示例:

   scope :search_by_name, lambda { |q|
      if q
        case q
        when  /^(.+),\s*(.*?)$/
         where(["(last_name LIKE ? or maiden_name LIKE ?) AND (first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
           "%#{$1}%","%#{$1}%","%#{$2}%","%#{$2}%","%#{$2}%"
           ])
        when /^(.+)\s+(.*?)$/
          where(["(last_name LIKE ? or maiden_name LIKE ?) AND (first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
            "%#{$2}%","%#{$2}%","%#{$1}%","%#{$1}%","%#{$1}%"
            ])
        else
           where(["(last_name LIKE ? or maiden_name LIKE ? OR first_name LIKE ? OR common_name LIKE ? OR middle_name LIKE ?)",
             "%#{q}%","%#{q}%","%#{q}%","%#{q}%","%#{q}%"
             ])
        end
      else
       {}
      end
}

正如您所看到的,我根据提供的内容进行正则表达式匹配以检测不同的模式,构建不同的搜索。作为额外的好处,如果没有提供任何内容,它将返回一个空哈希,实际上是where(true)并返回所有结果。

正如其他地方所提到的,当像%foo%这样在双方使用通配符时,db无法索引列,因此在非常大的数据集上可能会变慢。

相关问题