【问题标题】:Searching in Ruby on Rails - How do I search on each word entered and not the exact string?在 Ruby on Rails 中搜索 - 如何搜索输入的每个单词而不是确切的字符串?
【发布时间】:2011-02-11 08:48:20
【问题描述】:

我已经构建了一个带有 ruby​​ on rails 的博客应用程序,并且我正在尝试实现搜索功能。博客应用程序允许用户标记帖子。标签在它们自己的表和belong_to :post 中创建。创建标签时,标签表中的记录也会创建,其中标签的名称为 tag_name 并由 post_id 关联。标签是字符串。

我试图允许用户以任何顺序搜索任何单词 tag_name。这就是我的意思。假设一个特定的帖子有一个标签是“ruby 代码控制器”。在我当前的搜索功能中,如果用户搜索“ruby”、“ruby 代码”或“ruby 代码控制器”,就会找到该标签。如果用户输入“ruby controller”,它将不会被找到。

基本上我要说的是,我希望在搜索中输入的每个单词都被搜索,而不一定是输入到搜索中的“字符串”。

我一直在尝试提供多个文本字段以允许用户输入多个单词,并且也一直在玩弄下面的代码,但似乎无法完成上述操作。我是 ruby​​ 和 rails 的新手,如果这是一个明显的问题,我很抱歉,在安装 gem 或插件之前,我想我会检查一下是否有一个简单的修复。这是我的代码:

查看:/views/tags/index.html.erb

<% form_tag tags_path, :method => 'get' do %>
    <p>
      <%= text_field_tag :search, params[:search], :class => "textfield-search" %>
      <%= submit_tag "Search", :name => nil, :class => "search-button" %>
    </p>
  <% end %>

标签控制器

 def index
    @tags = Tag.search(params[:search]).paginate :page => params[:page], :per_page => 5
    @tagsearch = Tag.search(params[:search])
    @tag_counts = Tag.count(:group => :tag_name, 
       :order => 'count_all DESC', :limit => 100)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @tags }
    end
  end

标签模型

class Tag < ActiveRecord::Base
  belongs_to :post
  validates_length_of :tag_name, :maximum=>42
  validates_presence_of :tag_name

  def self.search(search)
    if search
      find(:all, :order => "created_at DESC", :conditions => ['tag_name LIKE ?', "%#{search}%"])
    else
      find(:all, :order => "created_at DESC")
    end
  end

end

【问题讨论】:

    标签: ruby-on-rails ruby search


    【解决方案1】:

    如果我正确阅读了您的问题,如果行的标记名称与查询字符串中传递的单词之一匹配,您希望返回一行。

    你可以重写你的search方法如下:

    def self.search(search)
      all :conditions =>  (search ? { :tag_name => search.split} : [])
    end
    

    如果您需要部分匹配,请执行以下操作:

    def self.search(str)
      return [] if str.blank?
      cond_text   = str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
      cond_values = str.split.map{|w| "%#{w}%"}
      all(:conditions =>  (str ? [cond_text, *cond_values] : []))
    end
    

    编辑 1 如果你想传递多个搜索字符串,那么:

    def self.search(*args)
      return [] if args.blank?
      cond_text, cond_values = [], []
      args.each do |str|
        next if str.blank?  
        cond_text << "( %s )" % str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
        cond_values.concat(str.split.map{|w| "%#{w}%"})
      end
      all :conditions =>  [cond_text.join(" AND "), *cond_values]
    end
    

    现在您可以拨打电话,例如:

    Tag.search("Ruby On Rails")
    
    Tag.search("Ruby On Rails", "Houston")
    
    Tag.search("Ruby On Rails", "Houston", "TX")
    
    Tag.search("Ruby On Rails", "Houston", "TX", "Blah")
    
    Tag.search("Ruby On Rails", "Houston", "TX", "Blah", ....) # n parameters
    

    警告:

    通配符LIKE 搜索效率不高(因为它们不使用索引)。如果您有大量数据,您应该考虑使用 Sphinx(通过 ThinkingSphinx)或 Solr(通过 SunSpot)。

    【讨论】:

    • 我刚刚尝试了您的第二个代码块并收到以下错误:SQLite3::SQLException: no such column: controller: SELECT * FROM "tags" WHERE (ruby OR controller)
    • 完美。那效果很好。下一个问题(当你在思考这个问题时)有没有办法使用第二个文本字段来基本上说“搜索那个'第一个标签',但也要确保它有'这个标签'。”这可能是一个更复杂的问题,但我想在提出新问题之前先把它扔在这里。
    • 好的,我明白你在说什么,这就是我想做的。您提供的 Edit 1 代码似乎不起作用。我没有收到错误消息,只是似乎没有注册提交。
    • 在代码中我有方法和变量名称search。这就是它不起作用的原因。我已经修复了代码。看看吧。
    • 谢谢。似乎这仍然返回具有该特定标签的任何帖子,不一定只返回具有两个标签的帖子。不过没问题。足够近。如果我想更进一步,我会研究 searchlogic。听起来这是一个更简单的方法。在去插件/宝石之前尽可能多地学习。再次感谢。
    【解决方案2】:

    你可以尝试设置ferret,或者如果你真的很喜欢只使用导轨,试试这个:

    # Break the search string into words
    words = params[:search].blank? ? [] : params[:search].split(' ')
    
    conditions = [[]] # Why this way? You'll know soon
    words.each do |word|
      conditions[0] << ["tag_name LIKE ?"]
      conditions << "%#{word}%"
    end
    conditions[0] = conditions.first.join(" OR ") # Converts condition string to include " OR " easily ;-)
    
    # Proceed to find using `:conditions => conditions` in your find
    

    希望这会有所帮助 =)

    【讨论】:

      【解决方案3】:

      听起来您需要全文搜索。目前最好的搜索集成是使用SphinxThinking_Sphinx 插件。我已经在几个项目中使用过它,而且设置起来非常容易。

      您确实需要在您的主机上安装 sphinx,因此如果您使用可能会出现一些问题的共享主机。

      您也可以在 MyISAM MySQL 数据库中使用全文搜索,但性能很差。

      安装 sphinx 后,只需将要索引的内容放入模型中并调用 model.search。结果将是模型对象的列表。它也支持 will_paginate。

      【讨论】:

        【解决方案4】:

        如果您不想使用单独的全文搜索引擎(Ferret、Sphinx 等),我建议您查看 Searchlogic。它使简单的搜索变得非常容易,尽管您可能不想在没有大量测试的情况下在面向公众的区域使用它。

        还可以查看 Railscast:http://railscasts.com/episodes/176-searchlogic

        【讨论】:

          【解决方案5】:

          1.您可以在控制器帖子中进行一些编码,如下所示:-

          <pre>
          def show
              @post = Post.find(params[:id])
              @tag_counts = Tag.count(:group => :name, :order => 'updated_at DESC', :limit => 10)
              respond_to do |format|
                format.html # show.html.erb
                format.json { render json: @post }
              end
            end
          </pre>
          

          2.现在对视图文件进行一些更改:-

            <pre>
            <b>Tags:</b>
            <%= join_tags(@post) %>
            <%unless @tag_counts.nil?%>
            <% @tag_counts.each do |tag_name, tag_count| %> 
            <tr><td><%= link_to(tag_name, posts_path(:name => tag_name)) %></td>
            <td>(<%=tag_count%>)</td>
            </tr><% end %>
            <%end%>
          </pre> 
          

          3.重要的一点是标签和帖子之间应该有多对多的关系

          【讨论】:

            猜你喜欢
            • 2012-07-19
            • 1970-01-01
            • 2021-10-06
            • 2019-10-12
            • 2011-09-02
            • 1970-01-01
            • 2017-01-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多