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
|