Topic: HOWTO: Adding search to your rails application

Sticky:: Please read the post below. I think you will find the next method even simpler.



This is a short guide on adding search to your Rails models.
For this tutorial, I'm using a model called Product and product has the following attributes: name, description, price. If you're new to rails, what this means is that you have a database table called products, and this table has the columns name, description and price (including id of course).

Now, the first thing to do is to install the ferret gem. Without this, this tutorial is moot. Open a command prompt and type:

gem install ferret

Next, you will need to download the simple_search plugin for rails. The svn repositry is located at:

http://julik.textdriven.com/svn/tools/r … le_search/

So you can get it through Subversion using the command

svn co http://julik.textdriven.com/svn/tools/r … ple_search

If you don't have Subversion you can download it from http://subversion.tigris.org/project_packages.html. After you download simple_search, place it in the vendor/plugins directory of your rails application.

Now go to app/models and open product.rb (i.e the file for your Product model or whichever model you are using). Your model should look something like this:

class Product < ActiveRecord::Base
    indexes_columns :title, :description, :into=>'idx'
   
    before_save :make_index
    before_update :make_index
   
    def make_index
        self.idx = self.index_repr
    end   
   
end

Now there is a bit of explaining to do here. In addition to all the other columns Product has, you should also add another one called idx which has the type "TEXT" in your SQL database. This means that the columns for product should be id, name, description, price and idx (which is a TEXT column).  idx contains the searchable terms for your product. You will notice the line at the top which reads "indexes_columns :title, :description, :into => 'idx' ". Well simple_search simply takes the value of these columns and creates a searchable index which it puts into the idx column. I could well have said "indexes_columns :title, :description, :price :into => 'idx' " which will also include the price as part of our index.

I've added some filters to the product model. Before a product is saved or updated, the idx value is first determined using the 'make_index' method. This saves you the trouble of doing this in your controller each time you save or update your product.

And that is pretty much it. You can fire up script/console with "ruby script/console" for your application.
Create a new product with

Product.create(:name => 'ninja_turtle', :description => 'It barks and eats your mother', :price => 20)

Then you can now type Product.find_using_term("ninja") and your recently created product should turn up. Use find_using_term to return any products that match your given term.

Last edited by daibatzu (2006-09-20 05:10:48)

Re: HOWTO: Adding search to your rails application

Well I've found an even simpler way to implement search. This is unrelated to the iPod shuffle competition by the way (:-> hehe). Um here goes.

First you will need to download the search library by Duane Johnson and Nate McNamara. You can find it here:

http://wiki.rubyonrails.org/rails/pages/TextSearch

Copy the code listing at the very end of the website above and save it as a file called search.rb. Place the search.rb file in the lib folder of your rails application. Now lets say you have a model called Product, do the following:

#First load the search library in your lib folder
require_dependency "search"

class Product < ActiveRecord::Base
   #choose which columns to search
   searches_on :all
   #anything else your model does
end


searches_on defines what columns I want to be made searchable. I can also say

searches_on :title, :description, :list_price

to search only these columns.
Now to search my product, all I have to do is say:

Product.search("ninja turtle")

If I need to search for many different queries I should say Product.search("ninja turtle or transformer") but this didn't really work for me because I don't want my users to type strange commands. So this is the search method I'm using:

Product.search(params[:query].gsub(' or ',' ').split().join(' or ')

This works for me and should work for you too. This is much simpler than the previous method using simple search.

Last edited by daibatzu (2006-10-06 00:43:11)

Re: HOWTO: Adding search to your rails application

how did you get it off of the site? I et tons and tons of errors when using copy and paste from just checking that files syntax

Re: HOWTO: Adding search to your rails application

Mmm, not sure what happened. It was OK previously. You can use the code below but it could be come outdated. The problem I think is in the quotes and double quotes used. Something like

Last edited by daibatzu (2006-10-06 00:55:50)

Re: HOWTO: Adding search to your rails application

Ok I got an email asking how to build the search form for this. This is really quite simple and there a number of ways you can do it (including with AJAX). Anyway, here goes.

First, you'll need to add a search action to your controller. This could be like this:

def search
  if params[:query]
    @products = Product.search(params[:query])
  else
    @products = []
  end
end

Next, on any of the views you can add this bit of code. This is basically a search box.

<form action='/search'>
<input name="query" type="text" />
<input type='submit' value='Search' />
</form>

Then you create a view called search for your controller. Not the best view but you can use whatever you like and modify it to your liking. So the code below is your search.rhtml.

<% if @products.empty? %>
        <span>There were no results for your query</span><br/><br/>
<% else %>

<% for product in @products %>
Result: <% product.title %>
<% end %>

<% end %>


So, just a form which accesses the search action in your controller and sends the parameter called query to it. Quite simple.

Last edited by daibatzu (2006-10-24 08:38:10)

Re: HOWTO: Adding search to your rails application

off hand would you know how to paginate this??

Re: HOWTO: Adding search to your rails application

paginating is pretty simple. Somewhere on the rails wiki I found someone had written a paginate_collection method. Add it to application.rb

  def paginate_collection(collection, options = {})
    default_options = {:per_page => 10, :page => 1}
    options = default_options.merge options
   
    pages = Paginator.new self, collection.size, options[:per_page], options[:page]
    first = pages.current.offset
    last = [first + options[:per_page], collection.size].min
    slice = collection[first...last]
    return [pages, slice]   
  end

call it like this in the controller

def search
   if params[:query]
      product_collection = Product.search(params[:query])
   else
       product_collection = []
   end

   @product_pages, @products = paginate_collection product_collection, :page => params[:page]
end


then in the view, paginate like this (I separated this stuff into a partial so I can paginate easily)

<div class="paginate_controls">    
    <% if header %>
    Showing <%= @product_pages.current.first_item %>
       to <%= @product_pages.current.last_item %>
       of <%= @product_pages.item_count %><br/>
       <% end %>
          <%= link_to(h('< Previous'), {:page => @product_pages.current.previous})  + " | " if @product_pages.current.previous %>
   <%= pagination_links(@product_pages, :window_size => 4) %>
   <%= " | " + link_to(h('Next >'), {:page => @product_pages.current.next}) if @product_pages.current.next %>
</div>

<div class="search_results">
<% @products.each do |product|
   <%= product.name %>
<% end %>
</div>

Re: HOWTO: Adding search to your rails application

I should note that the above paginating does not work for searches, because when you go to page two the search query defaults to nothing and you lose all your search results. Haven't figured out a clean solution to that just yet.

Re: HOWTO: Adding search to your rails application

Ok, I posted a tutorial on paginating search results (with semantically-meaningful labels, yet) at http://wiki.rubyonrails.com/rails/pages … teSearches Let me know what you think.

Last edited by paron (2007-01-24 09:08:57)

Re: HOWTO: Adding search to your rails application

Great tutorial! How to search by category ? I mean if I'm developing my school library website, how I want to let the students search by by Category, Year, Author ? Like this forum search options. Thanks!

Re: HOWTO: Adding search to your rails application

In the tutorial, the model provides two methods, "search_name" and "search_all."

For what you're trying to do, I would just provide one search method -- "search_author". I suspect search is overkill for the other two conditions, "category" and "year".

There's probably no need to "search" categories; you already know what they are. It's more of a filter, where you might pass an argument like ":conditions=>["category = 'fiction']. It's the same with year; you really don't need to 'search' it -- you're just going to filter for "1987" or ">1994  AND <2004". You're not going to search in the "year" field for terms like "Einstein" AND "math", so it isn't a search, per se.

Anyway, once your model returns its array of results, you could paginate and semantically label the pages with author data, so that the results are neatly broken into pages labeled "Aaron to Byron", "Camille to Dante", etc.

If it's a multi-year search, you could label them with "Able, 1883 to Anderson, 1994" and "Astarte, 1776 to Ballinger, 1923" or the other way around, like: "1810, Pursley to 1850, Bedinger" and "1851, Sydney to 1876, Lincoln" depending on your ordering of the results.

Is that what you're asking?

Ron

Last edited by paron (2007-01-24 08:47:43)

Re: HOWTO: Adding search to your rails application

remove line numbers in vim: %s/^.\{,3}\d\{,3}\W//g

Re: HOWTO: Adding search to your rails application

RoR newb here,
In suggested search.rhtml I found it didn't display the results. Eventually I found I had to change
Result: <% product.title %>

into

Result: <%= product.title %>

Re: HOWTO: Adding search to your rails application

I hope this thread hasn't been dead and buried too long.

I am having trouble with the search.rb (the second version presented) file generating a syntax error.  The problem is identified as being line 245:

244   def self.parse_and_expr(tokens, operand)
245     if (tokens[0]  :and)
246          tokens.shift
247     end
248   # Even if :and is missing, :and is implicit
249    [:and, operand, parse_term(tokens)]
250    end

But line 253, which is similar, is identified, too:

252   def self.parse_or_expr(tokens, operand)
253      if (tokens[0]  :or)
254           tokens.shift
255           [:or, operand, parse_expr(tokens)]
256         else
257           parse_and_expr(tokens, operand)
258         end
259      end

Any help is greatly appreciated.

EDIT: It appears that every instance of ":and" and ":or" is generating a syntax error - also appear on lines 293 and 297 of search.rb, respectively.

Last edited by kingworks (2008-03-19 10:46:43)

Re: HOWTO: Adding search to your rails application

hi all i hv tried out this tutorial but getting an error

-->"undefined method `search' for #<Class:0x467cd08>"

c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:1672:in `method_missing'
app/controllers/book_controller.rb:65:in `find'

Re: HOWTO: Adding search to your rails application

hi all

i am using acts_as_ferret  for searching on my User model

acts_as_ferret :fields => [:first_name,:email,:type,:city, :telephone, :last_name1 ,:address_line_1,:state_search]

type column contain user and seller

when i search it display value only for user type not for seller

when i search name of particular user it display result but when i search name of particular seller it does show any result

@users = User.id.find_with_ferret(params[:search])

i am using this command in controller

Re: HOWTO: Adding search to your rails application

I did everything below but I get the error 'No route matches for

Re: HOWTO: Adding search to your rails application

Thanks for sharing this. I will try.