findex 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +1 -1
- data/README.markdown +6 -3
- data/VERSION +1 -1
- data/lib/findex/tasks.rb +73 -52
- metadata +1 -1
data/MIT-LICENSE
CHANGED
data/README.markdown
CHANGED
@@ -18,9 +18,12 @@ You may want to configure it as a gem in environment.rb instead, since you're go
|
|
18
18
|
|
19
19
|
rake gems:install
|
20
20
|
|
21
|
-
Now add
|
21
|
+
Now add it to your projects' Rakefile:
|
22
22
|
|
23
|
-
|
23
|
+
begin
|
24
|
+
require 'findex/tasks'
|
25
|
+
rescue MissingSourceFile
|
26
|
+
end
|
24
27
|
|
25
28
|
## Find Missing Indexes ##
|
26
29
|
|
@@ -50,4 +53,4 @@ First, get some instructions:
|
|
50
53
|
|
51
54
|
Read the instructions above and start finding missing indexes! Thanks to Matt Janowski for the inspiration (http://robots.thoughtbot.com/post/163627511/a-grand-piano-for-your-violin) and Thoughtbot / Jon Yurek for the core of the indexes detection code!
|
52
55
|
|
53
|
-
Copyright (c)
|
56
|
+
Copyright (c) 2010 Flip Sasser, released under the MIT license.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/findex/tasks.rb
CHANGED
@@ -5,14 +5,15 @@ require 'rake/tasklib'
|
|
5
5
|
namespace :db do
|
6
6
|
desc 'Finds indexes your application probably needs'
|
7
7
|
task :indexes => [:environment, :prepare] do
|
8
|
-
indices =
|
9
|
-
|
8
|
+
indices = @findex.get_indices(:geo, [:name, [:id, :type]], :primary, :reflection, [:type, [:boolean, :date, :datetime, :time]])
|
9
|
+
@findex.send_indices(indices)
|
10
10
|
end
|
11
11
|
|
12
12
|
task :prepare do
|
13
|
-
@
|
14
|
-
@
|
15
|
-
@
|
13
|
+
@findex = Findex.new
|
14
|
+
@findex.generate_migration = ENV['migration'] == 'true'
|
15
|
+
@findex.perform_index = ENV['perform'] == 'true'
|
16
|
+
@findex.tables = ENV['tables'] ? ENV['tables'].split(',').map(&:strip) : nil
|
16
17
|
Dir[File.join(Rails.root, 'app', 'models', '*.rb')].each do |file|
|
17
18
|
load(file)
|
18
19
|
end
|
@@ -21,20 +22,20 @@ namespace :db do
|
|
21
22
|
namespace :indexes do
|
22
23
|
desc 'Finds unindexed boolean columns'
|
23
24
|
task :boolean => [:environment, :prepare] do
|
24
|
-
@migration_name = 'boolean'
|
25
|
-
|
25
|
+
@findex.migration_name = 'boolean'
|
26
|
+
@findex.send_indices(@findex.get_indices([:type, [:boolean]]))
|
26
27
|
end
|
27
28
|
|
28
29
|
desc 'Finds unindexed date, time, and datetime columns'
|
29
30
|
task :datetime => [:environment, :prepare] do
|
30
|
-
@migration_name = 'datetime'
|
31
|
-
|
31
|
+
@findex.migration_name = 'datetime'
|
32
|
+
@findex.send_indices(@findex.get_indices([:type, [:date, :datetime, :time]]))
|
32
33
|
end
|
33
34
|
|
34
35
|
desc 'Finds unindexed geo columns'
|
35
36
|
task :geo => [:environment, :prepare] do
|
36
|
-
@migration_name = 'geo'
|
37
|
-
|
37
|
+
@findex.migration_name = 'geo'
|
38
|
+
@findex.send_indices(@findex.get_indices(:geo))
|
38
39
|
end
|
39
40
|
|
40
41
|
desc 'Prints instructions on how to use rake:db:indexes'
|
@@ -48,7 +49,7 @@ namespace :db do
|
|
48
49
|
puts ' `rake db:indexes migration=true`'
|
49
50
|
puts ''
|
50
51
|
puts ' You can also target specific column types, like so:'
|
51
|
-
|
52
|
+
[:boolean, :datetime, :geo, :primary, :relationships].each do |type|
|
52
53
|
puts " `rake db:indexes:#{type}`"
|
53
54
|
end
|
54
55
|
puts ''
|
@@ -61,17 +62,17 @@ namespace :db do
|
|
61
62
|
|
62
63
|
desc 'Generates a migration file with the recommended indexes'
|
63
64
|
task :migration => :environment do
|
64
|
-
@generate_migration = true
|
65
|
-
@perform_index = false
|
66
|
-
indices =
|
67
|
-
|
65
|
+
@findex.generate_migration = true
|
66
|
+
@findex.perform_index = false
|
67
|
+
indices = @findex.get_indices(:geo, [:name, [:id, :type]], :primary, :reflection, [:type, [:boolean, :date, :datetime, :time]])
|
68
|
+
@findex.send_indices(indices)
|
68
69
|
end
|
69
70
|
|
70
71
|
desc 'Finds unindexed columns matching the names you supply'
|
71
72
|
task :names => [:environment, :prepare] do
|
72
73
|
if ENV['names']
|
73
|
-
indices =
|
74
|
-
|
74
|
+
indices = @findex.get_indices([:name, ENV['names'].split(',').map(&:strip).map(&:intern)])
|
75
|
+
@findex.send_indices(indices)
|
75
76
|
else
|
76
77
|
puts ''
|
77
78
|
puts ' You must pass in a comma-separated collection of names like so'
|
@@ -82,29 +83,29 @@ namespace :db do
|
|
82
83
|
|
83
84
|
desc 'Performs a migration with the recommended indexes'
|
84
85
|
task :perform => :environment do
|
85
|
-
@generate_migration = false
|
86
|
-
@perform_index = true
|
87
|
-
indices =
|
88
|
-
|
86
|
+
@findex.generate_migration = false
|
87
|
+
@findex.perform_index = true
|
88
|
+
indices = @findex.get_indices(:geo, [:name, [:id, :type]], :primary, :reflection, [:type, [:boolean, :date, :datetime, :time]])
|
89
|
+
@findex.send_indices(indices)
|
89
90
|
end
|
90
91
|
|
91
92
|
desc 'Finds unindexed primary keys'
|
92
93
|
task :primary => [:environment, :prepare] do
|
93
|
-
@migration_name = 'primary'
|
94
|
-
|
94
|
+
@findex.migration_name = 'primary'
|
95
|
+
@findex.send_indices(@findex.get_indices(:primary))
|
95
96
|
end
|
96
97
|
|
97
98
|
desc 'Finds unindexed relationship foreign keys'
|
98
99
|
task :relationships => [:environment, :prepare] do
|
99
|
-
@migration_name = 'relationship'
|
100
|
-
|
100
|
+
@findex.migration_name = 'relationship'
|
101
|
+
@findex.send_indices(@findex.get_indices(:reflection))
|
101
102
|
end
|
102
103
|
|
103
104
|
desc 'Finds unindexed columns matching the types you supply'
|
104
105
|
task :types => [:environment, :prepare] do
|
105
106
|
if ENV['types']
|
106
|
-
indices =
|
107
|
-
|
107
|
+
indices = @findex.get_indices([:type, ENV['types'].split(',').map(&:strip).map(&:intern)])
|
108
|
+
@findex.send_indices(indices)
|
108
109
|
else
|
109
110
|
puts ''
|
110
111
|
puts ' You must pass in a comma-separated collection of types like so'
|
@@ -116,13 +117,13 @@ namespace :db do
|
|
116
117
|
end
|
117
118
|
end
|
118
119
|
|
119
|
-
|
120
|
-
def
|
120
|
+
class Findex
|
121
|
+
def check_index(*args)
|
121
122
|
index = args.shift
|
122
123
|
!args.any?{|array| array.any?{|comparison_index| comparison_index == index}}
|
123
124
|
end
|
124
125
|
|
125
|
-
def
|
126
|
+
def collect_indices(indices)
|
126
127
|
indices.collect{|table, columns| [table, columns.sort{|a, b|
|
127
128
|
if a == :id
|
128
129
|
-1
|
@@ -134,11 +135,11 @@ module Findex
|
|
134
135
|
}]}.sort{|a, b| a[0].to_s <=> b[0].to_s}
|
135
136
|
end
|
136
137
|
|
137
|
-
def
|
138
|
+
def connection
|
138
139
|
@connection ||= ActiveRecord::Base.connection
|
139
140
|
end
|
140
141
|
|
141
|
-
def
|
142
|
+
def get_indices(*args)
|
142
143
|
indices = {}
|
143
144
|
ObjectSpace.each_object(Class) do |model|
|
144
145
|
next unless model.ancestors.include?(ActiveRecord::Base) && model != ActiveRecord::Base && model.table_exists?
|
@@ -151,7 +152,7 @@ module Findex
|
|
151
152
|
collect_indices(indices)
|
152
153
|
end
|
153
154
|
|
154
|
-
def
|
155
|
+
def get_model_geo_indices(model, indices, existing_indices)
|
155
156
|
indices[model.table_name] ||= []
|
156
157
|
parse_columns(model) do |column, column_name|
|
157
158
|
if column.type == :decimal && column.name =~ /(lat|lng)/ && model.column_names.include?(alternate_column_name = column.name.gsub(/(^|_)(lat|lng)($|_)/) { "#{$1}#{$2 == 'lat' ? 'lng' : 'lat'}#{$3}"})
|
@@ -162,7 +163,7 @@ module Findex
|
|
162
163
|
indices
|
163
164
|
end
|
164
165
|
|
165
|
-
def
|
166
|
+
def get_model_name_indices(model, names, indices, existing_indices)
|
166
167
|
indices[model.table_name] ||= []
|
167
168
|
parse_columns(model) do |column, column_name|
|
168
169
|
if names.include?(column_name) && check_index(column_name, indices[model.table_name], existing_indices)
|
@@ -172,7 +173,7 @@ module Findex
|
|
172
173
|
indices
|
173
174
|
end
|
174
175
|
|
175
|
-
def
|
176
|
+
def get_model_primary_indices(model, indices, existing_indices)
|
176
177
|
indices[model.table_name] ||= []
|
177
178
|
parse_columns(model) do |column, column_name|
|
178
179
|
if column.primary && check_index(column_name, indices[model.table_name], existing_indices)
|
@@ -182,9 +183,9 @@ module Findex
|
|
182
183
|
indices
|
183
184
|
end
|
184
185
|
|
185
|
-
def
|
186
|
+
def get_model_reflection_indices(model, indices, existing_indices)
|
186
187
|
indices[model.table_name] ||= []
|
187
|
-
|
188
|
+
model.reflections.each do |name, reflection|
|
188
189
|
case reflection.macro.to_sym
|
189
190
|
when :belongs_to
|
190
191
|
foreign_key = reflection.primary_key_name.to_sym
|
@@ -202,7 +203,7 @@ module Findex
|
|
202
203
|
indices
|
203
204
|
end
|
204
205
|
|
205
|
-
def
|
206
|
+
def get_model_type_indices(model, types, indices, existing_indices)
|
206
207
|
indices[model.table_name] ||= []
|
207
208
|
parse_columns(model) do |column, column_name|
|
208
209
|
if types.include?(column.type) && check_index(column_name, indices[model.table_name], existing_indices)
|
@@ -212,17 +213,17 @@ module Findex
|
|
212
213
|
indices
|
213
214
|
end
|
214
215
|
|
215
|
-
def
|
216
|
+
def parse_columns(model)
|
216
217
|
model.columns.each{|column| yield(column, column.name.to_sym)} if block_given?
|
217
218
|
end
|
218
219
|
|
219
|
-
def
|
220
|
+
def send_indices(indices)
|
220
221
|
if @generate_migration
|
221
222
|
require 'rails_generator'
|
222
223
|
migration_path = File.join(RAILS_ROOT, 'db', 'migrate')
|
223
224
|
migration_number = 1
|
224
225
|
migration_test = "add#{"_#{@migration_name}" if @migration_name}_indexes"
|
225
|
-
|
226
|
+
Dir[File.join(migration_path, '*.rb')].each do |file|
|
226
227
|
file = File.basename(file)
|
227
228
|
next unless file =~ /^\d+_#{migration_test}(\d+)\.rb$/
|
228
229
|
migration_number += 1
|
@@ -232,25 +233,29 @@ module Findex
|
|
232
233
|
if migration = Dir[File.join(migration_path, "*#{migration_name}.rb")].first
|
233
234
|
migration_up = []
|
234
235
|
migration_down = []
|
235
|
-
|
236
|
+
indices.sort{|a, b| a[0].to_s <=> b[0].to_s}.each do |table, columns|
|
236
237
|
next if columns.empty?
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
238
|
+
index_up = []
|
239
|
+
index_down = []
|
240
|
+
index_up.push("\s\s\s\s# Indices for `#{table}`")
|
241
|
+
index_down.push("\s\s\s\s# Remove indices for `#{table}`")
|
242
|
+
columns.each do |column|
|
243
|
+
index_up.push("\s\s\s\sadd_index :#{table}, #{column.inspect}")
|
244
|
+
index_down.push("\s\s\s\sremove_index :#{table}, #{column.inspect}")
|
242
245
|
end
|
246
|
+
migration_up.push(index_up.join("\n"))
|
247
|
+
migration_down.push(index_down.join("\n"))
|
243
248
|
end
|
244
|
-
migration_contents = File.read(migration).gsub("def self.up", "def self.up\n#{migration_up.join("\n")}").gsub("def self.down", "def self.down\n#{migration_down.join("\n")}")
|
249
|
+
migration_contents = File.read(migration).gsub("def self.up", "def self.up\n#{migration_up.join("\n\n")}").gsub("def self.down", "def self.down\n#{migration_down.join("\n\n")}")
|
245
250
|
File.open(migration, 'w+') do |file|
|
246
251
|
file.puts migration_contents
|
247
|
-
|
252
|
+
end
|
248
253
|
end
|
249
254
|
else
|
250
|
-
|
255
|
+
indices.sort{|a, b| a[0].to_s <=> b[0].to_s}.each do |table, columns|
|
251
256
|
next if columns.empty?
|
252
257
|
puts "\s\s# Indices for `#{table}`"
|
253
|
-
|
258
|
+
columns.each do |column|
|
254
259
|
if @perform_index
|
255
260
|
ActiveRecord::Migration.add_index(table, column)
|
256
261
|
else
|
@@ -261,4 +266,20 @@ module Findex
|
|
261
266
|
end
|
262
267
|
end
|
263
268
|
end
|
269
|
+
|
270
|
+
def generate_migration=(value)
|
271
|
+
@generate_migration = !!value
|
272
|
+
end
|
273
|
+
|
274
|
+
def perform_index=(value)
|
275
|
+
@perform_index = !!value
|
276
|
+
end
|
277
|
+
|
278
|
+
def tables=(tables)
|
279
|
+
@tables = tables
|
280
|
+
end
|
281
|
+
|
282
|
+
def migration_name=(value)
|
283
|
+
@migration_name = value
|
284
|
+
end
|
264
285
|
end
|