How-to: search across linked tables using acts_as_ferret

I’m in the process of adding search to a Rails application, using acts_as_ferret and ran into this issue. How to search a table using a field from another table, supplied by a foreign key?

Let’s say you created a table for a model named Feature:

  create_table "features", :force => true do |t|
    t.integer  "feature_name_id"
    t.integer  "chromosome_id"
    t.integer  "start"
    t.integer  "end"
    t.string   "strand"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

This model, Feature, belongs_to two models, FeatureName and Chromosome, each with a field “name”:

  create_table "feature_names", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "chromosomes", :force => true do |t|
    t.string   "name"
    t.integer  "length"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

You want to search for features using the associated feature_name or chromosome_name.

Here’s the trick. Edit app/models/feature.rb to look something like this (just showing the relevant parts here):

class Feature < ActiveRecord::Base
  acts_as_ferret :fields => [:fname,:cname]
  belongs_to :feature_name
  belongs_to :chromosome

  # ferret indexing on foreign keys
  def fname
    "#{self.feature_name.name}"
  end

  def cname
    "#{self.chromosome.name}"
  end

end

That tells acts_as_ferret to index whatever is returned by the methods fname and cname. Which is: the name field from the FeatureName and Chromosome models, respectively.

For testing, I use a small rake task to pre-build indices. Run it with “rake ferret:build”.

namespace :ferret do
  # Rebuild index task.
    desc "Rebuild a Ferret index."
      task :build => :environment do
        [Feature,MyOtherModel1,MyOtherModel2].each do |model|
            model.rebuild_index
        end
  end
end

If search isn’t returning results following your changes, don’t forget to restart the server.