Class: RailsPulse::Query

Inherits:
ApplicationRecord show all
Includes:
Taggable
Defined in:
app/models/rails_pulse/query.rb

Constant Summary

Constants included from Taggable

Taggable::MAX_TAG_LENGTH, Taggable::TAG_NAME_REGEX

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Taggable

#add_tag, #has_tag?, #remove_tag, #tag_list, #tag_list=

Class Method Details

.bulk_find_or_create(norm_map) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'app/models/rails_pulse/query.rb', line 27

def self.bulk_find_or_create(norm_map)
  return {} if norm_map.empty?

  id_map = where(hashed_sql: norm_map.keys).pluck(:hashed_sql, :id).to_h

  missing = norm_map.reject { |h, _| id_map.key?(h) }
  unless missing.empty?
    now = Time.current
    insert_all(missing.map { |h, n| { hashed_sql: h, normalized_sql: n, created_at: now, updated_at: now } })
    id_map.merge!(where(hashed_sql: missing.keys).pluck(:hashed_sql, :id).to_h)
  end

  id_map
end

.ransackable_associations(auth_object = nil) ⇒ Object



46
47
48
# File 'app/models/rails_pulse/query.rb', line 46

def self.ransackable_associations(auth_object = nil)
  %w[operations]
end

.ransackable_attributes(auth_object = nil) ⇒ Object



42
43
44
# File 'app/models/rails_pulse/query.rb', line 42

def self.ransackable_attributes(auth_object = nil)
  %w[id normalized_sql average_query_time_ms execution_count total_time_consumed performance_status occurred_at]
end

Instance Method Details

#analysis_statusObject



127
128
129
130
131
# File 'app/models/rails_pulse/query.rb', line 127

def analysis_status
  return "not_analyzed" unless analyzed?
  return "needs_update" if needs_reanalysis?
  "current"
end

#analyzed?Boolean

Analysis helper methods

Returns:

  • (Boolean)


109
110
111
# File 'app/models/rails_pulse/query.rb', line 109

def analyzed?
  analyzed_at.present?
end

#critical_issues_countObject



139
140
141
# File 'app/models/rails_pulse/query.rb', line 139

def critical_issues_count
  issues_by_severity["critical"]&.count || 0
end

#ensure_analyzed!Object



100
101
102
103
104
105
106
# File 'app/models/rails_pulse/query.rb', line 100

def ensure_analyzed!
  return if analyzed?
  QueryAnalysisService.analyze_query(id)
  reload
rescue => e
  RailsPulse.logger.warn("[Query] Auto-analysis failed for query #{id}: #{e.message}")
end

#generate_hashed_sqlObject



155
156
157
158
# File 'app/models/rails_pulse/query.rb', line 155

def generate_hashed_sql
  require "digest"
  self.hashed_sql = Digest::MD5.hexdigest(normalized_sql)
end

#has_recent_operations?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'app/models/rails_pulse/query.rb', line 113

def has_recent_operations?
  operations.where("occurred_at > ?", 48.hours.ago).exists?
end

#issues_by_severityObject



133
134
135
136
137
# File 'app/models/rails_pulse/query.rb', line 133

def issues_by_severity
  return {} unless analyzed? && issues.present?

  issues.group_by { |issue| issue["severity"] || "unknown" }
end

#n_plus_one_groups(ops) ⇒ Object



93
94
95
96
97
98
# File 'app/models/rails_pulse/query.rb', line 93

def n_plus_one_groups(ops)
  ops
    .reject { |op| op.repeated_query_group.nil? }
    .group_by(&:repeated_query_group)
    .transform_values { |group| group.map(&:repetition_count).compact.max }
end

#needs_reanalysis?Boolean

Returns:

  • (Boolean)


117
118
119
120
121
122
123
124
125
# File 'app/models/rails_pulse/query.rb', line 117

def needs_reanalysis?
  return true unless analyzed?

  # Check if there are new operations since analysis
  last_operation_time = operations.maximum(:occurred_at)
  return false unless last_operation_time

  last_operation_time > analyzed_at
end

#recent_operationsObject



85
86
87
88
89
90
91
# File 'app/models/rails_pulse/query.rb', line 85

def recent_operations
  operations
    .where("occurred_at > ?", 30.days.ago)
    .order(occurred_at: :desc)
    .limit(500)
    .to_a
end

#to_breadcrumbObject



147
148
149
# File 'app/models/rails_pulse/query.rb', line 147

def to_breadcrumb
  normalized_sql.to_s.truncate(60)
end

#to_sObject



151
152
153
# File 'app/models/rails_pulse/query.rb', line 151

def to_s
  id
end

#warning_issues_countObject



143
144
145
# File 'app/models/rails_pulse/query.rb', line 143

def warning_issues_count
  issues_by_severity["warning"]&.count || 0
end