Ferret DRB and sorting

Ferret, the Lucene port to ruby now comes with a DRB server that allows concurrent access. For sites that are running more than one mongrel, it is a lifesaver. If you want to sort the results, it doesn't work. I'll show you how to fix it inside. Update: The easier thing is just to use :sort=>"field desc" which is built in!

We do development with local ferret indexes and then deploy to an integration server that runs he DRB server. After adding sorting, we started seeing errors that looked like:

 DRb::DRbServerNotFound
 /usr/lib/ruby/1.8/drb/drb.rb:1650:in `current_server'

After several hours of restarting the DRB server, I finally figured out that the problem comes from the :sort parameter. Ferret::Search::Sort and Ferret::Search::SortField are both implemented in C, and as such don't marshall well via DRB. You can use :sort=>:field, but that doesn't let you reverse the direction of the sort.

Since I didn't want to go through re-implementing the Sort and SortField classes in ruby, I took the easy way out. I modified the find_id_by_contents method in the local_index to use a modified sort parameter. The new code in local_index.rb looks like:

   # Queries the Ferret index to retrieve model class, id, score and the
    # values of any fields stored in the index for each hit.
    # If a block is given, these are yielded and the number of total hits is
    # returned. Otherwise [total_hits, result_array] is returned.
    def find_id_by_contents(query, options = {})
      if  (sort=options[:sort])
        sort = [sort] unless sort.is_a?(Array)
        sort_fields = sort.map do |field|
          term,direction = field.split(/\s+/)
          direction ||= "asc"
          Ferret::Search::SortField.new(term,:reverse=>direction.match(/desc/i))
        end
        options[:sort]=Ferret::Search::Sort.new(sort_fields)
      end
     ...

By adding just a few lines of code, I was able to change the way parameters are passed. Instead of passing in a SortField object, I can now pass in "title desc" to get a reverse sort. This simple fix works for both local and remote indexes and is slightly easier to use from the programmers perspective than creating Ferret specific objects.

Posted by Mike Mangino on Tuesday, June 26, 2007