|
|
|
|
@ -42,21 +42,43 @@ module SearchEntries
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def build_tsquery(keyword)
|
|
|
|
|
def build_tsquery(keyword, with_or_vector: false)
|
|
|
|
|
keyword = keyword.delete("\0")
|
|
|
|
|
|
|
|
|
|
if keyword.match?(/\d/) && !keyword.match?(/\p{L}/)
|
|
|
|
|
number = keyword.gsub(/\D/, '')
|
|
|
|
|
|
|
|
|
|
["tsvector @@ ((quote_literal(?) || ':*')::tsquery || (quote_literal(?) || ':*')::tsquery || plainto_tsquery(?))",
|
|
|
|
|
number, number.length > 1 ? number.delete_prefix('0') : number, keyword]
|
|
|
|
|
sql =
|
|
|
|
|
if number.length <= 2
|
|
|
|
|
<<~SQL.squish
|
|
|
|
|
ngram @@ (quote_literal(?)::tsquery || quote_literal(?)::tsquery) OR tsvector @@ plainto_tsquery(?)
|
|
|
|
|
SQL
|
|
|
|
|
else
|
|
|
|
|
<<~SQL.squish
|
|
|
|
|
tsvector @@ ((quote_literal(?) || ':*')::tsquery || (quote_literal(?) || ':*')::tsquery || plainto_tsquery(?))
|
|
|
|
|
SQL
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
[sql, number, number.length > 1 ? number.delete_prefix('0') : number, keyword]
|
|
|
|
|
elsif keyword.match?(/[^\p{L}\d&@._\-+]/) || keyword.match?(/\A['"].*['"]\z/)
|
|
|
|
|
['tsvector @@ plainto_tsquery(?)', TextUtils.transliterate(keyword.downcase)]
|
|
|
|
|
else
|
|
|
|
|
[
|
|
|
|
|
"tsvector @@ (quote_literal(coalesce((ts_lexize('english_stem', :keyword))[1], :keyword)) || ':*')::tsquery",
|
|
|
|
|
{ keyword: TextUtils.transliterate(keyword.downcase).squish }
|
|
|
|
|
]
|
|
|
|
|
keyword = TextUtils.transliterate(keyword.downcase).squish
|
|
|
|
|
|
|
|
|
|
sql =
|
|
|
|
|
if keyword.length <= 2
|
|
|
|
|
arel = Arel.sql(<<~SQL.squish)
|
|
|
|
|
ngram @@ quote_literal(:keyword)::tsquery
|
|
|
|
|
SQL
|
|
|
|
|
|
|
|
|
|
arel = Arel::Nodes::Or.new([arel, Arel.sql('tsvector @@ plainto_tsquery(:keyword)')]).to_sql if with_or_vector
|
|
|
|
|
|
|
|
|
|
arel
|
|
|
|
|
else
|
|
|
|
|
"tsvector @@ (quote_literal(coalesce((ts_lexize('english_stem', :keyword))[1], :keyword)) || ':*')::tsquery"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
[sql, { keyword: }]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@ -78,25 +100,51 @@ module SearchEntries
|
|
|
|
|
["tsvector @@ (#{query.to_sql})", terms.index_by.with_index { |_, index| :"term#{index}" }.merge(weight:)]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def build_weights_wildcard_tsquery(keyword, weight)
|
|
|
|
|
keyword = TextUtils.transliterate(keyword.downcase).squish
|
|
|
|
|
|
|
|
|
|
sql =
|
|
|
|
|
if keyword.length <= 2
|
|
|
|
|
<<~SQL.squish
|
|
|
|
|
ngram @@ (quote_literal(:keyword) || ':' || :weight)::tsquery
|
|
|
|
|
SQL
|
|
|
|
|
else
|
|
|
|
|
<<~SQL.squish
|
|
|
|
|
tsvector @@ (quote_literal(coalesce((ts_lexize('english_stem', :keyword))[1], :keyword)) || ':*' || :weight)::tsquery
|
|
|
|
|
SQL
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
[sql, { keyword:, weight: }]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def index_submitter(submitter)
|
|
|
|
|
return if submitter.email.blank? && submitter.phone.blank? && submitter.name.blank?
|
|
|
|
|
|
|
|
|
|
email_phone_name = [
|
|
|
|
|
[submitter.email.to_s, submitter.email.to_s.split('@').last].join(' ').delete("\0"),
|
|
|
|
|
[submitter.phone.to_s.gsub(/\D/, ''),
|
|
|
|
|
submitter.phone.to_s.gsub(PhoneCodes::REGEXP, '').gsub(/\D/, '')].uniq.join(' ').delete("\0"),
|
|
|
|
|
TextUtils.transliterate(submitter.name).delete("\0")
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
sql = SearchEntry.sanitize_sql_array(
|
|
|
|
|
[
|
|
|
|
|
"SELECT setweight(to_tsvector(?), 'A') || setweight(to_tsvector(?), 'B') ||
|
|
|
|
|
setweight(to_tsvector(?), 'C') || setweight(to_tsvector(?), 'D')".squish,
|
|
|
|
|
[submitter.email.to_s, submitter.email.to_s.split('@').last].join(' ').downcase.delete("\0"),
|
|
|
|
|
[submitter.phone.to_s.gsub(/\D/, ''),
|
|
|
|
|
submitter.phone.to_s.gsub(PhoneCodes::REGEXP, '').gsub(/\D/, '')].uniq.join(' ').delete("\0"),
|
|
|
|
|
TextUtils.transliterate(submitter.name.to_s.downcase).delete("\0"),
|
|
|
|
|
build_submitter_values_string(submitter)
|
|
|
|
|
setweight(to_tsvector(?), 'C') || setweight(to_tsvector(?), 'D') as tsvector,
|
|
|
|
|
setweight(to_tsvector('simple', ?), 'A') ||
|
|
|
|
|
setweight(to_tsvector('simple', ?), 'B') ||
|
|
|
|
|
setweight(to_tsvector('simple', ?), 'C') as ngram".squish,
|
|
|
|
|
*email_phone_name,
|
|
|
|
|
build_submitter_values_string(submitter),
|
|
|
|
|
*email_phone_name
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entry = submitter.search_entry || submitter.build_search_entry
|
|
|
|
|
|
|
|
|
|
entry.account_id = submitter.account_id
|
|
|
|
|
entry.tsvector = SearchEntry.connection.select_value(sql)
|
|
|
|
|
entry.tsvector, ngram = SearchEntry.connection.select_rows(sql).first
|
|
|
|
|
entry.ngram = build_ngram(ngram)
|
|
|
|
|
|
|
|
|
|
return if entry.tsvector.blank?
|
|
|
|
|
|
|
|
|
|
@ -122,13 +170,15 @@ module SearchEntries
|
|
|
|
|
|
|
|
|
|
def index_template(template)
|
|
|
|
|
sql = SearchEntry.sanitize_sql_array(
|
|
|
|
|
['SELECT to_tsvector(?)', TextUtils.transliterate(template.name.to_s.downcase).delete("\0")]
|
|
|
|
|
["SELECT to_tsvector(:text), to_tsvector('simple', :text)",
|
|
|
|
|
{ text: TextUtils.transliterate(template.name.to_s.downcase).delete("\0") }]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entry = template.search_entry || template.build_search_entry
|
|
|
|
|
|
|
|
|
|
entry.account_id = template.account_id
|
|
|
|
|
entry.tsvector = SearchEntry.connection.select_value(sql)
|
|
|
|
|
entry.tsvector, ngram = SearchEntry.connection.select_rows(sql).first
|
|
|
|
|
entry.ngram = build_ngram(ngram)
|
|
|
|
|
|
|
|
|
|
return if entry.tsvector.blank?
|
|
|
|
|
|
|
|
|
|
@ -145,13 +195,15 @@ module SearchEntries
|
|
|
|
|
return if submission.name.blank?
|
|
|
|
|
|
|
|
|
|
sql = SearchEntry.sanitize_sql_array(
|
|
|
|
|
['SELECT to_tsvector(?)', TextUtils.transliterate(submission.name.to_s.downcase).delete("\0")]
|
|
|
|
|
["SELECT to_tsvector(:text), to_tsvector('simple', :text)",
|
|
|
|
|
{ text: TextUtils.transliterate(submission.name.to_s.downcase).delete("\0") }]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
entry = submission.search_entry || submission.build_search_entry
|
|
|
|
|
|
|
|
|
|
entry.account_id = submission.account_id
|
|
|
|
|
entry.tsvector = SearchEntry.connection.select_value(sql)
|
|
|
|
|
entry.tsvector, ngram = SearchEntry.connection.select_rows(sql).first
|
|
|
|
|
entry.ngram = build_ngram(ngram)
|
|
|
|
|
|
|
|
|
|
return if entry.tsvector.blank?
|
|
|
|
|
|
|
|
|
|
@ -163,4 +215,14 @@ module SearchEntries
|
|
|
|
|
|
|
|
|
|
retry
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def build_ngram(ngram)
|
|
|
|
|
ngrams =
|
|
|
|
|
ngram.split(/\s(?=')/).each_with_object([]) do |item, acc|
|
|
|
|
|
acc << item.sub(/'(.*?)':/) { "'#{Regexp.last_match(1).first(2)}':" }
|
|
|
|
|
acc << item.sub(/'(.*?)':/) { "'#{Regexp.last_match(1).first(1)}':" }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ngrams.uniq { |e| e.sub(/':[\d,]/, "':1") }.join(' ')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|