friendlyfashion-thinking-sphinx 2.0.13

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