Agile Ajax

Making acts_as_ferret, STI, and DRb Work Together

Ferret by hyperhaus

If you are using ferret to index a base class which has subclasses using STI, you may have seen the following error once you start using the DRb server which ferret uses for production mode:
undefined method `[]' for #<DRb::DRbUnknown:0x2501bd4>

A quick google on this error will reveal a simple fix; however, this fix might not work for you (it didn't for me). My problem was a result of using the :models option when invoking #find_ids_with_ferret. Fortunately, there is a relatively simple workaround to make ferret play nicely in this scenario.

Let's assume the following hierarchy:

class Animal < ActiveRecord::Base
  self.inheritance_column = 'species_type'
end
class Dog < Animal; end
class Cat < Animal; end
class Smurf < Animal; end

The first thing you need to do is include a special DRb class that will ensure the DRb server knows of the class (this is the fix which google will give you when you search for the error). This prevents the DRbUnknown problem is most cases.

class Animal < ActiveRecord::Base
  include DRb::DRbUndumped
end

Ferret will now have no problem when you pass classes in the :models option that have entities indexed for them. You will see a problem, however, if you pass a class that does not yet have anything indexed for it (oddly enough, #find_with_ferret will still work).

There are a number of ways around this. You could index each subclass separately which ensures the server is aware of the class (but then you lose the speed benefit of searching a single index), you could have dummy entries indexed for each class (causes clutter and is hacky), or you could stop passing :models and instead alter your query to filter on your inheritance_column. I choose the final option, as it seemed to get the job done without sacrificing too much.

The first thing you'll need to do is to have ferret index the inheritance_column. Notice in this example how :remote => true is set. This is another setting you must have in order for DRb to work properly with ferret (in addition to including the DRb::DRbUndumped module).

class Animal < ActiveRecord::Base
  include DRb::DRbUndumped
  acts_as_ferret(:remote => true,
    :fields => {
      :species_type => {:store => :yes, :index => :untokenized}
    }
  )
end

After this, the only thing left is to modify the query. If you choose, you could use Ferret::Search::TermQuery to help build your query. For this example, I just append text in a static way (the same mechanism is possible to build a dynamic query which filters on type).

class Animal < ActiveRecord::Base
  def find_ids_of_dogs_and_cats(query)
    find_ids_with_ferret(query + ' AND (species_type:(dog OR cat))')
  end
end

Since the remote index is no longer dealing with the :models option, you will get the correct results back (if you're curious why, take a look at the ferret code). However, when an instance of that class does eventually get indexed, the DRb::DRbUndumped will ensure that you don't get those DRb::DRbUnknown errors anymore. This will solve issues on new systems which might not have all types of content in them yet.

Leave a comment

Powered by WP Hashcash

About Pathfinder

  • We design and build extraordinary applications for companies looking to make the next great idea a reality.
  • learn more

Topics

WordPress

Comments about this site: info@pathf.com