2011-03-11 53 views
2

我试图用Ruby,ActiveRecord和MySQL在报价数据库中搜索多个词。我做的方式如下所示,它正在工作,但我想知道是否有更好的方法。用Ruby和MySQL搜索多个词

# receives a string, splits it in a array of words, create the 'conditions' 
# query, and send it to ActiveRecord 
def search 
    query = params[:query].strip.split if params[:query] 
    like = "quote LIKE " 
    conditions = "" 
    query.each do |word| 
     conditions += (like + "'%#{word}%'") 
     conditions += " AND " unless query.last == word 
    end 
    @quotes = Quote.all(:conditions => conditions) 
end 

我想知道是否有更好的方法来编写这个'条件'字符串。我也尝试过使用字符串插值,例如使用*运算符,但最终需要更多的字符串处理。在此先感谢

回答

7

首先,我强烈建议您将Model的逻辑转换为Models。不要在Controller中创建搜索逻辑,而要在报价模式中创建#search方法。

class Quote 
    def self.search(query) 
    ... 
    end 
end 

和您的控制器将成为

# receives a string, splits it in a array of words, create the 'conditions' 
# query, and send it to ActiveRecord 
def search 
    @quotes = Quote.search(params[:query]) 
end 

现在,回到原来的问题。您现有的搜索逻辑确实犯了一个非常糟糕的错误:它直接插值打开您的代码到SQL注入。假设你使用Rails 3,你可以利用新的#where语法。

class Quote 
    def self.search(query) 
    words = query.to_s.strip.split 
    words.inject(scoped) do |combined_scope, word| 
     combined_scope.where("quote LIKE ?", "%#{word}%") 
    end 
    end 
end 

这是一个高级话题。我想了解combined_scope + inject做什么,我建议您阅读文章The Skinny on Scopes

+0

如果您在示例中演示了mysql的全文搜索 – 2011-03-11 19:56:47

+2

,我会推荐'to_s.downcase.strip.split.uniq'将其折叠为小写并删除重复的单词。 – 2011-03-12 05:17:29

+0

我正在使用Rails 2.3.5,但无论如何,答案有很大帮助。谢谢。 – Eduardo 2011-03-12 14:37:48

1

更好的方法来执行全文搜索。您可以在MySQL中这样做,但我会强烈建议Solr。有许多资源online用于在轨道中实施Solr,但我会推荐Sunspot作为入口点。

+0

Solr的确实是一个不错的选择,但它是一个相当复杂的库。在某些情况下,您实际上并不需要添加Solr依赖关系,对MySQL尝试这种查询是完全正确的。我相信在这种情况下,用户可以通过重构他的代码而获得高度益处,而无需采取步骤来尝试Solr。 – 2011-03-11 17:11:43

3

MySQL的全文检索不能正常工作,那么最好的方式做到这一点:

class Quote 
    def self.search_by_quote(query) 
    words = query.to_s.strip.split 
    words.map! { |word| "quote LIKE '%#{word}%'" } 
    sql = words.join(" AND ") 
    self.where(sql) 
    end 
end