Rails: Database Search using ‘acts_as_ferret’

Do you need the capability to run full text based searches on your database? The Ruby on Rails plugin ‘acts_as_ferret’ will enable your application to do this.

‘acts_as_ferret’ is a Rails port to Ferret, the Ruby port of Lucene. It is an ActiveRecord mixin which adds full text searching capabilities to any Rails model.

ferret


It also caters for wildcard character searches and will find similarly spelt or miss-spelt words. To find out more technical information read below…

Key functionalities

  • Index of models on disk
  • Search multiple models (database tables)
  • Support for Rails single table inheritance (children will become searchable if the parent ‘acts_as_ferret’)
  • Index attributes or virtual attributes of a model
  • Indexing can be customized by overriding the to doc method
  • Find similar items (‘more like this’)

Setting up

  1. Install ferret:
    • gem install ferret
  2. Install acts_as_ferret plugin:
    • ruby script/plugin install svn://projects.jkraemer.net/acts_as_ferret/tags/stable/acts_as_ferret

Key findings

Main methods:

find_id_by_contents(query, options = {})

  • Returns the ids of matching results
find_by_contents(query, options = {}, find_options = {})
  • A wrapper to find_id_by_contents that filters the returned result set by additional conditions which can be specified in find_options.
Setting up custom fields to be searched:
class Member < ActiveRecord::Base
acts_as_ferret :fields => [:first_name, :last_name, :mother_name]

def mother_name
self.mother[:name]
end
end


* Note
(not clearly specified in the official documentation):

  • find_options takes conditions as a string:
  • eg. :conditions => “name = ‘john’”

Because the filtering process only apply the conditions to the returned result set AFTER the results have been returned (via calling find_id_by_contents) and NOT DURING the process, specifying a limit in options to find matching contents will force the search to look for matches within the limit number of records. In other words, the limit specified will be the number of records SEARCHED, not RETURNED.

Scenarios for searching across multiple models

  1. Multiple unrelated models
    • A simple solution was proposed that searches multiple unrelated models and have the results compiled in the following way [1]:
FERRETABLE_MODELS = [‘Advert’, ‘Content’]
results = []

FERRETABLE_MODELS.each do |klass|
k = Kernel.const_get klass
k.find_id_by_contents(query).each do |m|
results.push {
:score => m[:score],
:o bject => k.find(match[:id])
}
end
end

results = results.sort{ |a,b| b[:score] <=> a[:score] }
  1. Multiple related models
    • Models: Book, Author, Distributor
    • This will return objects belonging to any of the models searched.Book.multi_search("Pragmatic Press”, [ Author, Distributor ])

  1. Multiple related models, single object type
    • Models: Book, Author, Distributor Book.multi_search("Pragmatic Press”, [ Author, Distributor ])
    • If you only want Book objects to be returned, the solution proposed by duncanbeevers [4] extends acts_as_ferret’s to_doc method by adding the following code to it (in the search model):# Descend child elements
      self.class.reflect_on_all_associations.collect { |association|
      self.send(association.instance_values[\”name\”]).collect { |child|
      child.to_doc.all_fields.collect { |field|
      doc << field
      } if child.class.respond_to?(\”acts_as_ferret\”)
      }
      }
      return doc
    • Alternatively, one can use “setting up custom fields to be searched” mentioned in section 3 to perform search across attributes multiple models.

References:

  1. http://infovore.org/archives/2006/09/22/cross-model-searching-in-rails-with-ferret/
  1. http://www.railsenvy.com/2007/2/19/acts-as-ferret-tutorial
    A very easy to follow tutorial worthy to take a look.

Share this article: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • NewsVine
  • Slashdot
  • StumbleUpon
  • Technorati

1 Response to “Rails: Database Search using ‘acts_as_ferret’”


  1. Gravatar Icon 1 Peter Flynn Aug 10th, 2007 at 3:53 pm

    Have you guys looked at Solr as an alternative search/solution?
    http://blog.tourb.us/archives/searching-with-solr/

Leave a Reply