-
Notifications
You must be signed in to change notification settings - Fork 40
TypoWithFerret
acts_as_ferret makes integrating Ferret into Typo really easy.
Typo uses Rails single table inheritance for storing articles, pages and comments. They’re all stored in a single table named contents
. By letting acts_as_ferret work on the top level Content
class, we can run searches across all content types.
Add the following to app/model/content.rb:
acts_as_ferret( :fields => { 'title' => { :boost => 3 }, 'body' => { :boost => 2 }, 'keywords' => { :boost => 2 }, 'extended' => { :boost => 2 }, 'author' => { :boost => 1 }, 'email' => { :boost => 1 }, 'url' => { :boost => 1 }, 'name' => { :boost => 1 } }, :store_class_name => true )
def self.ferret_find(query) find_by_contents(query).map { |content| if content.is_a? Comment content.article elsif content.published? content end }.flatten.uniq rescue Exception => e [] end
The ferret_find
method is what we’ll use in the controller.
The Live Search is done by LiveController, where the the original search
method is replaced with something like this:
app/controller/live_controller.rb
def search @search = params[:q] @results = Content.ferret_find(@search) unless @search.to_s.blank? @headers[[Content-Type]] = "text/html; charset=utf-8" end
The non-Javascript search is done by ArticlesController :
app/controller/articles_controller.rb
def search @search = params[:q] @results = Content.ferret_find(params[:q]) unless @search.to_s.blank? end
As find_by_ferret returns Articles and Pages as well, it might not be suitable to display all the search results along with their contents on the results page. Instead, I’d suggest a search results page like this:
app/views/articles/search.rhtml
<div class="post"> <h2>Searched for <em>"<%=h @search %>"</em></h2> <% if @results -%> <ul> <% for content in @results -%> <li><%= result_link content %> - <%= js_distance_of_time_in_words_to_now content.created_at %></li> <% end -%> </ul> <% end -%> </div>
Don’t forget to modify the template for the live search, too:
app/views/live/search.rhtml
<% unless @search.to_s.blank? -%> <h3>Searched for <em>"<%=h @search %>"</em></h3> <% if @results -%> <ul> <% for content in @results -%> <li><%= result_link content %> - <%= js_distance_of_time_in_words_to_now content.created_at %></li> <% end -%> </ul> <% end -%> <% else %> <% end -%>
We still need to define that handy result_link
function, which builds the correct
links to Pages and Articles:
app/helpers/application_helper.rb
def result_link(content) return article_link content.title, content if content.is_a? Article return page_link content.title, content if content.is_a? Page end
Fire up your server and do a search. As there is no Ferret index yet, it will be built once the first search is run. The index location will be
RAILS_ROOT/index/RAILS_ENV/content
and has to be writable by the user your server is running as.
Current Versions of Ferret (that is, from 0.9 upwards) make some use of the System’s
locale settings to find out about the language that text to be indexed is in (I’m not sure if this is the real reason, please correct this if I’m wrong). On most servers the system locale is unset (and therefore has the default value ‘C’), which is something Ferret doesn’t like. I usually set
ENV['LANG'] = 'de_DE.UTF-8' ENV['LC_TIME'] = 'C'
in config/environment.rb. You should replace the de_DE with your locale of choice, of course. The explicit setting of LC_TIME to ‘C’ is done because otherwise I’d have german month names in the Posted at strings, which the JavaScript conversion of dates to e.g. 10 days ago isn’t able to handle.