49 lines
1.3 KiB
Ruby
49 lines
1.3 KiB
Ruby
|
require 'active_record'
|
||
|
|
||
|
class Carto::VisualizationQuerySearcher
|
||
|
|
||
|
PATTERN_ESCAPE_CHARS = ['_', '%'].freeze
|
||
|
|
||
|
def initialize(query)
|
||
|
@query = query
|
||
|
end
|
||
|
|
||
|
def search(tainted_search_pattern)
|
||
|
search_pattern = escape_characters_from_pattern(tainted_search_pattern)
|
||
|
@query.where(partial_match_sql, search_pattern, "%#{search_pattern}%")
|
||
|
.order("#{rank_sql(search_pattern)} DESC, visualizations.updated_at DESC")
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def escape_characters_from_pattern(pattern)
|
||
|
pattern.chars.map { |c| PATTERN_ESCAPE_CHARS.include?(c) ? "\\" + c : c }.join
|
||
|
end
|
||
|
|
||
|
def tsvector
|
||
|
%{
|
||
|
setweight(to_tsvector('english', coalesce("visualizations"."name",'')), 'A') ||
|
||
|
setweight(to_tsvector('english', coalesce(array_to_string(visualizations.tags, ''),'')), 'B') ||
|
||
|
setweight(to_tsvector('english', coalesce("visualizations"."description",'')), 'C')
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def rank_sql(search_pattern)
|
||
|
%{
|
||
|
ts_rank(
|
||
|
#{tsvector},
|
||
|
plainto_tsquery('english', #{ActiveRecord::Base.sanitize(search_pattern)})
|
||
|
)
|
||
|
}.squish
|
||
|
end
|
||
|
|
||
|
def partial_match_sql
|
||
|
%{
|
||
|
#{tsvector} @@ plainto_tsquery('english', ?)
|
||
|
OR CONCAT("visualizations"."name", array_to_string(visualizations.tags, ''), "visualizations"."description")
|
||
|
ILIKE ?
|
||
|
}.squish
|
||
|
end
|
||
|
|
||
|
end
|