freelancing-god-thinking-sphinx 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -0
- data/lib/thinking_sphinx.rb +7 -9
- data/lib/thinking_sphinx/active_record/delta.rb +11 -3
- data/lib/thinking_sphinx/attribute.rb +30 -6
- data/lib/thinking_sphinx/configuration.rb +21 -14
- data/lib/thinking_sphinx/deltas.rb +3 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +61 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +11 -2
- data/lib/thinking_sphinx/index.rb +7 -4
- data/lib/thinking_sphinx/rails_additions.rb +12 -0
- data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +1 -1
- data/tasks/thinking_sphinx_tasks.rb +24 -12
- data/vendor/riddle/lib/riddle/controller.rb +1 -1
- metadata +3 -2
data/README
CHANGED
data/lib/thinking_sphinx.rb
CHANGED
@@ -31,7 +31,7 @@ module ThinkingSphinx
|
|
31
31
|
module Version #:nodoc:
|
32
32
|
Major = 1
|
33
33
|
Minor = 1
|
34
|
-
Tiny =
|
34
|
+
Tiny = 2
|
35
35
|
|
36
36
|
String = [Major, Minor, Tiny].join('.')
|
37
37
|
end
|
@@ -131,13 +131,11 @@ module ThinkingSphinx
|
|
131
131
|
end
|
132
132
|
|
133
133
|
def self.sphinx_running?
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
false
|
141
|
-
end
|
134
|
+
!!sphinx_pid
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.sphinx_pid
|
138
|
+
pid_file = ThinkingSphinx::Configuration.instance.pid_file
|
139
|
+
`cat #{pid_file}`[/\d+/] if File.exists?(pid_file)
|
142
140
|
end
|
143
141
|
end
|
@@ -42,7 +42,11 @@ module ThinkingSphinx
|
|
42
42
|
# if running in the test environment.
|
43
43
|
#
|
44
44
|
def index_delta(instance = nil)
|
45
|
-
|
45
|
+
delta_object.index(self, instance)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delta_object
|
49
|
+
self.sphinx_indexes.first.delta_object
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
@@ -50,14 +54,18 @@ module ThinkingSphinx
|
|
50
54
|
|
51
55
|
# Set the delta value for the model to be true.
|
52
56
|
def toggle_delta
|
53
|
-
self.class.
|
57
|
+
self.class.delta_object.toggle(self) if should_toggle_delta?
|
54
58
|
end
|
55
59
|
|
56
60
|
# Build the delta index for the related model. This won't be called
|
57
61
|
# if running in the test environment.
|
58
62
|
#
|
59
63
|
def index_delta
|
60
|
-
self.class.index_delta(self)
|
64
|
+
self.class.index_delta(self) if self.class.delta_object.toggled(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def should_toggle_delta?
|
68
|
+
!self.respond_to?(:changed?) || self.changed? || self.new_record?
|
61
69
|
end
|
62
70
|
end
|
63
71
|
end
|
@@ -175,7 +175,7 @@ module ThinkingSphinx
|
|
175
175
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
176
176
|
"UNIX_TIMESTAMP(#{clause})"
|
177
177
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
178
|
-
|
178
|
+
"cast(extract(epoch from #{clause}) as int)"
|
179
179
|
else
|
180
180
|
clause
|
181
181
|
end
|
@@ -263,11 +263,7 @@ module ThinkingSphinx
|
|
263
263
|
when @associations.values.flatten.length > 1
|
264
264
|
:string
|
265
265
|
else
|
266
|
-
|
267
|
-
@associations.values.flatten.first.reflection.klass : @model
|
268
|
-
klass.columns.detect { |col|
|
269
|
-
@columns.collect { |c| c.__name.to_s }.include? col.name
|
270
|
-
}.type
|
266
|
+
translated_type_from_database
|
271
267
|
end
|
272
268
|
end
|
273
269
|
|
@@ -281,5 +277,33 @@ module ThinkingSphinx
|
|
281
277
|
}
|
282
278
|
}
|
283
279
|
end
|
280
|
+
|
281
|
+
def type_from_database
|
282
|
+
klass = @associations.values.flatten.first ?
|
283
|
+
@associations.values.flatten.first.reflection.klass : @model
|
284
|
+
|
285
|
+
klass.columns.detect { |col|
|
286
|
+
@columns.collect { |c| c.__name.to_s }.include? col.name
|
287
|
+
}.type
|
288
|
+
end
|
289
|
+
|
290
|
+
def translated_type_from_database
|
291
|
+
case type_from_db = type_from_database
|
292
|
+
when :datetime, :string, :float, :boolean, :integer
|
293
|
+
type_from_db
|
294
|
+
when :decimal
|
295
|
+
:float
|
296
|
+
when :timestamp, :date
|
297
|
+
:datetime
|
298
|
+
else
|
299
|
+
raise <<-MESSAGE
|
300
|
+
|
301
|
+
Cannot automatically map column type #{type_from_db} to an equivalent Sphinx
|
302
|
+
type (integer, float, boolean, datetime, string as ordinal). You could try to
|
303
|
+
explicitly convert the column's value in your define_index block:
|
304
|
+
has "CAST(column AS INT)", :type => :integer, :as => :column
|
305
|
+
MESSAGE
|
306
|
+
end
|
307
|
+
end
|
284
308
|
end
|
285
309
|
end
|
@@ -55,7 +55,7 @@ module ThinkingSphinx
|
|
55
55
|
|
56
56
|
attr_accessor :config_file, :searchd_log_file, :query_log_file,
|
57
57
|
:pid_file, :searchd_file_path, :address, :port, :allow_star,
|
58
|
-
:database_yml_file, :app_root, :bin_path
|
58
|
+
:database_yml_file, :app_root, :bin_path, :model_directories
|
59
59
|
|
60
60
|
attr_accessor :source_options, :index_options
|
61
61
|
|
@@ -85,6 +85,7 @@ module ThinkingSphinx
|
|
85
85
|
self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
|
86
86
|
self.allow_star = false
|
87
87
|
self.bin_path = ""
|
88
|
+
self.model_directories = ["#{app_root}/app/models/"]
|
88
89
|
|
89
90
|
self.source_options = {}
|
90
91
|
self.index_options = {
|
@@ -135,21 +136,22 @@ module ThinkingSphinx
|
|
135
136
|
# messy dependencies issues).
|
136
137
|
#
|
137
138
|
def load_models
|
138
|
-
|
139
|
-
|
140
|
-
|
139
|
+
self.model_directories.each do |base|
|
140
|
+
Dir["#{base}**/*.rb"].each do |file|
|
141
|
+
model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
|
141
142
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
next if model_name.nil?
|
144
|
+
next if ::ActiveRecord::Base.send(:subclasses).detect { |model|
|
145
|
+
model.name == model_name
|
146
|
+
}
|
146
147
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
148
|
+
begin
|
149
|
+
model_name.camelize.constantize
|
150
|
+
rescue LoadError
|
151
|
+
model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry
|
152
|
+
rescue NameError
|
153
|
+
next
|
154
|
+
end
|
153
155
|
end
|
154
156
|
end
|
155
157
|
end
|
@@ -240,6 +242,11 @@ module ThinkingSphinx
|
|
240
242
|
end unless conf.nil?
|
241
243
|
|
242
244
|
self.bin_path += '/' unless self.bin_path.blank?
|
245
|
+
|
246
|
+
if self.allow_star
|
247
|
+
self.index_options[:enable_star] = true
|
248
|
+
self.index_options[:min_prefix_len] = 1
|
249
|
+
end
|
243
250
|
end
|
244
251
|
|
245
252
|
def set_sphinx_setting(object, key, value, allowed = {})
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thinking_sphinx/deltas/default_delta'
|
2
2
|
require 'thinking_sphinx/deltas/delayed_delta'
|
3
|
+
require 'thinking_sphinx/deltas/datetime_delta'
|
3
4
|
|
4
5
|
module ThinkingSphinx
|
5
6
|
module Deltas
|
@@ -9,6 +10,8 @@ module ThinkingSphinx
|
|
9
10
|
DefaultDelta.new index, options
|
10
11
|
when :delayed
|
11
12
|
DelayedDelta.new index, options
|
13
|
+
when :datetime
|
14
|
+
DatetimeDelta.new index, options
|
12
15
|
when FalseClass, nil
|
13
16
|
nil
|
14
17
|
else
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Deltas
|
3
|
+
class DatetimeDelta < ThinkingSphinx::Deltas::DefaultDelta
|
4
|
+
attr_accessor :column, :threshold
|
5
|
+
|
6
|
+
def initialize(index, options)
|
7
|
+
@index = index
|
8
|
+
@column = options.delete(:delta_column) || :updated_at
|
9
|
+
@threshold = options.delete(:threshold) || 1.day
|
10
|
+
end
|
11
|
+
|
12
|
+
def index(model, instance = nil)
|
13
|
+
# do nothing
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def delayed_index(model)
|
18
|
+
config = ThinkingSphinx::Configuration.instance
|
19
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
20
|
+
|
21
|
+
output = `#{config.bin_path}indexer --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
22
|
+
output += `#{config.bin_path}indexer --config #{config.config_file} #{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0`
|
23
|
+
puts output unless ThinkingSphinx.suppress_delta_output?
|
24
|
+
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def toggle(instance)
|
29
|
+
# do nothing
|
30
|
+
end
|
31
|
+
|
32
|
+
def toggled(instance)
|
33
|
+
instance.send(@column) > @threshold.ago
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset_query(model)
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def clause(model, toggled)
|
41
|
+
if toggled
|
42
|
+
"#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
|
43
|
+
" > #{time_difference}"
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def time_difference
|
50
|
+
case @index.adapter
|
51
|
+
when :mysql
|
52
|
+
"DATE_SUB(NOW(), INTERVAL #{@threshold} SECOND)"
|
53
|
+
when :postgres
|
54
|
+
"current_timestamp - interval '#{@threshold} seconds'"
|
55
|
+
else
|
56
|
+
raise "Unknown Database Adapter"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -5,7 +5,7 @@ module ThinkingSphinx
|
|
5
5
|
|
6
6
|
def initialize(index, options)
|
7
7
|
@index = index
|
8
|
-
@column = options.delete(:
|
8
|
+
@column = options.delete(:delta_column) || :delta
|
9
9
|
end
|
10
10
|
|
11
11
|
def index(model, instance = nil)
|
@@ -31,6 +31,15 @@ module ThinkingSphinx
|
|
31
31
|
instance.delta = true
|
32
32
|
end
|
33
33
|
|
34
|
+
def toggled(instance)
|
35
|
+
instance.delta
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset_query(model)
|
39
|
+
"UPDATE #{model.quoted_table_name} SET " +
|
40
|
+
"#{@index.quote_column(@column.to_s)} = #{@index.db_boolean(false)}"
|
41
|
+
end
|
42
|
+
|
34
43
|
def clause(model, toggled)
|
35
44
|
"#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
|
36
45
|
" = #{@index.db_boolean(toggled)}"
|
@@ -44,7 +53,7 @@ module ThinkingSphinx
|
|
44
53
|
|
45
54
|
def delta_index_name(model)
|
46
55
|
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
|
47
|
-
end
|
56
|
+
end
|
48
57
|
end
|
49
58
|
end
|
50
59
|
end
|
@@ -126,7 +126,7 @@ module ThinkingSphinx
|
|
126
126
|
assocs = all_associations
|
127
127
|
|
128
128
|
where_clause = ""
|
129
|
-
if self.delta?
|
129
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
130
130
|
where_clause << " AND #{@delta_object.clause(@model, options[:delta])}"
|
131
131
|
end
|
132
132
|
unless @conditions.empty?
|
@@ -190,7 +190,10 @@ GROUP BY #{ (
|
|
190
190
|
|
191
191
|
sql = "SELECT #{min_statement}, #{max_statement} " +
|
192
192
|
"FROM #{@model.quoted_table_name} "
|
193
|
-
|
193
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
194
|
+
sql << "WHERE #{@delta_object.clause(@model, options[:delta])}"
|
195
|
+
end
|
196
|
+
|
194
197
|
sql
|
195
198
|
end
|
196
199
|
|
@@ -436,8 +439,8 @@ GROUP BY #{ (
|
|
436
439
|
end
|
437
440
|
|
438
441
|
def sql_query_pre_for_core
|
439
|
-
if self.delta?
|
440
|
-
[
|
442
|
+
if self.delta? && !@delta_object.reset_query(@model).blank?
|
443
|
+
[@delta_object.reset_query(@model)]
|
441
444
|
else
|
442
445
|
[]
|
443
446
|
end
|
@@ -29,6 +29,18 @@ Array.send(
|
|
29
29
|
:include, ThinkingSphinx::ArrayExtractOptions
|
30
30
|
) unless Array.instance_methods.include?("extract_options!")
|
31
31
|
|
32
|
+
module ThinkingSphinx
|
33
|
+
module AbstractQuotedTableName
|
34
|
+
def quote_table_name(name)
|
35
|
+
quote_column_name(name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(
|
41
|
+
:include, ThinkingSphinx::AbstractQuotedTableName
|
42
|
+
) unless ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_methods.include?("quote_table_name")
|
43
|
+
|
32
44
|
module ThinkingSphinx
|
33
45
|
module MysqlQuotedTableName
|
34
46
|
def quote_table_name(name) #:nodoc:
|
@@ -72,7 +72,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
|
|
72
72
|
before :each do
|
73
73
|
ThinkingSphinx::Configuration.stub_method(:environment => "spec")
|
74
74
|
ThinkingSphinx.stub_method(:deltas_enabled? => true, :sphinx_running? => true)
|
75
|
-
Person.
|
75
|
+
Person.delta_object.stub_methods(:` => "", :toggled => true)
|
76
76
|
|
77
77
|
@person = Person.new
|
78
78
|
@person.stub_method(
|
@@ -37,8 +37,9 @@ namespace :thinking_sphinx do
|
|
37
37
|
desc "Stop Sphinx using Thinking Sphinx's settings"
|
38
38
|
task :stop => :app_env do
|
39
39
|
raise RuntimeError, "searchd is not running." unless sphinx_running?
|
40
|
-
|
41
|
-
|
40
|
+
config = ThinkingSphinx::Configuration.instance
|
41
|
+
pid = sphinx_pid
|
42
|
+
system "searchd --stop --config #{config.config_file}"
|
42
43
|
puts "Stopped search daemon (pid #{pid})."
|
43
44
|
end
|
44
45
|
|
@@ -69,8 +70,22 @@ namespace :thinking_sphinx do
|
|
69
70
|
system cmd
|
70
71
|
end
|
71
72
|
|
73
|
+
namespace :index do
|
74
|
+
task :delta => :app_env do
|
75
|
+
ThinkingSphinx.indexed_models.select { |model|
|
76
|
+
model.constantize.sphinx_indexes.any? { |index| index.delta? }
|
77
|
+
}.each do |model|
|
78
|
+
model.constantize.sphinx_indexes.select { |index|
|
79
|
+
index.delta? && index.delta_object.respond_to?(:delayed_index)
|
80
|
+
}.each { |index|
|
81
|
+
index.delta_object.delayed_index(index.model)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
72
87
|
desc "Process stored delta index requests"
|
73
|
-
task :
|
88
|
+
task :delayed_delta => :app_env do
|
74
89
|
require 'delayed/worker'
|
75
90
|
|
76
91
|
Delayed::Worker.new(
|
@@ -90,6 +105,9 @@ namespace :ts do
|
|
90
105
|
desc "Index data for Sphinx using Thinking Sphinx's settings"
|
91
106
|
task :in => "thinking_sphinx:index"
|
92
107
|
desc "Index data for Sphinx using Thinking Sphinx's settings"
|
108
|
+
namespace :in do
|
109
|
+
task :delta => "thinking_sphinx:index:delta"
|
110
|
+
end
|
93
111
|
task :index => "thinking_sphinx:index"
|
94
112
|
desc "Restart Sphinx"
|
95
113
|
task :restart => "thinking_sphinx:restart"
|
@@ -98,19 +116,13 @@ namespace :ts do
|
|
98
116
|
desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
|
99
117
|
task :config => "thinking_sphinx:configure"
|
100
118
|
desc "Process stored delta index requests"
|
101
|
-
task :
|
119
|
+
task :dd => "thinking_sphinx:delayed_delta"
|
102
120
|
end
|
103
121
|
|
104
122
|
def sphinx_pid
|
105
|
-
|
106
|
-
|
107
|
-
if File.exists?(config.pid_file)
|
108
|
-
`cat #{config.pid_file}`[/\d+/]
|
109
|
-
else
|
110
|
-
nil
|
111
|
-
end
|
123
|
+
ThinkingSphinx.sphinx_pid
|
112
124
|
end
|
113
125
|
|
114
126
|
def sphinx_running?
|
115
|
-
|
127
|
+
ThinkingSphinx.sphinx_running?
|
116
128
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freelancing-god-thinking-sphinx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-08 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -33,6 +33,7 @@ files:
|
|
33
33
|
- lib/thinking_sphinx/attribute.rb
|
34
34
|
- lib/thinking_sphinx/collection.rb
|
35
35
|
- lib/thinking_sphinx/configuration.rb
|
36
|
+
- lib/thinking_sphinx/deltas/datetime_delta.rb
|
36
37
|
- lib/thinking_sphinx/deltas/default_delta.rb
|
37
38
|
- lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb
|
38
39
|
- lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb
|