У меня есть поле, в котором я использую JQuery TokenInput для автозаполнения продукта. Чтобы найти продукты, которые я делаю:

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

Если у меня есть продукт с именем Rice и я выполняю поиск по полю, набирая «r», «ri» и «ric», я хочу, чтобы ближайший результат был тем, который соответствует запросу. Однако вместо этого, если у меня в базе данных более 10 различных типов риса, результаты будут "не по порядку", а Rice окажется на 6-м или 9-м месте из 10 обнаруженных рисов. Для меня рис должен быть первым, а затем рисовый шар или торт, но вместо этого я получаю что-то вроде:

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

Даже если я наберу именно «Рис», я получу нечто похожее на приведенное выше. Я хочу, чтобы это был первый вариант, если набирается r, ri, ric or rice, но мои результаты не верят в порядок, они хотят только хаоса. Как это сделать?

3
user2784630 16 Мар 2014 в 14:36

4 ответа

Лучший ответ

Вы можете добавить в свою модель Product следующий метод:

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

Здесь я использую оператор соответствия (= ~), который возвращает позицию, в которой совпало регулярное выражение. Чем раньше встретится «рис», тем ближе будет строка результирующего массива к вершине. Этот метод может не подходить для больших массивов, но для 10 элементов это нормально.

А вот и ваш контроллер:

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

Удачи!

2
user2422869 16 Мар 2014 в 15:55

Полнотекстовый поиск

Мы сделали что-то вроде этого - вы ищете полный текст поиск

Мы сделали нечто похожее на то, что вы ищете на http://firststopcosmeticshop.co.uk - мы сделали это так:

#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]

#app/controllers/products_controller.rb
def search      
    @products = Product.search(params[:search])
    respond_to do |format|
        format.js   { render :partial => "elements/livesearch", :locals => {:search => @products, :query => params[:search]} }
        format.html { render :index }
    end
end

#app/models/product.rb
def self.search(search)
 basic_search(name: search, description: search).take(5) #-> uses textacular gem
end

Он использует текстовый гем для PGSQL, но, надеюсь, дает вам некоторое представление о том, как загрузить, выполнить поиск


Код

Для вас я бы порекомендовал выполнить ваш поиск так:

def token
   @products = Product.where(name: "%#{params[:q]}%").order(name: :desc)
   respond_to do |format|
     format.html
     format.json { render json: @products.to_json }
   end
end

Хотя это не даст прямого ответа на ваш вопрос, я надеюсь, что это даст вам некоторую информацию об эффективности и т. Д.

2
Richard Peck 16 Мар 2014 в 16:24

Я решил свой поиск с помощью этого запроса, и я думаю, что вы тоже можете его использовать, и результаты поиска будут намного лучше. Учитывая строку query в качестве входных данных, я использовал следующий sql:

select items.*
from items
where lower(items.name) like 'query%' or items.name like '% query%'
order by case when lower(items.name) like 'query%' then 1 else 2 end,
items.name ASC

Это вернет все элементы, у которых есть хотя бы одна работа, соответствующая заказанному результату с теми, которые начинают совпадение первыми.

В Rails это будет примерно так:

Item.where("lower(items.name) like '#{downcase_query}%' or 
            lower(items.name) like '% #{downcase_query}%'").
     order("case when lower(items.name) like '#{downcase_query}%' then 1 else 2 end, 
            items.name ASC")
2
coorasse 15 Апр 2016 в 21:36

В общем, ключ к сортировке чего-либо в Ruby заключается в использовании .sort или .sort_by.

Например, вот такой пример:

% w {банан, яблоко, груша, рис} .sort_by {| word | word.length} # => ["инжир", "груша", "яблоко", "банан"]

Теперь в вашем примере вам нужно указать, какой критерий вы хотите использовать.

Учитывая, что вы хотите сначала вернуть Rice, а затем Rice Ball, я бы предложил добавить метод сортировки, который предпочтительно сначала возвращает Rice, а затем работает с остальными результатами.

0
shevy 16 Мар 2014 в 16:04