miércoles, 19 de diciembre de 2007

multimodel search with acts_as_ferret

Hi everyone!,

First of all, I want to introduce myself to you people. I'm Marcelo Giorgi an Uruguayan developer who has a strong background in web technologies (mainly using J2EE and PHP), but lately I found myself very excited studiyng Ruby and RoR, why? I just can't explaing it, it was love at first sight ^_^.

I want to share with you how you can use acts_as_ferret plugin, for Ruby On Rails, with multiple models (http://projects.jkraemer.net/acts_as_ferret/), such an excellent search engine. I had to research a bit to keep things up and running, hopefully it could help someone...

Install acts_as_ferret

The installation can be accomplished by executing the following line on the rails project:

script/plugin install svn://projects.jkraemer.net/acts_as_ferret/tags/stable/acts_as_ferret

And that's it, now we can start messing around with it!

Use single model for a query

Before showing some complex query, I'll be showing the basic usage by example. Suppose you want to make a search through an Article model. To make this model searcheable we got to invoke the acts_as_ferret helper within the class definition, as shown:

article.rb
class Article < style="font-style: italic;"> ..
acts_as_ferret
..
end

Now, we simple need to define a new controller that make use of the new search feature, we named this controller as SearchEngineController:

search_engine_controller.rb
class SearchEngineController < collection =" Article.find_with_ferret params[:q], :page => params[:page]
end
end

Finally, we just need to modify the view to show the results:

app/views/search_engine_controller/index.rhtml
...

As you might noticed, we used will_paginate helper, that enables us paginate the results provided by acts_as_ferret.

Use many models for a query

Until this point, all seems very straighforwad (didn't it?). The documentation of find_with_ferret indicates that we simply code :store_class_name => :true to enable search over multiple models. Then, we only have to use find_with_ferret with the appropiate paramters to indacate in which models we want to search, this can be done with this:

search_engine_controller.rb
class SearchEngineController < style="font-style: italic;"> def index
@collection = Article.find_with_ferret params[:q], :page => params[:page], :multi => [NewsItem]
end
end

This code mandates that the search must be perfomed through the Article and NewsItem's models. Everything seems right, but...

But, when we run it, we get the following error:


RuntimeError in Search resultController#index
':store_class_name => true' required for multi_search to work

The problem is that multisearh requires us, to create the index over the models we will use in the search. This can be accomplished with a rake task. As the below code suggest.

lib/task/ferret.rake
namespace :ferret do
desc "Rebuild all Indexes"
task :rebuild_all_indexes => [:environment] do
%w(Article NewsItem).each { |s| s.constantize.rebuild_index }
end
end
Then, we just got to run the task 'rebuild_all_indexes' before we start the server and everthing should work just fine.