2010-04-23 7 views
9

He creado una aplicación de blog w/ruby ​​on rails y estoy tratando de implementar una función de búsqueda. La aplicación de blog permite a los usuarios etiquetar publicaciones. Las etiquetas se crean en su propia tabla y belong_to :post. Cuando se crea una etiqueta, también lo es un registro en la tabla de etiquetas donde el nombre de la etiqueta es tag_name y está asociado por post_id. Las etiquetas son cadenas.Buscando en Ruby on Rails: ¿cómo busco cada palabra ingresada y no la cadena exacta?

Estoy tratando de permitir que un usuario busque cualquier palabra tag_name en cualquier orden. Esto es lo que quiero decir. Digamos que una publicación en particular tiene una etiqueta que es 'controlador de código ruby'. En mi función de búsqueda actual, esa etiqueta se encontrará si el usuario busca 'ruby', 'ruby code' o 'ruby code controller'. No se encontrará si el usuario escribe 'controlador ruby'.

Esencialmente lo que estoy diciendo es que me gustaría que cada palabra ingresada en la búsqueda sea buscada, no necesariamente la 'cadena' que se ingresa en la búsqueda.

He estado experimentando con la provisión de campos de texto múltiples para permitir al usuario escribir varias palabras, y también he estado jugando con el código siguiente, pero no puedo lograr lo anterior. Soy nuevo en ruby ​​and rails lo siento si esta es una pregunta obvia y antes de instalar una joya o un plugin pensé que verificaría si había una solución simple. Aquí está mi código:

Vista: /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 Modelo

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 

Respuesta

10

Si leo el problema correctamente, que desea devolver una fila si los nombres de las etiquetas de la fila coincide con una de las palabras dictadas en la cadena de consulta.

puede volver a escribir su método de search de la siguiente manera:

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

Si necesita adaptación parcial a continuación, hacer lo siguiente:

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 

Editar 1 Si desea pasar varias cadenas de búsqueda a continuación, :

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 

Ahora puedes realizar llamadas como:

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 

advertencia:

el comodín LIKE búsquedas no son muy eficientes (ya que no utilizan el índice). Debería considerar usar Sphinx (a través de ThinkingSphinx) O Solr (a través de SunSpot) si tiene muchos datos.

+0

Acabo de probar su segundo bloque de código y obtuve el siguiente error: SQLite3 :: SQLException: no dicha columna: controller: SELECT * FROM "tags" WHERE (ruby OR controller) – bgadoci

+0

para la búsqueda: ruby ​​controller – bgadoci

+0

Hubo errores en mi código. Actualizado el código eche un vistazo. –

0

Usted puede tratar de establecer hasta ferret, o si realmente está agachado solo usando rieles, intente esto:

# 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 

esperanza esto ayuda =)

0

Suena como que necesita una búsqueda de texto completo. La mejor integración de búsqueda ahora es con Sphinx y el complemento Thinking_Sphinx. Lo he usado en varios proyectos y es muy fácil de configurar.

Usted necesita instalar la esfinge en el host de modo que si usted está usando un servidor compartido que podría presentar algunos problemas.

También puede utilizar la búsqueda de texto completo en una base de datos MySQL MyISAM, pero el rendimiento en el que es bastante pobre.

Una vez que haya instalado su esfinge que sólo hay que poner lo que desee indexar en su modelo y llamar model.search. Los resultados serán una lista de objetos modelo. Es compatible con will_paginate también.

0

Sugeriría mirar Searchlogic si no desea utilizar un motor de búsqueda de texto completo separado (Ferret, Sphinx, etc.). Hace que las búsquedas simples sean extremadamente fáciles, aunque es posible que no desee utilizarlas en un área pública sin muchas pruebas.

También puedes ver el Railscast en él: http://railscasts.com/episodes/176-searchlogic

0

1.You puede hacer algo de código en tu mensaje del controlador como tal: -

<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.Now hacer algunos cambios en su Ver fichero: -

<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. y O Lo importante es que debe haber una relación de muchos a muchos entre las etiquetas y publicar.

+0

desearía que esto pudiera ayudar, me gustó y funcionó para mí :-) –