summaryrefslogtreecommitdiff
path: root/lib/gitlab/usage/metrics/names_suggestions/generator.rb
blob: 33f025770e0c2c9f7ebc0637912d32943478bb14 (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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# frozen_string_literal: true

module Gitlab
  module Usage
    module Metrics
      module NamesSuggestions
        class Generator < ::Gitlab::UsageData
          FREE_TEXT_METRIC_NAME = "<please fill metric name>"

          class << self
            def generate(key_path)
              uncached_data.deep_stringify_keys.dig(*key_path.split('.'))
            end

            private

            def count(relation, column = nil, batch: true, batch_size: nil, start: nil, finish: nil)
              name_suggestion(column: column, relation: relation, prefix: 'count')
            end

            def distinct_count(relation, column = nil, batch: true, batch_size: nil, start: nil, finish: nil)
              name_suggestion(column: column, relation: relation, prefix: 'count_distinct', distinct: :distinct)
            end

            def redis_usage_counter
              FREE_TEXT_METRIC_NAME
            end

            def alt_usage_data(*)
              FREE_TEXT_METRIC_NAME
            end

            def redis_usage_data_totals(counter)
              counter.fallback_totals.transform_values { |_| FREE_TEXT_METRIC_NAME}
            end

            def sum(relation, column, *rest)
              name_suggestion(column: column, relation: relation, prefix: 'sum')
            end

            def estimate_batch_distinct_count(relation, column = nil, *rest)
              name_suggestion(column: column, relation: relation, prefix: 'estimate_distinct_count')
            end

            def add(*args)
              "add_#{args.join('_and_')}"
            end

            def name_suggestion(relation:, column: nil, prefix: nil, distinct: nil)
              parts = [prefix]

              if column
                parts << parse_target(column)
                parts << 'from'
              end

              source = parse_source(relation)
              constraints = parse_constraints(relation: relation, column: column, distinct: distinct)

              if constraints.include?(source)
                parts << "<adjective describing: '#{constraints}'>"
              end

              parts << source
              parts.compact.join('_')
            end

            def parse_constraints(relation:, column: nil, distinct: nil)
              connection = relation.connection
              ::Gitlab::Usage::Metrics::NamesSuggestions::RelationParsers::Constraints
                .new(connection)
                .accept(arel(relation: relation, column: column, distinct: distinct), collector(connection))
                .value
            end

            def parse_target(column)
              if column.is_a?(Arel::Attribute)
                "#{column.relation.name}.#{column.name}"
              else
                column
              end
            end

            def parse_source(relation)
              relation.table_name
            end

            def collector(connection)
              Arel::Collectors::SubstituteBinds.new(connection, Arel::Collectors::SQLString.new)
            end

            def arel(relation:, column: nil, distinct: nil)
              column ||= relation.primary_key

              if column.is_a?(Arel::Attribute)
                relation.select(column.count(distinct)).arel
              else
                relation.select(relation.all.table[column].count(distinct)).arel
              end
            end
          end
        end
      end
    end
  end
end