使用Ruby和MySQL进行多词搜索

使用Ruby和MySQL进行多词搜索,mysql,ruby,search,activerecord,Mysql,Ruby,Search,Activerecord,我正在尝试使用Ruby、ActiveRecord和MySQL在quotes数据库中完成多词搜索。我这样做的方式如下所示,它是有效的,但我想知道是否有更好的方式来做 # 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 pa

我正在尝试使用Ruby、ActiveRecord和MySQL在quotes数据库中完成多词搜索。我这样做的方式如下所示,它是有效的,但我想知道是否有更好的方式来做

# 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

我想知道是否有更好的方法来组成这个“条件”字符串。我还尝试使用字符串插值,例如使用*运算符,但最终需要更多的字符串处理。提前感谢

更好的方法是实现全文搜索。您可以在中完成此操作,但我强烈推荐。在rails中实现Solr有很多资源,但我建议将其作为切入点。

首先,我强烈建议您将模型的逻辑转移到模型中。不要在控制器中创建搜索逻辑,而是在报价模式中创建#搜索方法

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

这是一个有点高级的话题。如果您想了解
组合作用域
+
注入
的作用,我建议您阅读这篇文章。

在MySQL中创建全文索引。这样,您就可以将字符串处理留给MySQL了


示例:

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

Solr确实是一个不错的选择,但它是一个相当复杂的库。在某些情况下,您实际上不需要添加Solr依赖项,对MySQL尝试这种查询是完全正确的。我相信在这种情况下,用户可以从重构代码中获益匪浅,而无需采取步骤尝试Solr。如果您在示例中演示mysql的全文搜索,那就太好了。我建议
使用downcase.strip.split.uniq
折叠成小写并删除重复的单词。我使用的是Rails 2.3.5,但无论如何,这个答案帮助很大。谢谢。Rails 5更改:将“scoped”替换为“all”。多年来一直在使用这个有用的答案。