ThinkingSphinx gives Ruby-on-Rails developers the power of a full-text search engine, with only one downside; it’s a full-text search engine. Recently I had to develop an Advanced Search facility for a site which required me to locate content within certain indexed fields; TS can do this, but it does require a little more configuration than normal.
The Basics
If you’ve never used ThinkingSphinx before, make yourself comfortable, and then visit http://freelancing-god.github.com/ts/en/ for the basics of using ThinkingSphinx. The video at RailsCasts is also excellent; http://railscasts.com/episodes/120-thinking-sphinx
The Problem
The usual usage of ThinkingSphinx is to simply give it the search parameters.
@images = Image.search ferarri
This query will return an array containing each image object which has the word ferarri in it. Often, this is sufficient, but consider a model index structure such as
define_index do indexes :name, :as => :asset_name indexes description, :as => :desc indexes alt_tag, :as => :alttag indexes credit indexes image_type.name, :as => :image_type indexes keywords.name, :as => :keywords set_property :delta => :datetime, :threshold => 1.hour end
There may be occasions where we want to search for the word Ferarri in the name field, but we don’t want to see records where the word ferrari just occured in the description. To do this, we need to place Thinking Sphinx into Extended Mode and restrict the location of the search
@images = Image.search @asset_name => ferrari, :match_mode => :extended
If we want to specify multiple search terms across several fields, we add them into the search string seperated by commas;
@images = Image.search @asset_name => ferrari, @alttag => Peter, :match_mode => :extended
To specify a string, enclose it in quotes;
@images = Image.search @asset_name => ferrari, @alttag => 'Peter Connolly', :match_mode => :extended
That’s fine for hard coded searches, but how do we build this into an advanced search form? Here’s some code snippets that may help Ruby on Rails developers (no – this is not a step-by-step record of how to build advanced search forms, just a series of hints)
Model
For Thinking Sphinx to work, it needs to know which content is to be indexed. We do this by adding a define_index section to the appropriate model (image.rb, in this case)
define_index do indexes :name, :as => :asset_name indexes description, :as => :desc indexes alt_tag, :as => :alttag indexes credit indexes image_type.name, :as => :image_type indexes keywords.name, :as => :keywords set_property :delta => :datetime, :threshold => 1.hour end
View
The view will need pagination; for this, we use the will_paginate plugin (walkthrough at http://railscasts.com/episodes/51-will-paginate).
<%# Display the pagination %> <%= will_paginate @images, :inner_window => 1, :outer_window => 0, :params => { 'img_type' => @img_type, 'hidden_id' => @hidden_id, 'search' => @search_terms, 'search_title' => @search_title, 'search_desc' => @search_desc, 'search_alttag' => @search_alttag, 'search_credit' => @search_credit, 'search_imagetype' => @search_imagetype, 'search_keywords' => @search_keywords } %> <%# Display the correct set of results %> <div id="image_library_images"> <%= render :partial => 'search_image', :collection => @images, :as => :image %> </div>
The Advanced form itself uses a standard Rails form
<div id="advancedsearch" style="display:none;"> <p><strong>Advanced Image Search options</strong></p> <table> <tr><td> <%= label_tag "Title" %> </td><td> <%= text_field_tag :search_title %> </td></tr><tr><td> <%= label_tag "Caption" %> </td><td> <%= text_field_tag :search_desc %> </td></tr><tr><td> <%= label_tag "Alt Tag" %> </td><td> <%= text_field_tag :search_alttag %> </td></tr><tr><td> <%= label_tag "Credit" %> </td><td> <%= text_field_tag :search_credit %> </td></tr><tr><td> <%= label_tag "Image Type" %> </td><td> <%= text_field_tag :search_keywords %> </td></tr> </table> </div>
Controller
The controller is where all the heavy lifting occurs for this search function.
def advanced_search @hidden_id = params[:hidden_id] ? params[:hidden_id] : params[:content_type] + '_asset_attributes_primary_image_id' @switch_img = params[:img_id] ? params[:img_id] : :primary_image; @search_terms = params[:search] @search_title = params[:search_title] @search_desc = params[:search_desc] @search_alttag = params[:search_alttag] @search_credit = params[:search_credit] @search_keywords = params[:search_keywords] @search = [] @advsearch = "" if (@search_title.present?) @advsearch = ' @asset_name => ' + @search_title end if (@search_desc.present?) searchdesc = " @desc => " + @search_desc @advsearch += @advsearch.present? ? ", " + searchdesc : searchdesc end if (@search_alttag.present?) searchalttag = " @alttag => " + @search_alttag @advsearch += @advsearch.present? ? ", " + searchalttag : searchalttag end if (@search_credit.present?) searchcredit = " @credit => " + @search_credit @advsearch += @advsearch.present? ? ", " + searchcredit : searchcredit end if (@search_keywords.present?) searchkeywords = " @keywords => " + @search_keywords @advsearch += @advsearch.present? ? ", " + searchkeywords : searchkeywords end @images = Image.search @search_terms + @advsearch, :match_mode => :extended, :page => params[:page], :ignore_errors => false, :per_page => 10 render :partial => 'image_search_results', :layout => false end
Indexing Sphinx
This is the mistake that we often make.. After editing data in your database, you will need to reindex Sphinx; otherwise your new data will not be shown the search results.Use the following command to rebuild sphinx from the linux command prompt;
rake ts:rebuild
Again, please note that the code above is a series of hints, *not* a final, fully fledged solution. Use it as you will, but it’s supplied with no warranty or guarantees. Have fun!