Ferret Pagination in Rails
Gregg Pollack put up a great guide to showcase the power of act_as_ferret plugin earlier today. On its own, Ferret is a Ruby port of the Lucene search engine library which offers fast full-text search with a number of great freebies and addons. Gregg did an excellent job of showcasing some of these features, so I won't go into the details here. Instead, I want to show you how to integrate paginating_find with Ferret. It's a great combination which I've been using at Graphics World for quite a while.
Extending paginating_find
If you haven't already, read 'Faster Pagination in Rails' to get up to speed with paginating_find and windowed pagination. Now, to integrate Ferret, we need to add the following two ClassMethods to paginating_find.rb (copy/paste):
def paginating_ferret_search(options) count = find_by_contents(options[:q], {:lazy => true}).total_hits PagingEnumerator.new(options[:page_size], count, false, options[:current], 1) do |page| offset = (options[:current].to_i - 1) * options[:page_size] limit = options[:page_size] res = find_by_contents(options[:q], {:offset => offset, :limit => limit}) end end def paginating_ferret_multi_search(options) count = multi_search(options[:q], [List], {:limit => :all, :lazy => true}).total_hits PagingEnumerator.new(options[:page_size], count, false, options[:current], 1) do |page| offset = (options[:current].to_i - 1) * options[:page_size] limit = options[:page_size] multi_search(options[:q], [List], {:offset => offset, :limit => limit}) end end
You will notice that params[:q] refers to the search query, feel free to change it as you wish. More importantly, you will notice that my multi_search method asks Ferret to search for the 'List' model in addition to the default model (Tutorial). In my application, this value was hard-coded right into the plugin, you may want to generate this value dynamically.
Extending your controller
Now that our search query is wrapped in a PagingEnumerator, all we have to do is define a new search method in one of our controllers. Here is a sample from my own controller for Graphics World:
def search unless params[:q].blank? params[:q] = sanitize(params[:q]) params[:p] = 1 unless params[:p] case params[:t] when 'tutorials' @collection = Tutorial.paginating_ferret_search({:q => params[:q], :page_size => 10, :current => params[:p]}) when 'all' @collection = Tutorial.paginating_ferret_multi_search({:q => params[:q], :page_size => 10, :current => params[:p]}) end else flash[:warning] = "Please provide a search query" redirect_to home_url end end
Voila, with a little work you should have a nice-looking, full-text, paginated search component:

About this entry
- Published:
- 20.02.07 / 9pm
- Category:
- Ruby on Rails
Top Posts
Related Posts
- 10.09 Faster Pagination in Rails
- 22.09 Eager Find by SQL Pagination in Rails
- 17.09 Using Helpers instead of Components
- 07.11 Monit makes Mongrel play nice!
- 31.07 Reconstructing Request URIs in Rails









108 Comments
comments rss | trackback uri