summaryrefslogtreecommitdiff
path: root/lib/gitlab/search/query.rb
blob: 8583bce77921d9d5e897538d61764dad5f8a56d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
module Gitlab
  module Search
    class Query < SimpleDelegator
      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)
        filter = { parser: @filter_options[:default_parser], name: name }.merge(attributes)

        @filters << filter
      end

      def filter_options(**options)
        @filter_options.merge!(options)
      end

      def extract_filters
        fragments = []

        filters = @filters.each_with_object([]) do |filter, parsed_filters|
          match = @raw_query.split.find { |part| part =~ /\A#{filter[:name]}:/ }
          next unless match

          input = match.split(':')[1..-1].join
          next if input.empty?

          filter[:value] = parse_filter(filter, input)
          filter[:regex_value] = Regexp.escape(filter[:value]).gsub('\*', '.*?')
          fragments << match

          parsed_filters << filter
        end

        query = (@raw_query.split - fragments).join(' ')

        [query, filters]
      end

      def parse_filter(filter, input)
        filter[:parser].call(input)
      end
    end
  end
end