Rails Sunspot gem:使用多模型全站搜索的构面

发布于 2024-12-06 08:41:36 字数 4311 浏览 0 评论 0原文

我正在尝试通过强大的 Sunspot gem for Rails 实现全站点搜索。这涉及同时搜索多个截然不同的模型。我想做的是使用分面功能来允许用户过滤每个模型上的搜索结果,或者默认情况下在同一页面上查看所有结果,并按 :boost 限定符排序彼此散布。将 Sunspot Railscast 中的分面代码与另一个中的多模型搜索代码相结合Stackoverflow 问题 (来自 Sunspot 文档中“多类型”代码的变体)给了我一个我认为可行的解决方案,但事实并非如此。

多方法搜索成功,但分面始终为空。我的基本方法是在每个模型上提供一个具有相同名称的虚拟属性:search_class,这只是呈现为字符串的模型的类名。然后我尝试将其用作一个方面。但是,在视图逻辑中,facet (@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>

I'm trying to implement a sitewide search through the powerful Sunspot gem for Rails. This involves a search across multiple, very different models at once. What I WANT to do is use the faceting feature to allow the user to filter their search results on each model, or by default view all on the same page, interspersed with each other ordered by the :boost qualifier. Combining the faceting code from the Sunspot Railscast with the multiple model searching code from another Stackoverflow question (a variation upon the 'Multiple Type' code from the Sunspot documentation) gave me a solution that I'd think would work, but doesn't.

The multiple method search succeeds, but the facets always turn up null. My basic approach is to provide a virtual attribute on each model by the same name, :search_class, that is just the model's class name rendered into a string. I then try and use that as a facet. However, in the view logic the results of the facet (@search.facet(:search_class).rows) is always an empty array, including when @search.results returns many different models in the same query, despite each returned instance having a perfectly accessible Instance.search_class attribute.

I'm using Rails 3.1.0 and sunspot-rails 1.2.1.

What should I do to make this faceting code work?

Controller:

#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

Models:

#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

View:

#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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

○闲身 2024-12-13 08:41:36

Sunspot.search(Foo, Bar){with(:about, 'a');面(:名称)}

在 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/log/solr_Production.log 文件中找到确切的 Solr 查询

如果您注意到 facet(:name) 翻译为 f.name_s.facet 和 not f.foo.facet 和 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 查询日志来调试搜索问题。太阳黑子使许多事情变得神奇,但它也有其局限性;-)

This

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

translates to the following in 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 

You can find the exact Solr query in the solr/log/ solr_production.log file

if you notice facet(:name) translated to f.name_s.facet and not f.foo.facet and f.bar.facet. That is why it did not work as you expected.

The following will work but that needs one to create 3 dummy methods in each model. The idea is you need a separate facet line for each of the types.

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

Again, it is always better to look at the actual SOLR query log to debug the search issues. Sunspot makes many things magical but it has its limitations ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文