Rails Sunspot gem: использование граней с несколькими поисками по всей модели - PullRequest
9 голосов
/ 25 сентября 2011

Я пытаюсь внедрить поиск по всему миру через мощный гем Sunspot для Rails.Это включает в себя поиск по нескольким, очень разным моделям одновременно.То, что я ХОЧУ сделать, - это использовать функцию огранки, чтобы позволить пользователю фильтровать свои результаты поиска по каждой модели, или по умолчанию просматривать все на одной и той же странице, перемежаясь друг с другом в соответствии с классификатором: boost.Объединение кода огранки из Sunspot Railscast с кодом поиска нескольких моделей из другого вопроса Stackoverflow (вариант кода «Несколько типов» из документации Sunspot ) дал мне решение, которое, как мне кажется, сработало бы, но не работает.

Поиск по нескольким методам завершается успешно, но аспекты всегда обращаются в нуль.Мой основной подход заключается в предоставлении виртуального атрибута для каждой модели с одинаковым именем: search_class, который представляет собой просто имя класса модели, отображаемое в строку.Затем я пытаюсь использовать это как аспект.Однако в логике представления результаты фасета (@ search.facet (: search_class) .rows) всегда являются пустым массивом, в том числе когда @ search.results возвращает много разных моделей в одном запросе, несмотря на то, что каждый возвращаемый экземпляр имеетпрекрасно доступный атрибут Instance.search_class.

Я использую Rails 3.1.0 и sunspot-rails 1.2.1.

Что мне нужно сделать, чтобы этот код фацетирования работал?

Контроллер:

#searches_controller.rb
class SearchesController < ApplicationController

  def show
    @search = search(params[:q])
    @results = @search.results
  end

  protected
  def search(q)
    Sunspot.search Foo, Bar, CarlSagan do
      keywords q
      #provide faceting for "search class", a field representing a pretty version of the model name
      facet(:search_class)
      with(:search_class, params[:class]) if params[:class].present?
      paginate(:page => params[:page], :per_page => 30)
    end
  end

end

Модели:

#Foo.rb
class Foo < ActiveRecord::Base
  searchable do
    text :full_name, :boost => 5
    text :about, :boost => 2
    #string for faceting
    string :search_class
  end

  #get model name, and, if 2+ words, make pretty
  def search_class
    self.class.name#.underscore.humanize.split(" ").each{|word| word.capitalize!}.join(" ")
  end
end

#Bar.rb
class Bar < ActiveRecord::Base
  searchable do
    text :full_name, :boost => 5
    text :about, :boost => 2
    #string for faceting
    string :search_class
  end

  #get model name, and, if 2+ words, make pretty
  def search_class
    self.class.name.underscore.humanize.split(" ").each{|word| word.capitalize!}.join(" ")
  end
end

#CarlSagan.rb
class CarlSagan < ActiveRecord::Base
  searchable do
    text :full_name, :boost => 5
    text :about, :boost => 2
    #string for faceting
    string :search_class
  end

  #get model name, and, if 2+ words, make pretty
  def search_class
    self.class.name#.underscore.humanize.split(" ").each{|word| word.capitalize!}.join(" ")
  end
end

Вид:

#searches/show.html.erb
<div id="search_results">
<% if @results.present? %> # If results exist, display them

            # If Railscasts-style facets are found, display and allow for filtering through params[:class]
    <% if @search.facet(:search_class).rows.count > 0 %>
        <div id="search_facets"> 
          <h3>Found:</h3>  
          <ul>  
            <% for row in @search.facet(:search_class).rows %>  
              <li>  
                <% if params[:class].blank? %>  
                  <%= row.count %>  <%= link_to row.value, :class => row.value %>
                <% else %>  
                  <strong><%= row.value %></strong> (<%= link_to "remove", :class => nil %>)  
                <% end %>  
              </li>  
            <% end %>  
          </ul>  
        </div>
    <% end %>


    <% @results.each do |s| %>
        <div id="search_result">
            <% if s.class.name=="Foo"%>
                <h5>Foo</h5>
                <p><%= link_to s.name, foo_path(s) %></p>
            <% elsif s.class.name=="Bar"%>
                <h5>Bar</h5>
                <p><%= link_to s.name, bar_path(s) %></p>
            <% elsif s.class.name=="CarlSagan"%>
                <h5>I LOVE YOU CARL SAGAN!</h5>
                <p><%= link_to s.name, carl_sagan_path(s.user) %></p>
            <% end %>
        </div>
    <% end %>

<% else %>
    <p>Your search returned no results.</p>
    <% end %>
</div>

1 Ответ

2 голосов
/ 03 марта 2012

This

Sunspot.search (Foo, Bar) {with (: about, 'a');facet (: name)}

переводит в Solr следующее:

INFO: [] webapp=/solr path=/select params={facet=true&start=0&q=*:*&f.name_s.facet.mincount=1&facet.field=name_s&wt=ruby&fq=type:(Foo+OR+Bar)&fq=about_s:a&rows=30} hits=1 status=0 QTime=1 

Точный запрос Solr можно найти в solr/log/ solr_production.log файле

, есливы замечаете, что facet(:name) переведено на f. name_s .facet и not f.foo.facet and f.bar.facet.. Поэтому оно не сработало так, как вы ожидали.

Следующее будет работать, но для этого нужносоздать 3 фиктивных метода в каждой модели.Идея в том, что вам нужна отдельная грань для каждого из типов.

Sunspot.search Foo, Bar, CarlSagan do
  keywords q
  #provide faceting for "search class", a field representing a pretty version of the model name
  facet(:foo)
  facet(:bar)
  facet(:carlsagan)
  with(:search_class, params[:class]) if params[:class].present?
  paginate(:page => params[:page], :per_page => 30)
end

Опять же, всегда лучше взглянуть на фактический журнал запросов SOLR, чтобы отладить проблемы поиска.Sunspot делает многие вещи волшебными, но у него есть свои ограничения; -)

...