Поиск в Ruby on Rails - Как искать по каждому введенному слову, а не по точной строке? - PullRequest
9 голосов
/ 23 апреля 2010

Я создал приложение для блогов w / ruby ​​на рельсах и пытаюсь реализовать функцию поиска.Приложение блога позволяет пользователям отмечать сообщения.Теги создаются в их собственной таблице и belong_to :post.Когда тэг создан, то же самое происходит с записью в таблице тэгов, где имя тэга равно tag_name и связано с post_id.Теги - это строки.

Я пытаюсь разрешить пользователю искать любое слово tag_name в любом порядке.Вот что я имею в виду.Допустим, в конкретном сообщении есть тег 'ruby code controller'.В моей текущей функции поиска этот тег будет найден, если пользователь ищет «ruby», «ruby code» или «ruby code controller».Он не будет найден, если пользователь введет «ruby controller».

По сути, я хочу сказать, что я хотел бы, чтобы каждое слово, введенное в поиске, искалось, а не обязательно «строка», вводимая в поиске.

Я экспериментировалс предоставлением нескольких текстовых полей, позволяющих пользователю вводить несколько слов, а также поэкспериментировал с приведенным ниже кодом, но, похоже, не может выполнить вышеуказанное.Я новичок в ruby ​​и rails, так что извините, если это очевидный вопрос, и перед установкой гема или плагина я подумал, что проверю, есть ли простое исправление.Вот мой код:

Просмотр: /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 %>

TagsController

 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

Tag Model

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

Ответы [ 5 ]

10 голосов
/ 23 апреля 2010

Если я правильно прочитал вашу проблему, вы хотите вернуть строку, если имена тегов для строки соответствуют одному из слов, переданных в строке запроса.

Вы можете переписать свой 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), если у вас много данных.

0 голосов
/ 29 февраля 2012

1.Вы можете написать код в своем посте контроллера следующим образом: <code><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 2. Теперь внесите некоторые изменения в ваш файл просмотра: -

<code>  <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%>

3. И одна важная вещь заключается в том, что между тегами и post должно быть много-много связей.

0 голосов
/ 26 апреля 2010

Я бы посоветовал посмотреть Searchlogic , если вы не хотите использовать отдельную систему полнотекстового поиска (Ferret, Sphinx и т. Д.). Это делает простой поиск чрезвычайно простым, хотя вы можете не захотеть использовать его в публичных местах без большого количества тестирования.

Также посмотрите Railscast на нем: http://railscasts.com/episodes/176-searchlogic

0 голосов
/ 23 апреля 2010

Похоже, вам нужен полнотекстовый поиск. Наилучшая поисковая интеграция на данный момент - с Sphinx и плагином Thinking_Sphinx . Я использовал его в нескольких проектах, и он очень прост в настройке.

Вам необходимо установить sphinx на свой хост, так что если вы используете общий хост, который может представлять некоторые проблемы.

Вы также можете использовать полнотекстовый поиск в базе данных MyISAM MySQL, но производительность на нем довольно низкая.

После того, как вы установили свой sphinx, вы просто помещаете то, что хотите индексировать, в вашу модель и вызываете model.search. Результатом будет список объектов модели. Он также поддерживает will_paginate.

0 голосов
/ 23 апреля 2010

Вы можете попытаться настроить хорька , или, если вы действительно наклоняетесь, используя только рельсы, попробуйте это:

# 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

надеюсь, это поможет =)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...