Awais
Awais

Reputation: 1821

Search result ordering by precedence in rails

I am searching against title and description of Products. it works cool but all i need a search like to get a result precedence based. like in result products with title match comes first then description. Here is my controller code:

def products
    scope = Product.joins(:vendor).enabled.includes(:vendor, :images)

    # Make sure to select only available vendors
    scope = scope.where("vendors.enabled = ?", true)

    # Filter by vendor
    if params[:vendor_id].present?
      scope = scope.where(vendor_id: params[:vendor_id])
    end

    if params[:search].present?
      scope = scope.filter(params[:search])
    end
    scope
  end

and here is model. product.rb

def self.filter(search)
    where("title ILIKE '%#{search}%' OR description ILIKE '%#{search}%'")
  end

Upvotes: 1

Views: 418

Answers (1)

glenatron
glenatron

Reputation: 11372

So the outcome of this will be that you have a set of Product objects that match the search in one of their fields.

The missing part is that now you need to sort your results- you can do this as part of your query but it's relatively hard work and I would be inclined to do the work in Ruby.

I would start by generating a result score based on the number of occurrences of the search terms in your result, something like this:

def score_result( result, search_term )
   score = result.title =~ search_term ? 5 : 0
   score+= result.text =~ search_term ? 1: 0
end

Then you could apply that as a filter on your search:

if params[:search].present?
  searchPattern = Regexp.new(params[:search])
  scope = scope.filter(params[:search]).sort_by { | res | score_result(res, searchPattern) }
end

That should sort results where the term appears in both first, then ones where it appears only in the title, then ones where it appears only in the text.

A couple of notes of caution:

  1. This is a very naive interpretation - what happens if your search term is top hats and tails - do you only want to match that exact phrase? Should you be split it up and then assign a score per word? If you do that, do you want to return results where the title or text contains and as a search result? How do you weight them? If one word matches multiple times should that score as highly as different words matching once each?
  2. This is a fairly common problem and in the Ruby world that means that someone has probably solved it already in fact there are probably many people who have solved it. It is always worth searching around to see whether there are any convenient gems you can drop into your bundle that will allow you to use someone else's code to solve your problems - in this case something like SearchLogic looks as though it could help.

Upvotes: 1

Related Questions