Casecommons / pg_search

pg_search builds ActiveRecord named scopes that take advantage of PostgreSQL’s full text search
http://www.casebook.net
MIT License
1.31k stars 369 forks source link

Minimum character for prefix true option #410

Open princejoseph opened 5 years ago

princejoseph commented 5 years ago

Is there a way I can specify minimum number of characters for prefix? Right now, it returns even for the first character.

class Toon < ActiveRecord::Base
  include PgSearch

  pg_search_scope :search_by_name, 
                  against: :name, 
                  tsearch: { 
                    prefix: true
                  }
end
cowboy_bebop = Toon.create!(name: 'cowboy bebop')

Toon.search_by_name 'cow' # => [cowboy_bebop]
Toon.search_by_name 'co' # => [cowboy_bebop]
Toon.search_by_name 'c' # => [cowboy_bebop]

I was thinking of something like this (see min_prefix key):

class Toon < ActiveRecord::Base
  include PgSearch

  pg_search_scope :search_by_name, 
                  against: :name, 
                  tsearch: { 
                    prefix: true, 
                    min_prefix: 3 
                  }
end

which will act like this:

Toon.search_by_name 'cow' # => [cowboy_bebop]
Toon.search_by_name 'co' # => []
Toon.search_by_name 'c' # => []
Vlagilen commented 4 years ago

I have the same problem.

first = Good.create!(title: 'iPhone 5 Battery')
second = Good.create!(title: 'iPhone 5s Battery') 
pg_search_scope :search_by_all, associated_against: {
      models: [:title],
      brands: [:title],
      part: [:title]
  }, using: {
      tsearch: { prefix: true }
  }
Good.search_by_all 'iphone 5 batt'

It's return first and second goods if prefix true. But If it were possible to set min length prefix, then the correct result would be returned

It may be possible to manually specify the tag if you need to find it using the prefix ? I used this practice when splitting a string into an array.

array = [...]
array.each do |a|
 if a.length > 3
   a = a + '*'
 end
end

Result: 'iphone 5 batt*'

Vlagilen commented 4 years ago

I'v solved this problem, it's not quite beautiful, but it works. If you run the search twice, the first time with the (prefix: true) parameter, the second time (you can also configure associations for yourself) without it.

params[:search] = 'iPhone 5 battery'

pg_search_scope :search_all, against: [:title, :tag, :barcode], associated_against: {
      models: [:title],
      brands: [:title]
  }, using: {
      tsearch: {prefix: true}
  }
pg_search_scope :search_model, associated_against: {
      models: [:title]
}

The next step in the controller is to search for the length of the word we need in the string that will fit our condition. (greater than 3 or as in my case equal to 1)

model = nil
a = params[:search].split(' ') # transform our string into an array ['iPhone', '5', 'battery']
a.each do |item|
   item.length == 1 ? model = item : nil # search our model (or length > 3 as you want)
end
search = Part.search_all(params[:search]) # First search method
!model.blank? ? search = search.search_model(model) : nil # If we find a model, we do a second search without the parameter (prefix: true)

return search