friendlyfashion-thinking-sphinx 2.0.13

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.
Files changed (175) hide show
  1. data/HISTORY +244 -0
  2. data/LICENCE +20 -0
  3. data/README.textile +235 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +21 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +88 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/field_sorting.feature +18 -0
  15. data/features/handling_edits.feature +94 -0
  16. data/features/retry_stale_indexes.feature +24 -0
  17. data/features/searching_across_models.feature +20 -0
  18. data/features/searching_by_index.feature +40 -0
  19. data/features/searching_by_model.feature +175 -0
  20. data/features/searching_with_find_arguments.feature +56 -0
  21. data/features/sphinx_detection.feature +25 -0
  22. data/features/sphinx_scopes.feature +68 -0
  23. data/features/step_definitions/alpha_steps.rb +16 -0
  24. data/features/step_definitions/beta_steps.rb +7 -0
  25. data/features/step_definitions/common_steps.rb +201 -0
  26. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  27. data/features/step_definitions/facet_steps.rb +96 -0
  28. data/features/step_definitions/find_arguments_steps.rb +36 -0
  29. data/features/step_definitions/gamma_steps.rb +15 -0
  30. data/features/step_definitions/scope_steps.rb +19 -0
  31. data/features/step_definitions/search_steps.rb +94 -0
  32. data/features/step_definitions/sphinx_steps.rb +35 -0
  33. data/features/sti_searching.feature +19 -0
  34. data/features/support/env.rb +27 -0
  35. data/features/support/lib/generic_delta_handler.rb +8 -0
  36. data/features/thinking_sphinx/database.example.yml +3 -0
  37. data/features/thinking_sphinx/db/.gitignore +1 -0
  38. data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
  39. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  40. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  41. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  42. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  43. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  44. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  45. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  46. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  47. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  48. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  49. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  50. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  51. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  52. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
  53. data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
  54. data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
  55. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  56. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  57. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  58. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  59. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  60. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  61. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  62. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  63. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  64. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  65. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  66. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  67. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  68. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  69. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  70. data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
  71. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  72. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  73. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  74. data/features/thinking_sphinx/models/alpha.rb +23 -0
  75. data/features/thinking_sphinx/models/andrew.rb +17 -0
  76. data/features/thinking_sphinx/models/animal.rb +5 -0
  77. data/features/thinking_sphinx/models/author.rb +3 -0
  78. data/features/thinking_sphinx/models/beta.rb +13 -0
  79. data/features/thinking_sphinx/models/box.rb +8 -0
  80. data/features/thinking_sphinx/models/cat.rb +3 -0
  81. data/features/thinking_sphinx/models/category.rb +4 -0
  82. data/features/thinking_sphinx/models/comment.rb +10 -0
  83. data/features/thinking_sphinx/models/developer.rb +21 -0
  84. data/features/thinking_sphinx/models/dog.rb +3 -0
  85. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  86. data/features/thinking_sphinx/models/fox.rb +5 -0
  87. data/features/thinking_sphinx/models/gamma.rb +5 -0
  88. data/features/thinking_sphinx/models/genre.rb +3 -0
  89. data/features/thinking_sphinx/models/medium.rb +5 -0
  90. data/features/thinking_sphinx/models/music.rb +10 -0
  91. data/features/thinking_sphinx/models/person.rb +24 -0
  92. data/features/thinking_sphinx/models/post.rb +22 -0
  93. data/features/thinking_sphinx/models/robot.rb +12 -0
  94. data/features/thinking_sphinx/models/tag.rb +3 -0
  95. data/features/thinking_sphinx/models/tagging.rb +4 -0
  96. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  97. data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
  98. data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
  99. data/lib/thinking-sphinx.rb +1 -0
  100. data/lib/thinking_sphinx/action_controller.rb +31 -0
  101. data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
  102. data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
  103. data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
  104. data/lib/thinking_sphinx/active_record/delta.rb +67 -0
  105. data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
  106. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
  107. data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
  108. data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
  109. data/lib/thinking_sphinx/active_record.rb +386 -0
  110. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
  111. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
  112. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
  113. data/lib/thinking_sphinx/association.rb +230 -0
  114. data/lib/thinking_sphinx/attribute.rb +405 -0
  115. data/lib/thinking_sphinx/auto_version.rb +40 -0
  116. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  117. data/lib/thinking_sphinx/class_facet.rb +20 -0
  118. data/lib/thinking_sphinx/configuration.rb +375 -0
  119. data/lib/thinking_sphinx/context.rb +76 -0
  120. data/lib/thinking_sphinx/core/string.rb +15 -0
  121. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  122. data/lib/thinking_sphinx/deltas.rb +28 -0
  123. data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
  124. data/lib/thinking_sphinx/excerpter.rb +23 -0
  125. data/lib/thinking_sphinx/facet.rb +135 -0
  126. data/lib/thinking_sphinx/facet_search.rb +170 -0
  127. data/lib/thinking_sphinx/field.rb +98 -0
  128. data/lib/thinking_sphinx/index/builder.rb +315 -0
  129. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  130. data/lib/thinking_sphinx/index.rb +159 -0
  131. data/lib/thinking_sphinx/join.rb +37 -0
  132. data/lib/thinking_sphinx/property.rb +187 -0
  133. data/lib/thinking_sphinx/railtie.rb +43 -0
  134. data/lib/thinking_sphinx/search.rb +1061 -0
  135. data/lib/thinking_sphinx/search_methods.rb +439 -0
  136. data/lib/thinking_sphinx/sinatra.rb +7 -0
  137. data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
  138. data/lib/thinking_sphinx/source/sql.rb +174 -0
  139. data/lib/thinking_sphinx/source.rb +194 -0
  140. data/lib/thinking_sphinx/tasks.rb +142 -0
  141. data/lib/thinking_sphinx/test.rb +55 -0
  142. data/lib/thinking_sphinx/version.rb +3 -0
  143. data/lib/thinking_sphinx.rb +297 -0
  144. data/spec/fixtures/data.sql +32 -0
  145. data/spec/fixtures/database.yml.default +3 -0
  146. data/spec/fixtures/models.rb +164 -0
  147. data/spec/fixtures/structure.sql +146 -0
  148. data/spec/spec_helper.rb +61 -0
  149. data/spec/sphinx_helper.rb +60 -0
  150. data/spec/support/rails.rb +25 -0
  151. data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
  152. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
  153. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  154. data/spec/thinking_sphinx/active_record_spec.rb +573 -0
  155. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
  156. data/spec/thinking_sphinx/association_spec.rb +250 -0
  157. data/spec/thinking_sphinx/attribute_spec.rb +552 -0
  158. data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
  159. data/spec/thinking_sphinx/configuration_spec.rb +326 -0
  160. data/spec/thinking_sphinx/context_spec.rb +126 -0
  161. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  162. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  163. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  164. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  165. data/spec/thinking_sphinx/facet_spec.rb +359 -0
  166. data/spec/thinking_sphinx/field_spec.rb +127 -0
  167. data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
  168. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  169. data/spec/thinking_sphinx/index_spec.rb +189 -0
  170. data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
  171. data/spec/thinking_sphinx/search_spec.rb +1455 -0
  172. data/spec/thinking_sphinx/source_spec.rb +267 -0
  173. data/spec/thinking_sphinx/test_spec.rb +20 -0
  174. data/spec/thinking_sphinx_spec.rb +204 -0
  175. metadata +524 -0
@@ -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
+ add_internal_attributes_and_facets
31
+ end
32
+
33
+ def name
34
+ index.name
35
+ end
36
+
37
+ def to_riddle_for_core(offset, position)
38
+ source = Riddle::Configuration::SQLSource.new(
39
+ "#{index.core_name}_#{position}", adapter.sphinx_identifier
40
+ )
41
+
42
+ set_source_database_settings source
43
+ set_source_fields source
44
+ set_source_attributes source, offset
45
+ set_source_settings source
46
+ set_source_sql source, offset
47
+
48
+ source
49
+ end
50
+
51
+ def to_riddle_for_delta(offset, position)
52
+ source = Riddle::Configuration::SQLSource.new(
53
+ "#{index.delta_name}_#{position}", adapter.sphinx_identifier
54
+ )
55
+ source.parent = "#{index.core_name}_#{position}"
56
+
57
+ set_source_database_settings source
58
+ set_source_fields source
59
+ set_source_attributes source, offset, true
60
+ set_source_settings source
61
+ set_source_sql source, offset, true
62
+
63
+ source
64
+ end
65
+
66
+ def delta?
67
+ !@index.delta_object.nil?
68
+ end
69
+
70
+ # Gets the association stack for a specific key.
71
+ #
72
+ def association(key)
73
+ @associations[key] ||= Association.children(@model, key)
74
+ end
75
+
76
+ private
77
+
78
+ def adapter
79
+ @adapter ||= @model.sphinx_database_adapter
80
+ end
81
+
82
+ def available_attributes
83
+ attributes.select { |attrib| attrib.available? }
84
+ end
85
+
86
+ def set_source_database_settings(source)
87
+ config = @database_configuration
88
+
89
+ source.sql_host = config[:host] || "localhost"
90
+ source.sql_user = config[:username] || config[:user] || ENV['USER']
91
+ source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
92
+ source.sql_db = config[:database]
93
+ source.sql_port = config[:port]
94
+ source.sql_sock = config[:socket]
95
+
96
+ # MySQL SSL support
97
+ source.mysql_ssl_ca = config[:sslca] if config[:sslca]
98
+ source.mysql_ssl_cert = config[:sslcert] if config[:sslcert]
99
+ source.mysql_ssl_key = config[:sslkey] if config[:sslkey]
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,142 @@
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
+ if ENV["NODETACH"] == "true"
37
+ unless pid = fork
38
+ config.controller.start(:nodetach => true)
39
+ end
40
+ Signal.trap('TERM') { Process.kill(:QUIT, pid) }
41
+ Signal.trap('INT') { Process.kill(:QUIT, pid) }
42
+ Process.wait(pid)
43
+ else
44
+ config.controller.start
45
+
46
+ if sphinx_running?
47
+ puts "Started successfully (pid #{sphinx_pid})."
48
+ else
49
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}"
50
+ puts "Be sure to run thinking_sphinx:index before thinking_sphinx:start"
51
+ end
52
+ end
53
+ end
54
+
55
+ desc "Stop Sphinx using Thinking Sphinx's settings"
56
+ task :stop => :app_env do
57
+ unless sphinx_running?
58
+ puts "searchd is not running"
59
+ else
60
+ config = ThinkingSphinx::Configuration.instance
61
+ pid = sphinx_pid
62
+ config.controller.stop
63
+
64
+ # Ensure searchd is stopped, but don't try too hard
65
+ Timeout.timeout(config.stop_timeout) do
66
+ sleep(1) until config.controller.stop
67
+ end
68
+
69
+ puts "Stopped search daemon (pid #{pid})."
70
+ end
71
+ end
72
+
73
+ desc "Restart Sphinx"
74
+ task :restart => [:app_env, :stop, :start]
75
+
76
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
77
+ task :configure => :app_env do
78
+ config = ThinkingSphinx::Configuration.instance
79
+ puts "Generating Configuration to #{config.config_file}"
80
+ config.build
81
+ end
82
+
83
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
84
+ task :index => :app_env do
85
+ config = ThinkingSphinx::Configuration.instance
86
+ unless ENV["INDEX_ONLY"] == "true"
87
+ puts "Generating Configuration to #{config.config_file}"
88
+ config.build
89
+ end
90
+
91
+ FileUtils.mkdir_p config.searchd_file_path
92
+ config.controller.index :verbose => true
93
+ end
94
+
95
+ desc "Reindex Sphinx without regenerating the configuration file"
96
+ task :reindex => :app_env do
97
+ config = ThinkingSphinx::Configuration.instance
98
+ FileUtils.mkdir_p config.searchd_file_path
99
+ output = config.controller.index
100
+ puts output
101
+ config.touch_reindex_file(output)
102
+ end
103
+
104
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
105
+ task :rebuild => :app_env do
106
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
107
+ Rake::Task["thinking_sphinx:index"].invoke
108
+ Rake::Task["thinking_sphinx:start"].invoke
109
+ end
110
+ end
111
+
112
+ namespace :ts do
113
+ desc "Output the current Thinking Sphinx version"
114
+ task :version => "thinking_sphinx:version"
115
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
116
+ task :run => "thinking_sphinx:running_start"
117
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
118
+ task :start => "thinking_sphinx:start"
119
+ desc "Stop Sphinx using Thinking Sphinx's settings"
120
+ task :stop => "thinking_sphinx:stop"
121
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
122
+ task :in => "thinking_sphinx:index"
123
+ task :index => "thinking_sphinx:index"
124
+ desc "Reindex Sphinx without regenerating the configuration file"
125
+ task :reindex => "thinking_sphinx:reindex"
126
+ desc "Restart Sphinx"
127
+ task :restart => "thinking_sphinx:restart"
128
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
129
+ task :conf => "thinking_sphinx:configure"
130
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
131
+ task :config => "thinking_sphinx:configure"
132
+ desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
133
+ task :rebuild => "thinking_sphinx:rebuild"
134
+ end
135
+
136
+ def sphinx_pid
137
+ ThinkingSphinx.sphinx_pid
138
+ end
139
+
140
+ def sphinx_running?
141
+ ThinkingSphinx.sphinx_running?
142
+ 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.13'
3
+ end
@@ -0,0 +1,297 @@
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
+ require 'thinking_sphinx/version'
27
+
28
+ require 'thinking_sphinx/adapters/abstract_adapter'
29
+ require 'thinking_sphinx/adapters/mysql_adapter'
30
+ require 'thinking_sphinx/adapters/postgresql_adapter'
31
+
32
+ require 'thinking_sphinx/railtie' if defined?(Rails)
33
+
34
+ module ThinkingSphinx
35
+ mattr_accessor :database_adapter
36
+
37
+ # A ConnectionError will get thrown when a connection to Sphinx can't be
38
+ # made.
39
+ class ConnectionError < StandardError
40
+ end
41
+
42
+ # A StaleIdsException is thrown by Collection.instances_from_matches if there
43
+ # are records in Sphinx but not in the database, so the search can be retried.
44
+ class StaleIdsException < StandardError
45
+ attr_accessor :ids
46
+ def initialize(ids)
47
+ self.ids = ids
48
+ end
49
+ end
50
+
51
+ # A SphinxError occurs when Sphinx responds with an error due to problematic
52
+ # queries or indexes.
53
+ class SphinxError < RuntimeError
54
+ attr_accessor :results
55
+ def initialize(message = nil, results = nil)
56
+ super(message)
57
+ self.results = results
58
+ end
59
+ end
60
+
61
+ # The collection of indexed models. Keep in mind that Rails lazily loads
62
+ # its classes, so this may not actually be populated with _all_ the models
63
+ # that have Sphinx indexes.
64
+ @@sphinx_mutex = Mutex.new
65
+ @@context = nil
66
+ @@define_indexes = true
67
+ @@deltas_enabled = nil
68
+ @@updates_enabled = nil
69
+ @@suppress_delta_output = false
70
+ @@remote_sphinx = false
71
+ @@use_group_by_shortcut = nil
72
+
73
+ def self.mutex
74
+ @@sphinx_mutex
75
+ end
76
+
77
+ def self.context
78
+ if @@context.nil?
79
+ mutex.synchronize do
80
+ if @@context.nil?
81
+ @@context = ThinkingSphinx::Context.new
82
+ @@context.prepare
83
+ end
84
+ end
85
+ end
86
+
87
+ @@context
88
+ end
89
+
90
+ def self.reset_context!(context = nil)
91
+ mutex.synchronize do
92
+ @@context = context
93
+ end
94
+ end
95
+
96
+ def self.unique_id_expression(adapter, offset = nil)
97
+ "* #{adapter.cast_to_int context.indexed_models.size} + #{offset || 0}"
98
+ end
99
+
100
+ # Check if index definition is disabled.
101
+ #
102
+ def self.define_indexes?
103
+ @@define_indexes
104
+ end
105
+
106
+ # Enable/disable indexes - you may want to do this while migrating data.
107
+ #
108
+ # ThinkingSphinx.define_indexes = false
109
+ #
110
+ def self.define_indexes=(value)
111
+ mutex.synchronize do
112
+ @@define_indexes = value
113
+ end
114
+ end
115
+
116
+ # Check if delta indexing is enabled/disabled.
117
+ #
118
+ def self.deltas_enabled?
119
+ if @@deltas_enabled.nil?
120
+ mutex.synchronize do
121
+ if @@deltas_enabled.nil?
122
+ @@deltas_enabled = (
123
+ ThinkingSphinx::Configuration.environment != "test"
124
+ )
125
+ end
126
+ end
127
+ end
128
+
129
+ @@deltas_enabled && !deltas_suspended?
130
+ end
131
+
132
+ # Enable/disable delta indexing.
133
+ #
134
+ # ThinkingSphinx.deltas_enabled = false
135
+ #
136
+ def self.deltas_enabled=(value)
137
+ mutex.synchronize do
138
+ @@deltas_enabled = value
139
+ end
140
+ end
141
+
142
+ # Check if delta indexing is suspended.
143
+ #
144
+ def self.deltas_suspended?
145
+ if Thread.current[:thinking_sphinx_deltas_suspended].nil?
146
+ Thread.current[:thinking_sphinx_deltas_suspended] = false
147
+ end
148
+
149
+ Thread.current[:thinking_sphinx_deltas_suspended]
150
+ end
151
+
152
+ # Suspend/resume delta indexing.
153
+ #
154
+ # ThinkingSphinx.deltas_suspended = false
155
+ #
156
+ def self.deltas_suspended=(value)
157
+ Thread.current[:thinking_sphinx_deltas_suspended] = value
158
+ end
159
+
160
+ # Check if updates are enabled. True by default, unless within the test
161
+ # environment.
162
+ #
163
+ def self.updates_enabled?
164
+ if @@updates_enabled.nil?
165
+ mutex.synchronize do
166
+ if @@updates_enabled.nil?
167
+ @@updates_enabled = (
168
+ ThinkingSphinx::Configuration.environment != "test"
169
+ )
170
+ end
171
+ end
172
+ end
173
+
174
+ @@updates_enabled
175
+ end
176
+
177
+ # Enable/disable updates to Sphinx
178
+ #
179
+ # ThinkingSphinx.updates_enabled = false
180
+ #
181
+ def self.updates_enabled=(value)
182
+ mutex.synchronize do
183
+ @@updates_enabled = value
184
+ end
185
+ end
186
+
187
+ def self.suppress_delta_output?
188
+ @@suppress_delta_output
189
+ end
190
+
191
+ def self.suppress_delta_output=(value)
192
+ mutex.synchronize do
193
+ @@suppress_delta_output = value
194
+ end
195
+ end
196
+
197
+ # Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
198
+ # or if not using MySQL, this will return false.
199
+ #
200
+ def self.use_group_by_shortcut?
201
+ if @@use_group_by_shortcut.nil?
202
+ mutex.synchronize do
203
+ if @@use_group_by_shortcut.nil?
204
+ @@use_group_by_shortcut = !!(
205
+ mysql? && ::ActiveRecord::Base.connection.select_all(
206
+ "SELECT @@global.sql_mode, @@session.sql_mode;"
207
+ ).all? { |key, value|
208
+ value.nil? || value[/ONLY_FULL_GROUP_BY/].nil?
209
+ }
210
+ )
211
+ end
212
+ end
213
+ end
214
+
215
+ @@use_group_by_shortcut
216
+ end
217
+
218
+ def self.reset_use_group_by_shortcut
219
+ mutex.synchronize do
220
+ @@use_group_by_shortcut = nil
221
+ end
222
+ end
223
+
224
+ # An indication of whether Sphinx is running on a remote machine instead of
225
+ # the same machine.
226
+ #
227
+ def self.remote_sphinx?
228
+ @@remote_sphinx
229
+ end
230
+
231
+ # Tells Thinking Sphinx that Sphinx is running on a different machine, and
232
+ # thus it can't reliably guess whether it is running or not (ie: the
233
+ # #sphinx_running? method), and so just assumes it is.
234
+ #
235
+ # Useful for multi-machine deployments. Set it in your production.rb file.
236
+ #
237
+ # ThinkingSphinx.remote_sphinx = true
238
+ #
239
+ def self.remote_sphinx=(value)
240
+ mutex.synchronize do
241
+ @@remote_sphinx = value
242
+ end
243
+ end
244
+
245
+ # Check if Sphinx is running. If remote_sphinx is set to true (indicating
246
+ # Sphinx is on a different machine), this will always return true, and you
247
+ # will have to handle any connection errors yourself.
248
+ #
249
+ def self.sphinx_running?
250
+ remote_sphinx? || sphinx_running_by_pid?
251
+ end
252
+
253
+ # Check if Sphinx is actually running, provided the pid is on the same
254
+ # machine as this code.
255
+ #
256
+ def self.sphinx_running_by_pid?
257
+ !!sphinx_pid && pid_active?(sphinx_pid)
258
+ end
259
+
260
+ def self.sphinx_pid
261
+ if File.exists?(ThinkingSphinx::Configuration.instance.pid_file)
262
+ File.read(ThinkingSphinx::Configuration.instance.pid_file)[/\d+/]
263
+ else
264
+ nil
265
+ end
266
+ end
267
+
268
+ def self.pid_active?(pid)
269
+ !!Process.kill(0, pid.to_i)
270
+ rescue Errno::EPERM => e
271
+ true
272
+ rescue Exception => e
273
+ false
274
+ end
275
+
276
+ def self.microsoft?
277
+ RUBY_PLATFORM =~ /mswin/
278
+ end
279
+
280
+ def self.jruby?
281
+ defined?(JRUBY_VERSION)
282
+ end
283
+
284
+ def self.mysql?
285
+ ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlAdapter" ||
286
+ ::ActiveRecord::Base.connection.class.name.demodulize == "Mysql2Adapter" ||
287
+ ::ActiveRecord::Base.connection.class.name.demodulize == "MysqlplusAdapter" || (
288
+ jruby? && ::ActiveRecord::Base.connection.config[:adapter] == "jdbcmysql"
289
+ )
290
+ end
291
+
292
+ def self.rails_3_1?
293
+ !!defined?(::ActiveRecord::Associations::CollectionProxy)
294
+ end
295
+
296
+ extend ThinkingSphinx::SearchMethods::ClassMethods
297
+ end