debian-mirror-gitlab/lib/gitlab/search/query.rb

79 lines
2.2 KiB
Ruby
Raw Normal View History

2019-02-15 15:39:39 +05:30
# frozen_string_literal: true
2018-11-08 19:23:39 +05:30
module Gitlab
module Search
class Query < SimpleDelegator
2019-02-15 15:39:39 +05:30
include EncodingHelper
2021-03-11 19:13:27 +05:30
QUOTES_REGEXP = %r{\A"|"\Z}.freeze
TOKEN_WITH_QUOTES_REGEXP = %r{\s(?=(?:[^"]|"[^"]*")*$)}.freeze
2018-11-08 19:23:39 +05:30
def initialize(query, filter_opts = {}, &block)
@raw_query = query.dup
@filters = []
@filter_options = { default_parser: :downcase.to_proc }.merge(filter_opts)
self.instance_eval(&block) if block_given?
@query = Gitlab::Search::ParsedQuery.new(*extract_filters)
# set the ParsedQuery as our default delegator thanks to SimpleDelegator
super(@query)
end
private
def filter(name, **attributes)
2020-10-24 23:57:45 +05:30
filter = {
parser: @filter_options[:default_parser],
name: name
}.merge(attributes)
2018-11-08 19:23:39 +05:30
@filters << filter
end
def filter_options(**options)
@filter_options.merge!(options)
end
def extract_filters
fragments = []
2021-03-11 19:13:27 +05:30
query_tokens = parse_raw_query
2018-11-08 19:23:39 +05:30
filters = @filters.each_with_object([]) do |filter, parsed_filters|
2021-03-11 19:13:27 +05:30
match = query_tokens.find { |part| part =~ /\A-?#{filter[:name]}:/ }
2018-11-08 19:23:39 +05:30
next unless match
2022-01-26 12:08:38 +05:30
input = match.split(':')[1..].join
2018-11-08 19:23:39 +05:30
next if input.empty?
2020-10-24 23:57:45 +05:30
filter[:negated] = match.start_with?("-")
2021-03-11 19:13:27 +05:30
filter[:value] = parse_filter(filter, input.gsub(QUOTES_REGEXP, ''))
2018-11-08 19:23:39 +05:30
filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
fragments << match
parsed_filters << filter
end
2021-03-11 19:13:27 +05:30
query = (query_tokens - fragments).join(' ')
2021-02-22 17:27:13 +05:30
query = '*' if query.empty?
2018-11-08 19:23:39 +05:30
[query, filters]
end
def parse_filter(filter, input)
2019-02-15 15:39:39 +05:30
result = filter[:parser].call(input)
@filter_options[:encode_binary] ? encode_binary(result) : result
2018-11-08 19:23:39 +05:30
end
2021-03-11 19:13:27 +05:30
def parse_raw_query
# Positive lookahead for any non-quote char or even number of quotes
# for example '"search term" path:"foo bar.txt"' would break into
# ["search term", "path:\"foo bar.txt\""]
@raw_query.split(TOKEN_WITH_QUOTES_REGEXP).reject(&:empty?)
end
2018-11-08 19:23:39 +05:30
end
end
end