Model Specific Formatted Search Results Using Thinking Sphinx

By eric

Having recently implemented Thinking Sphinx on one of my web sites, I thought it would be cool to be able to search every indexed model. With Thinking Sphinx, it’s easy to have a bunch of different classes returned in the results. The tougher part is displaying them in a way that is organized (although admittedly not very DRY).

Let’s start out with the controller method for search results (app/controllers/search_controller.rb):

1
2
3
4
5
6
7
8
9
10
  def result
    if !params[:model].blank?
      @model = params[:model]
    else
      @model = "ThinkingSphinx"
    end
    @query = params[:search][:query]

    @results = Search.model_search(@model, @query)
  end

Also, let’s make sure we define the actual search model. I define it in a separate search library (lib/search.rb). This is the relevant snippet of code:

1
2
3
4
5
6
7
8
9
10
  def self.model_search(model, keywords, var = {})
    @search_options = { :page => var[:page] || 1,
                          :per_page => 15 }

    @search_options.merge!( :order => "@relevance DESC",
                            :sort_mode => :expr,
                            :sort_by => "@weight * @weight")

    model.constantize.search(keywords, @search_options)
  end

Assuming that we are working with the all models search, where the above model is ThinkingSphinx, let’s iterate over the search results with this code in the view (app/views/search/results.html.erb):

1
2
3
4
5
6
7
8
9
10
11
12
< % if @results.total_entries > 0 %>
<div id="localLocationList">
    <ul class="list">
    < % @results.each do |result| %>
        <li>
            < %= display_search_result(result) %>
        </li>
    < % end %>
    </ul>
    <div class="clear"></div>
</div>
< % end %>

And the view helper (app/helpers/search_helper.rb):

1
2
3
4
def display_search_result(result)
  eval "render :partial => '#{result.class.to_s.downcase.pluralize}/result',
    :locals => { :result => result }"

end

The interesting thing about this bit of code is the eval. The eval on each iteration decides which search result partial to display based on the class and then passes the result to the partial for display. So if the result has a class of Business, the partial app/views/businesses/_result.html.erb will be rendered. This is a quick example of a search result partial:

1
2
<span id="localBizName">< %= link_to result.name, business_path( result ) %></span><br />
<p class="smallOffset">< %= truncate( "#{result.description}", :length => 128 ) %></p>

This is useful because all models don’t have the same characteristics. By creating a search result partial for each model type, this can be reused for consistent looking search results around your webapp. If the search is model specific, the same result partial will be used in every iteration over the results. If the search is model agnostic, then you can display your search results in a consistent manner.

Follow My Travels

Buy My Book

Archives

  • 2019
  • 2017
  • 2014
  • 2013
  • 2012
  • 2011
  • 2010
  • 2009
  • 2008
  • 2007
  • 2006

New Posts By Email

writing