Introducing acts_as_searchable

As promised in the last article about scaling, I am hereby introducing my first publicly released Rails plugin.

Preface

Implementing fulltext searching can be an interesting endeavor. Not only from the scaling aspect, but also from the aspect of making people feel right at home with the results of their searches. This hasn’t exactly gotten easier with the flexible and easy syntax and the mostly brilliant search results provided by the big search engines like Google. This is probably one of the reasons many sites offer an integrated Google search rather than rolling their own.

But handing searches externally isn’t always feasible. Maybe you want to provide searches for non-public data, such as personal messages. Maybe you want to provide a little more flexibility in terms of filtering and cross linking. Or maybe you just don’t like to hand things to external parties.

While MySQL provides its FULLTEXT indexing and searching capabilities, it’s restricted to the MyISAM table type. If you need more performance, transactions, row-level instead of table-level-locking you’re out of luck with internal searching capabilities.

Meet acts_as_searchable.

What’s it do?

acts_as_searchable is a Rails plugin to easily integrate your ActiveRecord models with the search capabilities provided by HyperEstraier. HyperEstraier is an open source fulltext search engine with both local access and access through an HTTP interface. We’ll focus on the latter.

The plugin installs the appropriate hooks to keep the search engine index in sync with your database contents as this is the key pitfall when handing searching features to an application external to your database. You have to make sure things keep in sync.

How does it work?

HyperEstraier comes with a complete and concise pure-Ruby API to connect to its HTTP-based instances. That way you can both work with the index in terms of adding and removing records as well as perform actual search with potentially complex options.

acts_as_searchable is, in the event that you follow the conventions of yours truly, a single line to be added to the models you’d like to search. Options to customize behavior obviously exist, check the API docs for details.

Example:


class Article < ActiveRecord::Base
  acts_as_searchable
end

Simple, isn’t it?

Once this is in place, you can issue searches like these:


Article.fulltext_search("biscuits AND gravy")
Article.fulltext_search("biscuits AND gravy", :limit => 15, :offset => 14)
Article.fulltext_search("biscuits AND gravy", :attributes => "tag STRINC food")
Article.fulltext_search("biscuits AND gravy", :order => "@title STRA")

By default, the values returned by Model#fulltext_search are instantiated ActiveRecord objects that you can immediately work with in your controller or view.

acts_as_searchable takes care of removing documents from the index which are deleted via Model#destroy, adding documents to the index which have been created via Model#create or Model#new and has been optimized to only update the index if attributes have been touched that are relevant to the index. If you don’t store the value of the updated_at column in the index the plugin will not update the search index if you only touched this attribute and call Model#save.

Where do I get it?

The plugin should be available via RubyGems shortly (as soon as RubyForge distributed the release):

gem install acts_as_searchable

Follow the generic plugin instructions from the Rails Wiki for more information on how to install a Rails plugin if you don’t want to or don’t have permission to install it system-wide through the command above. Basically it boils down to put the plugin as a subdirectory into:

RAILS_ROOT/vendor/plugins/acts_as_searchable

This can be handled via svn:externals, the extracted tarball from RubyForge or a manual Subversion checkout (see below).

Other useful URLs:

Thanks!

Filed Under: Rails