Class: Dbviewer::QueryAnalyzer

Inherits:
Object
  • Object
show all
Defined in:
lib/dbviewer/query_analyzer.rb

Overview

QueryAnalyzer handles analysis of query patterns and statistics

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection) ⇒ QueryAnalyzer

Initialize the analyzer for instance methods

Parameters:

  • connection (ActiveRecord::ConnectionAdapters::AbstractAdapter)

    Database connection



22
23
24
25
# File 'lib/dbviewer/query_analyzer.rb', line 22

def initialize(connection)
  @connection = connection
  @adapter_name = connection.adapter_name.downcase
end

Instance Attribute Details

#adapter_nameObject (readonly)

Instance methods for query analysis



18
19
20
# File 'lib/dbviewer/query_analyzer.rb', line 18

def adapter_name
  @adapter_name
end

#connectionObject (readonly)

Instance methods for query analysis



18
19
20
# File 'lib/dbviewer/query_analyzer.rb', line 18

def connection
  @connection
end

Class Method Details

.detect_potential_n_plus_1(queries) ⇒ Object

Detect potential N+1 query patterns



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/dbviewer/query_analyzer.rb', line 63

def self.detect_potential_n_plus_1(queries)
  potential_issues = []

  # Group queries by request_id
  queries.group_by { |q| q[:request_id] }.each do |request_id, request_queries|
    # Skip if there are too few queries to indicate a problem
    next if request_queries.size < 5

    # Look for repeated patterns within this request
    analyze_request_patterns(request_id, request_queries, potential_issues)
  end

  # Sort by number of repetitions (most problematic first)
  potential_issues.sort_by { |issue| -issue[:count] }
end

.generate_stats(queries) ⇒ Object

Calculate statistics for a collection of queries



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/dbviewer/query_analyzer.rb', line 5

def self.generate_stats(queries)
  {
    total_count: queries.size,
    total_duration_ms: queries.sum { |q| q[:duration_ms] },
    avg_duration_ms: calculate_average_duration(queries),
    max_duration_ms: queries.map { |q| q[:duration_ms] }.max || 0,
    tables_queried: extract_queried_tables(queries),
    potential_n_plus_1: detect_potential_n_plus_1(queries),
    slowest_queries: get_slowest_queries(queries)
  }.merge(calculate_request_stats(queries))
end

.get_slowest_queries(queries, limit: 5) ⇒ Object

Get the slowest queries from the dataset



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/dbviewer/query_analyzer.rb', line 80

def self.get_slowest_queries(queries, limit: 5)
  # Return top N slowest queries with relevant info
  queries.sort_by { |q| -q[:duration_ms] }
    .first(limit)
    .map do |q|
      {
        sql: q[:sql],
        duration_ms: q[:duration_ms],
        timestamp: q[:timestamp],
        request_id: q[:request_id],
        name: q[:name]
      }
    end
end

Instance Method Details

#analyze_query(table_name, query_params) ⇒ Hash

Analyze a query and provide performance statistics and recommendations

Parameters:

  • table_name (String)

    Name of the table

  • query_params (TableQueryParams)

    Query parameters with filters

Returns:

  • (Hash)

    Analysis results with statistics and recommendations



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/dbviewer/query_analyzer.rb', line 43

def analyze_query(table_name, query_params)
  results = {
    table: table_name,
    filters: query_params.column_filters.keys,
    analysis: [],
    recommendations: []
  }

  # Check created_at filter performance
  if query_params.column_filters["created_at"].present?
    analyze_timestamp_query(table_name, "created_at", results)
  end

  # Add general performance recommendations based on database type
  add_database_specific_recommendations(results)

  results
end

#has_index_on?(table_name, column_name) ⇒ Boolean

Check if a table has an index on a column

Parameters:

  • table_name (String)

    Name of the table

  • column_name (String)

    Name of the column

Returns:

  • (Boolean)

    True if column has an index, false otherwise



31
32
33
34
35
36
37
# File 'lib/dbviewer/query_analyzer.rb', line 31

def has_index_on?(table_name, column_name)
  indexes = connection.indexes(table_name)
  indexes.any? do |index|
    index.columns.include?(column_name) ||
    (index.columns.length == 1 && index.columns[0] == column_name)
  end
end