findex 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|