thinking-sphinx 2.0.6 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/HISTORY +157 -0
  2. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  3. data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
  4. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  5. data/lib/thinking-sphinx.rb +1 -0
  6. data/lib/thinking_sphinx/action_controller.rb +31 -0
  7. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  8. data/lib/thinking_sphinx/active_record/collection_proxy.rb +40 -0
  9. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  10. data/lib/thinking_sphinx/active_record/delta.rb +65 -0
  11. data/lib/thinking_sphinx/active_record/has_many_association.rb +37 -0
  12. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  13. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  14. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  15. data/lib/thinking_sphinx/active_record.rb +383 -0
  16. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  17. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  18. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +171 -0
  19. data/lib/thinking_sphinx/association.rb +229 -0
  20. data/lib/thinking_sphinx/attribute.rb +407 -0
  21. data/lib/thinking_sphinx/auto_version.rb +38 -0
  22. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  23. data/lib/thinking_sphinx/class_facet.rb +20 -0
  24. data/lib/thinking_sphinx/configuration.rb +335 -0
  25. data/lib/thinking_sphinx/context.rb +77 -0
  26. data/lib/thinking_sphinx/core/string.rb +15 -0
  27. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  28. data/lib/thinking_sphinx/deltas.rb +28 -0
  29. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  30. data/lib/thinking_sphinx/excerpter.rb +23 -0
  31. data/lib/thinking_sphinx/facet.rb +128 -0
  32. data/lib/thinking_sphinx/facet_search.rb +170 -0
  33. data/lib/thinking_sphinx/field.rb +98 -0
  34. data/lib/thinking_sphinx/index/builder.rb +312 -0
  35. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  36. data/lib/thinking_sphinx/index.rb +157 -0
  37. data/lib/thinking_sphinx/join.rb +37 -0
  38. data/lib/thinking_sphinx/property.rb +185 -0
  39. data/lib/thinking_sphinx/railtie.rb +46 -0
  40. data/lib/thinking_sphinx/search.rb +995 -0
  41. data/lib/thinking_sphinx/search_methods.rb +439 -0
  42. data/lib/thinking_sphinx/sinatra.rb +7 -0
  43. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  44. data/lib/thinking_sphinx/source/sql.rb +157 -0
  45. data/lib/thinking_sphinx/source.rb +194 -0
  46. data/lib/thinking_sphinx/tasks.rb +132 -0
  47. data/lib/thinking_sphinx/test.rb +55 -0
  48. data/lib/thinking_sphinx/version.rb +3 -0
  49. data/lib/thinking_sphinx.rb +296 -0
  50. metadata +53 -4
@@ -0,0 +1,194 @@
1
+ require 'thinking_sphinx/source/internal_properties'
2
+ require 'thinking_sphinx/source/sql'
3
+
4
+ module ThinkingSphinx
5
+ class Source
6
+ include ThinkingSphinx::Source::InternalProperties
7
+ include ThinkingSphinx::Source::SQL
8
+
9
+ attr_accessor :model, :fields, :attributes, :joins, :conditions, :groupings,
10
+ :options
11
+ attr_reader :base, :index, :database_configuration
12
+
13
+ def initialize(index, options = {})
14
+ @index = index
15
+ @model = index.model
16
+ @fields = []
17
+ @attributes = []
18
+ @joins = []
19
+ @conditions = []
20
+ @groupings = []
21
+ @options = options
22
+ @associations = {}
23
+ @database_configuration = @model.connection.
24
+ instance_variable_get(:@config).clone
25
+
26
+ @base = join_dependency_class.new(
27
+ @model, [], initial_joins
28
+ )
29
+
30
+ unless @model.descends_from_active_record?
31
+ stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
32
+ @conditions << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'"
33
+ end
34
+
35
+ add_internal_attributes_and_facets
36
+ end
37
+
38
+ def name
39
+ index.name
40
+ end
41
+
42
+ def to_riddle_for_core(offset, position)
43
+ source = Riddle::Configuration::SQLSource.new(
44
+ "#{index.core_name}_#{position}", adapter.sphinx_identifier
45
+ )
46
+
47
+ set_source_database_settings source
48
+ set_source_fields source
49
+ set_source_attributes source, offset
50
+ set_source_settings source
51
+ set_source_sql source, offset
52
+
53
+ source
54
+ end
55
+
56
+ def to_riddle_for_delta(offset, position)
57
+ source = Riddle::Configuration::SQLSource.new(
58
+ "#{index.delta_name}_#{position}", adapter.sphinx_identifier
59
+ )
60
+ source.parent = "#{index.core_name}_#{position}"
61
+
62
+ set_source_database_settings source
63
+ set_source_fields source
64
+ set_source_attributes source, offset, true
65
+ set_source_settings source
66
+ set_source_sql source, offset, true
67
+
68
+ source
69
+ end
70
+
71
+ def delta?
72
+ !@index.delta_object.nil?
73
+ end
74
+
75
+ # Gets the association stack for a specific key.
76
+ #
77
+ def association(key)
78
+ @associations[key] ||= Association.children(@model, key)
79
+ end
80
+
81
+ private
82
+
83
+ def adapter
84
+ @adapter ||= @model.sphinx_database_adapter
85
+ end
86
+
87
+ def available_attributes
88
+ attributes.select { |attrib| attrib.available? }
89
+ end
90
+
91
+ def set_source_database_settings(source)
92
+ config = @database_configuration
93
+
94
+ source.sql_host = config[:host] || "localhost"
95
+ source.sql_user = config[:username] || config[:user] || 'root'
96
+ source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
97
+ source.sql_db = config[:database]
98
+ source.sql_port = config[:port]
99
+ source.sql_sock = config[:socket]
100
+ end
101
+
102
+ def set_source_fields(source)
103
+ fields.each do |field|
104
+ source.sql_file_field << field.unique_name if field.file?
105
+ source.sql_field_string << field.unique_name if field.with_attribute?
106
+ source.sql_field_str2wordcount << field.unique_name if field.with_wordcount?
107
+ end
108
+ end
109
+
110
+ def set_source_attributes(source, offset, delta = false)
111
+ available_attributes.each do |attrib|
112
+ source.send(attrib.type_to_config) << attrib.config_value(offset, delta)
113
+ end
114
+ end
115
+
116
+ def set_source_sql(source, offset, delta = false)
117
+ source.sql_query = to_sql(:offset => offset, :delta => delta).gsub(/\n/, ' ')
118
+ source.sql_query_range = to_sql_query_range(:delta => delta)
119
+ source.sql_query_info = to_sql_query_info(offset)
120
+
121
+ source.sql_query_pre += send(!delta ? :sql_query_pre_for_core : :sql_query_pre_for_delta)
122
+
123
+ if @index.local_options[:group_concat_max_len]
124
+ source.sql_query_pre << "SET SESSION group_concat_max_len = #{@index.local_options[:group_concat_max_len]}"
125
+ end
126
+
127
+ source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
128
+ source.sql_query_pre << adapter.utc_query_pre
129
+ end
130
+
131
+ def set_source_settings(source)
132
+ config = ThinkingSphinx::Configuration.instance
133
+ config.source_options.each do |key, value|
134
+ source.send("#{key}=".to_sym, value)
135
+ end
136
+
137
+ source_options = ThinkingSphinx::Configuration::SourceOptions
138
+ @options.each do |key, value|
139
+ if source_options.include?(key.to_s) && !value.nil?
140
+ source.send("#{key}=".to_sym, value)
141
+ end
142
+ end
143
+ end
144
+
145
+ # Returns all associations used amongst all the fields and attributes.
146
+ # This includes all associations between the model and what the actual
147
+ # columns are from.
148
+ #
149
+ def all_associations
150
+ @all_associations ||= (
151
+ # field associations
152
+ @fields.collect { |field|
153
+ field.associations.values
154
+ }.flatten +
155
+ # attribute associations
156
+ @attributes.collect { |attrib|
157
+ attrib.associations.values if attrib.include_as_association?
158
+ }.compact.flatten +
159
+ # explicit joins
160
+ @joins.collect { |join|
161
+ join.associations
162
+ }.flatten
163
+ ).uniq.collect { |assoc|
164
+ # get ancestors as well as column-level associations
165
+ assoc.ancestors
166
+ }.flatten.uniq
167
+ end
168
+
169
+ def utf8?
170
+ @index.options[:charset_type] =~ /utf-8|zh_cn.utf-8/
171
+ end
172
+
173
+ def join_dependency_class
174
+ if rails_3_1?
175
+ ::ActiveRecord::Associations::JoinDependency
176
+ else
177
+ ::ActiveRecord::Associations::ClassMethods::JoinDependency
178
+ end
179
+ end
180
+
181
+ def initial_joins
182
+ if rails_3_1?
183
+ []
184
+ else
185
+ nil
186
+ end
187
+ end
188
+
189
+ def rails_3_1?
190
+ ::ActiveRecord::Associations.constants.include?(:JoinDependency) ||
191
+ ::ActiveRecord::Associations.constants.include?('JoinDependency')
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,132 @@
1
+ require 'fileutils'
2
+ require 'timeout'
3
+
4
+ namespace :thinking_sphinx do
5
+ task :app_env do
6
+ if defined?(Rails)
7
+ Rails.application.require_environment!
8
+ Rails.configuration.cache_classes = false
9
+ elsif defined?(Merb)
10
+ Rake::Task[:merb_env].invoke
11
+ elsif defined?(Sinatra)
12
+ Sinatra::Application.environment = ENV['RACK_ENV']
13
+ end
14
+ end
15
+
16
+ desc "Output the current Thinking Sphinx version"
17
+ task :version => :app_env do
18
+ puts "Thinking Sphinx v" + ThinkingSphinx::Version
19
+ end
20
+
21
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
22
+ task :running_start => :app_env do
23
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
24
+ Rake::Task["thinking_sphinx:start"].invoke
25
+ end
26
+
27
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
28
+ task :start => :app_env do
29
+ config = ThinkingSphinx::Configuration.instance
30
+
31
+ FileUtils.mkdir_p config.searchd_file_path
32
+ raise RuntimeError, "searchd is already running." if sphinx_running?
33
+
34
+ Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
35
+
36
+ config.controller.start
37
+
38
+ if sphinx_running?
39
+ puts "Started successfully (pid #{sphinx_pid})."
40
+ else
41
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
42
+ end
43
+ end
44
+
45
+ desc "Stop Sphinx using Thinking Sphinx's settings"
46
+ task :stop => :app_env do
47
+ unless sphinx_running?
48
+ puts "searchd is not running"
49
+ else
50
+ config = ThinkingSphinx::Configuration.instance
51
+ pid = sphinx_pid
52
+ config.controller.stop
53
+
54
+ # Ensure searchd is stopped, but don't try too hard
55
+ Timeout.timeout(config.stop_timeout) do
56
+ sleep(1) until config.controller.stop
57
+ end
58
+
59
+ puts "Stopped search daemon (pid #{pid})."
60
+ end
61
+ end
62
+
63
+ desc "Restart Sphinx"
64
+ task :restart => [:app_env, :stop, :start]
65
+
66
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
67
+ task :configure => :app_env do
68
+ config = ThinkingSphinx::Configuration.instance
69
+ puts "Generating Configuration to #{config.config_file}"
70
+ config.build
71
+ end
72
+
73
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
74
+ task :index => :app_env do
75
+ config = ThinkingSphinx::Configuration.instance
76
+ unless ENV["INDEX_ONLY"] == "true"
77
+ puts "Generating Configuration to #{config.config_file}"
78
+ config.build
79
+ end
80
+
81
+ FileUtils.mkdir_p config.searchd_file_path
82
+ config.controller.index :verbose => true
83
+ end
84
+
85
+ desc "Reindex Sphinx without regenerating the configuration file"
86
+ task :reindex => :app_env do
87
+ config = ThinkingSphinx::Configuration.instance
88
+ FileUtils.mkdir_p config.searchd_file_path
89
+ output = config.controller.index
90
+ puts output
91
+ config.touch_reindex_file(output)
92
+ end
93
+
94
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
95
+ task :rebuild => :app_env do
96
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
97
+ Rake::Task["thinking_sphinx:index"].invoke
98
+ Rake::Task["thinking_sphinx:start"].invoke
99
+ end
100
+ end
101
+
102
+ namespace :ts do
103
+ desc "Output the current Thinking Sphinx version"
104
+ task :version => "thinking_sphinx:version"
105
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
106
+ task :run => "thinking_sphinx:running_start"
107
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
108
+ task :start => "thinking_sphinx:start"
109
+ desc "Stop Sphinx using Thinking Sphinx's settings"
110
+ task :stop => "thinking_sphinx:stop"
111
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
112
+ task :in => "thinking_sphinx:index"
113
+ task :index => "thinking_sphinx:index"
114
+ desc "Reindex Sphinx without regenerating the configuration file"
115
+ task :reindex => "thinking_sphinx:reindex"
116
+ desc "Restart Sphinx"
117
+ task :restart => "thinking_sphinx:restart"
118
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
119
+ task :conf => "thinking_sphinx:configure"
120
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
121
+ task :config => "thinking_sphinx:configure"
122
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
123
+ task :rebuild => "thinking_sphinx:rebuild"
124
+ end
125
+
126
+ def sphinx_pid
127
+ ThinkingSphinx.sphinx_pid
128
+ end
129
+
130
+ def sphinx_running?
131
+ ThinkingSphinx.sphinx_running?
132
+ end
@@ -0,0 +1,55 @@
1
+ class ThinkingSphinx::Test
2
+ def self.init(suppress_delta_output = true)
3
+ set_flags suppress_delta_output
4
+ create_indexes_folder
5
+ end
6
+
7
+ def self.start
8
+ config.build
9
+ config.controller.index
10
+ config.controller.start
11
+ end
12
+
13
+ def self.start_with_autostop
14
+ autostop
15
+ start
16
+ end
17
+
18
+ def self.stop
19
+ config.controller.stop
20
+ sleep(0.5) # Ensure Sphinx has shut down completely
21
+ end
22
+
23
+ def self.autostop
24
+ Kernel.at_exit do
25
+ ThinkingSphinx::Test.stop
26
+ end
27
+ end
28
+
29
+ def self.run(&block)
30
+ begin
31
+ start
32
+ yield
33
+ ensure
34
+ stop
35
+ end
36
+ end
37
+
38
+ def self.config
39
+ @config ||= ::ThinkingSphinx::Configuration.instance
40
+ end
41
+
42
+ def self.index(*indexes)
43
+ config.controller.index *indexes
44
+ end
45
+
46
+ def self.set_flags(suppress_delta_output)
47
+ ::ThinkingSphinx.deltas_enabled = true
48
+ ::ThinkingSphinx.updates_enabled = true
49
+ ::ThinkingSphinx.suppress_delta_output = suppress_delta_output
50
+ end
51
+
52
+ def self.create_indexes_folder
53
+ FileUtils.mkdir_p config.searchd_file_path
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module ThinkingSphinx
2
+ Version = '2.0.7'
3
+ end
@@ -0,0 +1,296 @@
1
+ require 'thread'
2
+ require 'active_record'
3
+ require 'yaml'
4
+ require 'riddle'
5
+
6
+ require 'thinking_sphinx/auto_version'
7
+ require 'thinking_sphinx/core/string'
8
+ require 'thinking_sphinx/property'
9
+ require 'thinking_sphinx/active_record'
10
+ require 'thinking_sphinx/association'
11
+ require 'thinking_sphinx/attribute'
12
+ require 'thinking_sphinx/bundled_search'
13
+ require 'thinking_sphinx/configuration'
14
+ require 'thinking_sphinx/context'
15
+ require 'thinking_sphinx/excerpter'
16
+ require 'thinking_sphinx/facet'
17
+ require 'thinking_sphinx/class_facet'
18
+ require 'thinking_sphinx/facet_search'
19
+ require 'thinking_sphinx/field'
20
+ require 'thinking_sphinx/index'
21
+ require 'thinking_sphinx/join'
22
+ require 'thinking_sphinx/source'
23
+ require 'thinking_sphinx/search'
24
+ require 'thinking_sphinx/search_methods'
25
+ require 'thinking_sphinx/deltas'
26
+
27
+ require 'thinking_sphinx/adapters/abstract_adapter'
28
+ require 'thinking_sphinx/adapters/mysql_adapter'
29
+ require 'thinking_sphinx/adapters/postgresql_adapter'
30
+
31
+ require 'thinking_sphinx/railtie' if defined?(Rails)
32
+
33
+ module ThinkingSphinx
34
+ mattr_accessor :database_adapter
35
+
36
+ # A ConnectionError will get thrown when a connection to Sphinx can't be
37
+ # made.
38
+ class ConnectionError < StandardError
39
+ end
40
+
41
+ # A StaleIdsException is thrown by Collection.instances_from_matches if there
42
+ # are records in Sphinx but not in the database, so the search can be retried.
43
+ class StaleIdsException < StandardError
44
+ attr_accessor :ids
45
+ def initialize(ids)
46
+ self.ids = ids
47
+ end
48
+ end
49
+
50
+ # A SphinxError occurs when Sphinx responds with an error due to problematic
51
+ # queries or indexes.
52
+ class SphinxError < RuntimeError
53
+ attr_accessor :results
54
+ def initialize(message = nil, results = nil)
55
+ super(message)
56
+ self.results = results
57
+ end
58
+ end
59
+
60
+ # The collection of indexed models. Keep in mind that Rails lazily loads
61
+ # its classes, so this may not actually be populated with _all_ the models
62
+ # that have Sphinx indexes.
63
+ @@sphinx_mutex = Mutex.new
64
+ @@context = nil
65
+ @@define_indexes = true
66
+ @@deltas_enabled = nil
67
+ @@updates_enabled = nil
68
+ @@suppress_delta_output = false
69
+ @@remote_sphinx = false
70
+ @@use_group_by_shortcut = nil
71
+
72
+ def self.mutex
73
+ @@sphinx_mutex
74
+ end
75
+
76
+ def self.context
77
+ if @@context.nil?
78
+ mutex.synchronize do
79
+ if @@context.nil?
80
+ @@context = ThinkingSphinx::Context.new
81
+ @@context.prepare
82
+ end
83
+ end
84
+ end
85
+
86
+ @@context
87
+ end
88
+
89
+ def self.reset_context!(context = nil)
90
+ mutex.synchronize do
91
+ @@context = context
92
+ end
93
+ end
94
+
95
+ def self.unique_id_expression(adapter, offset = nil)
96
+ "* #{adapter.cast_to_int context.indexed_models.size} + #{offset || 0}"
97
+ end
98
+
99
+ # Check if index definition is disabled.
100
+ #
101
+ def self.define_indexes?
102
+ @@define_indexes
103
+ end
104
+
105
+ # Enable/disable indexes - you may want to do this while migrating data.
106
+ #
107
+ # ThinkingSphinx.define_indexes = false
108
+ #
109
+ def self.define_indexes=(value)
110
+ mutex.synchronize do
111
+ @@define_indexes = value
112
+ end
113
+ end
114
+
115
+ # Check if delta indexing is enabled/disabled.
116
+ #
117
+ def self.deltas_enabled?
118
+ if @@deltas_enabled.nil?
119
+ mutex.synchronize do
120
+ if @@deltas_enabled.nil?
121
+ @@deltas_enabled = (
122
+ ThinkingSphinx::Configuration.environment != "test"
123
+ )
124
+ end
125
+ end
126
+ end
127
+
128
+ @@deltas_enabled && !deltas_suspended?
129
+ end
130
+
131
+ # Enable/disable delta indexing.
132
+ #
133
+ # ThinkingSphinx.deltas_enabled = false
134
+ #
135
+ def self.deltas_enabled=(value)
136
+ mutex.synchronize do
137
+ @@deltas_enabled = value
138
+ end
139
+ end
140
+
141
+ # Check if delta indexing is suspended.
142
+ #
143
+ def self.deltas_suspended?
144
+ if Thread.current[:thinking_sphinx_deltas_suspended].nil?
145
+ Thread.current[:thinking_sphinx_deltas_suspended] = false
146
+ end
147
+
148
+ Thread.current[:thinking_sphinx_deltas_suspended]
149
+ end
150
+
151
+ # Suspend/resume delta indexing.
152
+ #
153
+ # ThinkingSphinx.deltas_suspended = false
154
+ #
155
+ def self.deltas_suspended=(value)
156
+ Thread.current[:thinking_sphinx_deltas_suspended] = value
157
+ end
158
+
159
+ # Check if updates are enabled. True by default, unless within the test
160
+ # environment.
161
+ #
162
+ def self.updates_enabled?
163
+ if @@updates_enabled.nil?
164
+ mutex.synchronize do
165
+ if @@updates_enabled.nil?
166
+ @@updates_enabled = (
167
+ ThinkingSphinx::Configuration.environment != "test"
168
+ )
169
+ end
170
+ end
171
+ end
172
+
173
+ @@updates_enabled
174
+ end
175
+
176
+ # Enable/disable updates to Sphinx
177
+ #
178
+ # ThinkingSphinx.updates_enabled = false
179
+ #
180
+ def self.updates_enabled=(value)
181
+ mutex.synchronize do
182
+ @@updates_enabled = value
183
+ end
184
+ end
185
+
186
+ def self.suppress_delta_output?
187
+ @@suppress_delta_output
188
+ end
189
+
190
+ def self.suppress_delta_output=(value)
191
+ mutex.synchronize do
192
+ @@suppress_delta_output = value
193
+ end
194
+ end
195
+
196
+ # Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
197
+ # or if not using MySQL, this will return false.
198
+ #
199
+ def self.use_group_by_shortcut?
200
+ if @@use_group_by_shortcut.nil?
201
+ mutex.synchronize do
202
+ if @@use_group_by_shortcut.nil?
203
+ @@use_group_by_shortcut = !!(
204
+ mysql? && ::ActiveRecord::Base.connection.select_all(
205
+ "SELECT @@global.sql_mode, @@session.sql_mode;"
206
+ ).all? { |key, value|
207
+ value.nil? || value[/ONLY_FULL_GROUP_BY/].nil?
208
+ }
209
+ )
210
+ end
211
+ end
212
+ end
213
+
214
+ @@use_group_by_shortcut
215
+ end
216
+
217
+ def self.reset_use_group_by_shortcut
218
+ mutex.synchronize do
219
+ @@use_group_by_shortcut = nil
220
+ end
221
+ end
222
+
223
+ # An indication of whether Sphinx is running on a remote machine instead of
224
+ # the same machine.
225
+ #
226
+ def self.remote_sphinx?
227
+ @@remote_sphinx
228
+ end
229
+
230
+ # Tells Thinking Sphinx that Sphinx is running on a different machine, and
231
+ # thus it can't reliably guess whether it is running or not (ie: the
232
+ # #sphinx_running? method), and so just assumes it is.
233
+ #
234
+ # Useful for multi-machine deployments. Set it in your production.rb file.
235
+ #
236
+ # ThinkingSphinx.remote_sphinx = true
237
+ #
238
+ def self.remote_sphinx=(value)
239
+ mutex.synchronize do
240
+ @@remote_sphinx = value
241
+ end
242
+ end
243
+
244
+ # Check if Sphinx is running. If remote_sphinx is set to true (indicating
245
+ # Sphinx is on a different machine), this will always return true, and you
246
+ # will have to handle any connection errors yourself.
247
+ #
248
+ def self.sphinx_running?
249
+ remote_sphinx? || sphinx_running_by_pid?
250
+ end
251
+
252
+ # Check if Sphinx is actually running, provided the pid is on the same
253
+ # machine as this code.
254
+ #
255
+ def self.sphinx_running_by_pid?
256
+ !!sphinx_pid && pid_active?(sphinx_pid)
257
+ end
258
+
259
+ def self.sphinx_pid
260
+ if File.exists?(ThinkingSphinx::Configuration.instance.pid_file)
261
+ File.read(ThinkingSphinx::Configuration.instance.pid_file)[/\d+/]
262
+ else
263
+ nil
264
+ end
265
+ end
266
+
267
+ def self.pid_active?(pid)
268
+ !!Process.kill(0, pid.to_i)
269
+ rescue Errno::EPERM => e
270
+ true
271
+ rescue Exception => e
272
+ false
273
+ end
274
+
275
+ def self.microsoft?
276
+ RUBY_PLATFORM =~ /mswin/
277
+ end
278
+
279
+ def self.jruby?
280
+ defined?(JRUBY_VERSION)
281
+ end
282
+
283
+ def self.mysql?
284
+ ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlAdapter" ||
285
+ ::ActiveRecord::Base.connection.class.name.demodulize == "Mysql2Adapter" ||
286
+ ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlplusAdapter" || (
287
+ jruby? && ::ActiveRecord::Base.connection.config[:adapter] == "jdbcmysql"
288
+ )
289
+ end
290
+
291
+ def self.rails_3_1?
292
+ !!defined?(::ActiveRecord::Associations::CollectionProxy)
293
+ end
294
+
295
+ extend ThinkingSphinx::SearchMethods::ClassMethods
296
+ end