ruby-on-railsrubyruby-on-rails-3ruby-on-rails-4jquery-tokeninput

How to order results by closest match to query?


I have a field where I'm using Jquery TokenInput to autocomplete a Product. To find the products I'm doing:

def token
   @products = Product.order(:name)
   respond_to do |format|
     format.html
     format.json { render json: @products.where("name ILIKE ?", "%#{params[:q]}%").limit(10).order('name DESC') }
   end
end

If I have a product named Rice and do a search on the field, typing in "r", "ri", and "ric", I want the closest result to be the one that matches the query. Instead though, if I have over 10 different types of Rices in the database, the results will be "out of order" with Rice being placed around 6th or 9th place out of 10 founded rices. To me, Rice should come first and than Rice Ball or Cake but instead I get something like:

Brown Rice # First result in Tokeninput
Rice Cake
Yellow Rice
Long Grain Rice
Rice Ball
Chinese Rice
Super Rice
Rice
New Rice
The Super Rice Game

Even if I type in "Rice" exactly I get something similar to the above. I want it to be the 1st choice if r, ri, ric or rice is typed in but my results don't believe in order, they only want chaos. How would this be done?


Solution

  • You may add the following method to your Product model:

    def self.good_search(criteria)
      regexp = /#{criteria}/i; # case-insensitive regexp based on your string
    
      result = order(:name).where("name ILIKE ?", "%#{criteria}%").limit(10)
      result.sort{|x, y| (x =~ regexp) <=> (y =~ regexp) } 
    end
    

    Here I use the match operator (=~) which returns position where regexp matched. The earlier the "rice" met the closer to the top of result array string'll be. This method might not be good for big arrays, but for 10 elements it's okay.

    And here's your controller:

    def token
      respond_to do |format|
        format.html
        format.json { render json: Product.good_search(params[:q]) }
      end
    end
    

    Good luck!