我的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
但这在数据库级别上是否有效。或者有更快的方法来检索搜索结果吗?
感谢您的帮助。
答案 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无法索引列,因此在非常大的数据集上可能会变慢。