thinking-sphinx 1.2.13 → 1.4.0

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 (204) hide show
  1. data/README.textile +37 -4
  2. data/VERSION +1 -0
  3. data/features/abstract_inheritance.feature +10 -0
  4. data/features/alternate_primary_key.feature +1 -1
  5. data/features/attribute_updates.feature +49 -5
  6. data/features/deleting_instances.feature +3 -0
  7. data/features/excerpts.feature +8 -0
  8. data/features/facets.feature +15 -1
  9. data/features/facets_across_model.feature +2 -2
  10. data/features/field_sorting.feature +18 -0
  11. data/features/handling_edits.feature +1 -1
  12. data/features/searching_across_models.feature +2 -2
  13. data/features/searching_by_index.feature +40 -0
  14. data/features/searching_by_model.feature +1 -8
  15. data/features/sphinx_scopes.feature +33 -0
  16. data/features/step_definitions/alpha_steps.rb +14 -1
  17. data/features/step_definitions/beta_steps.rb +1 -1
  18. data/features/step_definitions/common_steps.rb +21 -2
  19. data/features/step_definitions/facet_steps.rb +4 -0
  20. data/features/step_definitions/scope_steps.rb +8 -0
  21. data/features/step_definitions/search_steps.rb +5 -0
  22. data/features/step_definitions/sphinx_steps.rb +8 -4
  23. data/features/sti_searching.feature +5 -0
  24. data/features/support/env.rb +7 -6
  25. data/features/{support → thinking_sphinx}/db/fixtures/betas.rb +1 -0
  26. data/features/{support → thinking_sphinx}/db/fixtures/comments.rb +1 -1
  27. data/features/{support → thinking_sphinx}/db/fixtures/developers.rb +2 -0
  28. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  29. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  30. data/features/{support → thinking_sphinx}/db/fixtures/people.rb +1 -1
  31. data/features/{support → thinking_sphinx}/db/fixtures/tags.rb +1 -1
  32. data/features/{support → thinking_sphinx}/db/migrations/create_alphas.rb +1 -0
  33. data/features/{support → thinking_sphinx}/db/migrations/create_developers.rb +0 -2
  34. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  35. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  36. data/features/thinking_sphinx/models/alpha.rb +23 -0
  37. data/features/thinking_sphinx/models/andrew.rb +17 -0
  38. data/features/{support → thinking_sphinx}/models/beta.rb +1 -1
  39. data/features/{support → thinking_sphinx}/models/developer.rb +2 -2
  40. data/features/{support → thinking_sphinx}/models/extensible_beta.rb +1 -1
  41. data/features/thinking_sphinx/models/fox.rb +5 -0
  42. data/features/thinking_sphinx/models/genre.rb +3 -0
  43. data/features/thinking_sphinx/models/medium.rb +5 -0
  44. data/features/thinking_sphinx/models/music.rb +8 -0
  45. data/features/{support → thinking_sphinx}/models/person.rb +2 -1
  46. data/features/{support → thinking_sphinx}/models/post.rb +2 -1
  47. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  48. data/lib/cucumber/thinking_sphinx/internal_world.rb +13 -11
  49. data/lib/thinking_sphinx/active_record/attribute_updates.rb +17 -15
  50. data/lib/thinking_sphinx/active_record/delta.rb +0 -26
  51. data/lib/thinking_sphinx/active_record/has_many_association.rb +34 -11
  52. data/lib/thinking_sphinx/active_record/scopes.rb +46 -3
  53. data/lib/thinking_sphinx/active_record.rb +271 -193
  54. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +45 -9
  55. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +5 -1
  56. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +9 -1
  57. data/lib/thinking_sphinx/attribute.rb +67 -23
  58. data/lib/thinking_sphinx/auto_version.rb +24 -0
  59. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  60. data/lib/thinking_sphinx/class_facet.rb +3 -2
  61. data/lib/thinking_sphinx/configuration.rb +78 -64
  62. data/lib/thinking_sphinx/context.rb +76 -0
  63. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  64. data/lib/thinking_sphinx/deltas.rb +0 -2
  65. data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
  66. data/lib/thinking_sphinx/excerpter.rb +1 -1
  67. data/lib/thinking_sphinx/facet.rb +6 -5
  68. data/lib/thinking_sphinx/facet_search.rb +54 -24
  69. data/lib/thinking_sphinx/field.rb +2 -4
  70. data/lib/thinking_sphinx/index/builder.rb +36 -20
  71. data/lib/thinking_sphinx/index/faux_column.rb +8 -0
  72. data/lib/thinking_sphinx/index.rb +77 -19
  73. data/lib/thinking_sphinx/join.rb +37 -0
  74. data/lib/thinking_sphinx/property.rb +9 -2
  75. data/lib/thinking_sphinx/rails_additions.rb +4 -4
  76. data/lib/thinking_sphinx/search.rb +212 -66
  77. data/lib/thinking_sphinx/search_methods.rb +22 -4
  78. data/lib/thinking_sphinx/source/internal_properties.rb +2 -2
  79. data/lib/thinking_sphinx/source/sql.rb +5 -3
  80. data/lib/thinking_sphinx/source.rb +21 -12
  81. data/lib/thinking_sphinx/tasks.rb +26 -58
  82. data/lib/thinking_sphinx/test.rb +55 -0
  83. data/lib/thinking_sphinx.rb +70 -38
  84. data/rails/init.rb +4 -2
  85. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +6 -8
  86. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +26 -3
  87. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  88. data/spec/thinking_sphinx/active_record_spec.rb +618 -0
  89. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +134 -0
  90. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +1 -1
  91. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +87 -46
  92. data/spec/thinking_sphinx/auto_version_spec.rb +47 -0
  93. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +73 -63
  94. data/spec/thinking_sphinx/context_spec.rb +127 -0
  95. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +1 -1
  96. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +1 -1
  97. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +1 -9
  98. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +76 -82
  99. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +5 -5
  100. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +1 -42
  101. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +71 -31
  102. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +8 -2
  103. data/spec/thinking_sphinx/index_spec.rb +183 -0
  104. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +5 -5
  105. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +5 -1
  106. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +183 -31
  107. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +18 -2
  108. data/spec/thinking_sphinx/test_spec.rb +20 -0
  109. data/spec/thinking_sphinx_spec.rb +204 -0
  110. data/tasks/distribution.rb +7 -26
  111. data/tasks/testing.rb +32 -20
  112. metadata +488 -147
  113. data/VERSION.yml +0 -5
  114. data/features/datetime_deltas.feature +0 -66
  115. data/features/delayed_delta_indexing.feature +0 -37
  116. data/features/step_definitions/datetime_delta_steps.rb +0 -15
  117. data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
  118. data/features/support/database.yml +0 -5
  119. data/features/support/db/active_record.rb +0 -40
  120. data/features/support/db/database.yml +0 -5
  121. data/features/support/db/fixtures/delayed_betas.rb +0 -10
  122. data/features/support/db/fixtures/thetas.rb +0 -10
  123. data/features/support/db/migrations/create_delayed_betas.rb +0 -17
  124. data/features/support/db/migrations/create_thetas.rb +0 -5
  125. data/features/support/db/mysql.rb +0 -3
  126. data/features/support/db/postgresql.rb +0 -3
  127. data/features/support/models/alpha.rb +0 -10
  128. data/features/support/models/delayed_beta.rb +0 -7
  129. data/features/support/models/theta.rb +0 -7
  130. data/features/support/post_database.rb +0 -43
  131. data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
  132. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
  133. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
  134. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
  135. data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
  136. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
  137. data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
  138. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
  139. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
  140. data/spec/lib/thinking_sphinx_spec.rb +0 -162
  141. data/vendor/after_commit/LICENSE +0 -20
  142. data/vendor/after_commit/README +0 -16
  143. data/vendor/after_commit/Rakefile +0 -22
  144. data/vendor/after_commit/init.rb +0 -8
  145. data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
  146. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
  147. data/vendor/after_commit/lib/after_commit.rb +0 -45
  148. data/vendor/after_commit/test/after_commit_test.rb +0 -53
  149. data/vendor/delayed_job/lib/delayed/job.rb +0 -251
  150. data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
  151. data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
  152. data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
  153. data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
  154. data/vendor/riddle/lib/riddle/client/message.rb +0 -66
  155. data/vendor/riddle/lib/riddle/client/response.rb +0 -84
  156. data/vendor/riddle/lib/riddle/client.rb +0 -635
  157. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
  158. data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
  159. data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
  160. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
  161. data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
  162. data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
  163. data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
  164. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
  165. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
  166. data/vendor/riddle/lib/riddle/configuration.rb +0 -33
  167. data/vendor/riddle/lib/riddle/controller.rb +0 -53
  168. data/vendor/riddle/lib/riddle.rb +0 -30
  169. data/features/{support → thinking_sphinx}/database.example.yml +0 -0
  170. data/features/{support → thinking_sphinx}/db/fixtures/alphas.rb +0 -0
  171. data/features/{support → thinking_sphinx}/db/fixtures/authors.rb +0 -0
  172. data/features/{support → thinking_sphinx}/db/fixtures/boxes.rb +0 -0
  173. data/features/{support → thinking_sphinx}/db/fixtures/categories.rb +0 -0
  174. data/features/{support → thinking_sphinx}/db/fixtures/cats.rb +0 -0
  175. data/features/{support → thinking_sphinx}/db/fixtures/dogs.rb +0 -0
  176. data/features/{support → thinking_sphinx}/db/fixtures/extensible_betas.rb +0 -0
  177. data/features/{support → thinking_sphinx}/db/fixtures/gammas.rb +0 -0
  178. data/features/{support → thinking_sphinx}/db/fixtures/posts.rb +0 -0
  179. data/features/{support → thinking_sphinx}/db/fixtures/robots.rb +0 -0
  180. data/features/{support → thinking_sphinx}/db/migrations/create_animals.rb +0 -0
  181. data/features/{support → thinking_sphinx}/db/migrations/create_authors.rb +0 -0
  182. data/features/{support → thinking_sphinx}/db/migrations/create_authors_posts.rb +0 -0
  183. data/features/{support → thinking_sphinx}/db/migrations/create_betas.rb +0 -0
  184. data/features/{support → thinking_sphinx}/db/migrations/create_boxes.rb +0 -0
  185. data/features/{support → thinking_sphinx}/db/migrations/create_categories.rb +0 -0
  186. data/features/{support → thinking_sphinx}/db/migrations/create_comments.rb +0 -0
  187. data/features/{support → thinking_sphinx}/db/migrations/create_extensible_betas.rb +0 -0
  188. data/features/{support → thinking_sphinx}/db/migrations/create_gammas.rb +0 -0
  189. data/features/{support → thinking_sphinx}/db/migrations/create_people.rb +0 -0
  190. data/features/{support → thinking_sphinx}/db/migrations/create_posts.rb +0 -0
  191. data/features/{support → thinking_sphinx}/db/migrations/create_robots.rb +0 -0
  192. data/features/{support → thinking_sphinx}/db/migrations/create_taggings.rb +0 -0
  193. data/features/{support → thinking_sphinx}/db/migrations/create_tags.rb +0 -0
  194. data/features/{support → thinking_sphinx}/models/animal.rb +0 -0
  195. data/features/{support → thinking_sphinx}/models/author.rb +0 -0
  196. data/features/{support → thinking_sphinx}/models/box.rb +0 -0
  197. data/features/{support → thinking_sphinx}/models/cat.rb +0 -0
  198. data/features/{support → thinking_sphinx}/models/category.rb +0 -0
  199. data/features/{support → thinking_sphinx}/models/comment.rb +3 -3
  200. /data/features/{support → thinking_sphinx}/models/dog.rb +0 -0
  201. /data/features/{support → thinking_sphinx}/models/gamma.rb +0 -0
  202. /data/features/{support → thinking_sphinx}/models/robot.rb +0 -0
  203. /data/features/{support → thinking_sphinx}/models/tag.rb +0 -0
  204. /data/features/{support → thinking_sphinx}/models/tagging.rb +0 -0
@@ -1,10 +1,16 @@
1
1
  require 'fileutils'
2
+ require 'timeout'
2
3
 
3
4
  namespace :thinking_sphinx do
4
5
  task :app_env do
5
6
  if defined?(RAILS_ROOT)
6
7
  Rake::Task[:environment].invoke
7
- Rails.configuration.cache_classes = false
8
+
9
+ if defined?(Rails.configuration)
10
+ Rails.configuration.cache_classes = false
11
+ else
12
+ Rails::Initializer.run { |config| config.cache_classes = false }
13
+ end
8
14
  end
9
15
 
10
16
  Rake::Task[:merb_env].invoke if defined?(Merb)
@@ -29,10 +35,8 @@ namespace :thinking_sphinx do
29
35
  raise RuntimeError, "searchd is already running." if sphinx_running?
30
36
 
31
37
  Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
32
-
33
- system! "#{config.bin_path}#{config.searchd_binary_name} --pidfile --config \"#{config.config_file}\""
34
38
 
35
- sleep(2)
39
+ config.controller.start
36
40
 
37
41
  if sphinx_running?
38
42
  puts "Started successfully (pid #{sphinx_pid})."
@@ -48,7 +52,13 @@ namespace :thinking_sphinx do
48
52
  else
49
53
  config = ThinkingSphinx::Configuration.instance
50
54
  pid = sphinx_pid
51
- system! "#{config.bin_path}#{config.searchd_binary_name} --stop --config \"#{config.config_file}\""
55
+ config.controller.stop
56
+
57
+ # Ensure searchd is stopped, but don't try too hard
58
+ Timeout.timeout(5) do
59
+ sleep(1) until config.controller.stop
60
+ end
61
+
52
62
  puts "Stopped search daemon (pid #{pid})."
53
63
  end
54
64
  end
@@ -65,19 +75,21 @@ namespace :thinking_sphinx do
65
75
 
66
76
  desc "Index data for Sphinx using Thinking Sphinx's settings"
67
77
  task :index => :app_env do
68
- ThinkingSphinx::Deltas::Job.cancel_thinking_sphinx_jobs
69
-
70
78
  config = ThinkingSphinx::Configuration.instance
71
79
  unless ENV["INDEX_ONLY"] == "true"
72
80
  puts "Generating Configuration to #{config.config_file}"
73
81
  config.build
74
82
  end
75
-
76
- FileUtils.mkdir_p config.searchd_file_path
77
- cmd = "#{config.bin_path}#{config.indexer_binary_name} --config \"#{config.config_file}\" --all"
78
- cmd << " --rotate" if sphinx_running?
79
83
 
80
- system! cmd
84
+ FileUtils.mkdir_p config.searchd_file_path
85
+ config.controller.index :verbose => true
86
+ end
87
+
88
+ desc "Reindex Sphinx without regenerating the configuration file"
89
+ task :reindex => :app_env do
90
+ config = ThinkingSphinx::Configuration.instance
91
+ FileUtils.mkdir_p config.searchd_file_path
92
+ puts config.controller.index
81
93
  end
82
94
 
83
95
  desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
@@ -86,30 +98,6 @@ namespace :thinking_sphinx do
86
98
  Rake::Task["thinking_sphinx:index"].invoke
87
99
  Rake::Task["thinking_sphinx:start"].invoke
88
100
  end
89
-
90
- namespace :index do
91
- task :delta => :app_env do
92
- ThinkingSphinx.indexed_models.select { |model|
93
- model.constantize.sphinx_indexes.any? { |index| index.delta? }
94
- }.each do |model|
95
- model.constantize.sphinx_indexes.select { |index|
96
- index.delta? && index.delta_object.respond_to?(:delayed_index)
97
- }.each { |index|
98
- index.delta_object.delayed_index(index.model)
99
- }
100
- end
101
- end
102
- end
103
-
104
- desc "Process stored delta index requests"
105
- task :delayed_delta => :app_env do
106
- require 'delayed/worker'
107
-
108
- Delayed::Worker.new(
109
- :min_priority => ENV['MIN_PRIORITY'],
110
- :max_priority => ENV['MAX_PRIORITY']
111
- ).start
112
- end
113
101
  end
114
102
 
115
103
  namespace :ts do
@@ -123,11 +111,9 @@ namespace :ts do
123
111
  task :stop => "thinking_sphinx:stop"
124
112
  desc "Index data for Sphinx using Thinking Sphinx's settings"
125
113
  task :in => "thinking_sphinx:index"
126
- namespace :in do
127
- desc "Index Thinking Sphinx datetime delta indexes"
128
- task :delta => "thinking_sphinx:index:delta"
129
- end
130
114
  task :index => "thinking_sphinx:index"
115
+ desc "Reindex Sphinx without regenerating the configuration file"
116
+ task :reindex => "thinking_sphinx:reindex"
131
117
  desc "Restart Sphinx"
132
118
  task :restart => "thinking_sphinx:restart"
133
119
  desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
@@ -136,8 +122,6 @@ namespace :ts do
136
122
  task :config => "thinking_sphinx:configure"
137
123
  desc "Stop Sphinx (if it's running), rebuild the indexes, and start Sphinx"
138
124
  task :rebuild => "thinking_sphinx:rebuild"
139
- desc "Process stored delta index requests"
140
- task :dd => "thinking_sphinx:delayed_delta"
141
125
  end
142
126
 
143
127
  def sphinx_pid
@@ -147,19 +131,3 @@ end
147
131
  def sphinx_running?
148
132
  ThinkingSphinx.sphinx_running?
149
133
  end
150
-
151
- # a fail-fast, hopefully helpful version of system
152
- def system!(cmd)
153
- unless system(cmd)
154
- raise <<-SYSTEM_CALL_FAILED
155
- The following command failed:
156
- #{cmd}
157
-
158
- This could be caused by a PATH issue in the environment of cron/passenger/etc. Your current PATH:
159
- #{ENV['PATH']}
160
- You can set the path to your indexer and searchd binaries using the bin_path property in config/sphinx.yml:
161
- production:
162
- bin_path: '/usr/local/bin'
163
- SYSTEM_CALL_FAILED
164
- end
165
- 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
@@ -1,26 +1,25 @@
1
- Dir[File.join(File.dirname(__FILE__), '../vendor/*/lib')].each do |path|
2
- $LOAD_PATH.unshift path
3
- end
4
-
5
1
  require 'active_record'
6
- require 'riddle'
7
2
  require 'after_commit'
8
3
  require 'yaml'
9
- require 'cgi'
4
+ require 'riddle'
10
5
 
6
+ require 'thinking_sphinx/auto_version'
11
7
  require 'thinking_sphinx/core/array'
12
8
  require 'thinking_sphinx/core/string'
13
9
  require 'thinking_sphinx/property'
14
10
  require 'thinking_sphinx/active_record'
15
11
  require 'thinking_sphinx/association'
16
12
  require 'thinking_sphinx/attribute'
13
+ require 'thinking_sphinx/bundled_search'
17
14
  require 'thinking_sphinx/configuration'
15
+ require 'thinking_sphinx/context'
18
16
  require 'thinking_sphinx/excerpter'
19
17
  require 'thinking_sphinx/facet'
20
18
  require 'thinking_sphinx/class_facet'
21
19
  require 'thinking_sphinx/facet_search'
22
20
  require 'thinking_sphinx/field'
23
21
  require 'thinking_sphinx/index'
22
+ require 'thinking_sphinx/join'
24
23
  require 'thinking_sphinx/source'
25
24
  require 'thinking_sphinx/rails_additions'
26
25
  require 'thinking_sphinx/search'
@@ -38,6 +37,8 @@ Merb::Plugins.add_rakefiles(
38
37
  ) if defined?(Merb)
39
38
 
40
39
  module ThinkingSphinx
40
+ mattr_accessor :database_adapter
41
+
41
42
  # A ConnectionError will get thrown when a connection to Sphinx can't be
42
43
  # made.
43
44
  class ConnectionError < StandardError
@@ -57,26 +58,48 @@ module ThinkingSphinx
57
58
  # @return [String] The version number as a string
58
59
  #
59
60
  def self.version
60
- hash = YAML.load_file File.join(File.dirname(__FILE__), '../VERSION.yml')
61
- [hash[:major], hash[:minor], hash[:patch]].join('.')
61
+ open(File.join(File.dirname(__FILE__), '../VERSION')) { |f|
62
+ f.read.strip
63
+ }
62
64
  end
63
65
 
64
66
  # The collection of indexed models. Keep in mind that Rails lazily loads
65
67
  # its classes, so this may not actually be populated with _all_ the models
66
68
  # that have Sphinx indexes.
67
- def self.indexed_models
68
- @@indexed_models ||= []
69
+ @@sphinx_mutex = Mutex.new
70
+ @@context = nil
71
+
72
+ def self.context
73
+ if @@context.nil?
74
+ @@sphinx_mutex.synchronize do
75
+ if @@context.nil?
76
+ @@context = ThinkingSphinx::Context.new
77
+ @@context.prepare
78
+ end
79
+ end
80
+ end
81
+
82
+ @@context
83
+ end
84
+
85
+ def self.reset_context!
86
+ @@sphinx_mutex.synchronize do
87
+ @@context = nil
88
+ end
69
89
  end
70
90
 
71
91
  def self.unique_id_expression(offset = nil)
72
- "* #{ThinkingSphinx.indexed_models.size} + #{offset || 0}"
92
+ "* #{context.indexed_models.size} + #{offset || 0}"
73
93
  end
74
94
 
75
95
  # Check if index definition is disabled.
76
96
  #
77
97
  def self.define_indexes?
78
- @@define_indexes = true unless defined?(@@define_indexes)
79
- @@define_indexes == true
98
+ if Thread.current[:thinking_sphinx_define_indexes].nil?
99
+ Thread.current[:thinking_sphinx_define_indexes] = true
100
+ end
101
+
102
+ Thread.current[:thinking_sphinx_define_indexes]
80
103
  end
81
104
 
82
105
  # Enable/disable indexes - you may want to do this while migrating data.
@@ -84,16 +107,19 @@ module ThinkingSphinx
84
107
  # ThinkingSphinx.define_indexes = false
85
108
  #
86
109
  def self.define_indexes=(value)
87
- @@define_indexes = value
110
+ Thread.current[:thinking_sphinx_define_indexes] = value
88
111
  end
89
112
 
90
- @@deltas_enabled = nil
91
-
92
113
  # Check if delta indexing is enabled.
93
114
  #
94
115
  def self.deltas_enabled?
95
- @@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil?
96
- @@deltas_enabled
116
+ if Thread.current[:thinking_sphinx_deltas_enabled].nil?
117
+ Thread.current[:thinking_sphinx_deltas_enabled] = (
118
+ ThinkingSphinx::Configuration.environment != "test"
119
+ )
120
+ end
121
+
122
+ Thread.current[:thinking_sphinx_deltas_enabled]
97
123
  end
98
124
 
99
125
  # Enable/disable all delta indexing.
@@ -101,17 +127,20 @@ module ThinkingSphinx
101
127
  # ThinkingSphinx.deltas_enabled = false
102
128
  #
103
129
  def self.deltas_enabled=(value)
104
- @@deltas_enabled = value
130
+ Thread.current[:thinking_sphinx_deltas_enabled] = value
105
131
  end
106
132
 
107
- @@updates_enabled = nil
108
-
109
133
  # Check if updates are enabled. True by default, unless within the test
110
134
  # environment.
111
135
  #
112
136
  def self.updates_enabled?
113
- @@updates_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@updates_enabled.nil?
114
- @@updates_enabled
137
+ if Thread.current[:thinking_sphinx_updates_enabled].nil?
138
+ Thread.current[:thinking_sphinx_updates_enabled] = (
139
+ ThinkingSphinx::Configuration.environment != "test"
140
+ )
141
+ end
142
+
143
+ Thread.current[:thinking_sphinx_updates_enabled]
115
144
  end
116
145
 
117
146
  # Enable/disable updates to Sphinx
@@ -119,38 +148,37 @@ module ThinkingSphinx
119
148
  # ThinkingSphinx.updates_enabled = false
120
149
  #
121
150
  def self.updates_enabled=(value)
122
- @@updates_enabled = value
151
+ Thread.current[:thinking_sphinx_updates_enabled] = value
123
152
  end
124
153
 
125
- @@suppress_delta_output = false
126
-
127
154
  def self.suppress_delta_output?
128
- @@suppress_delta_output
155
+ Thread.current[:thinking_sphinx_suppress_delta_output] ||= false
129
156
  end
130
157
 
131
158
  def self.suppress_delta_output=(value)
132
- @@suppress_delta_output = value
159
+ Thread.current[:thinking_sphinx_suppress_delta_output] = value
133
160
  end
134
161
 
135
- @@use_group_by_shortcut = nil
136
162
  # Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
137
163
  # or if not using MySQL, this will return false.
138
164
  #
139
165
  def self.use_group_by_shortcut?
140
- @@use_group_by_shortcut ||= !!(
141
- mysql? && ::ActiveRecord::Base.connection.select_all(
142
- "SELECT @@global.sql_mode, @@session.sql_mode;"
143
- ).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
144
- )
166
+ if Thread.current[:thinking_sphinx_use_group_by_shortcut].nil?
167
+ Thread.current[:thinking_sphinx_use_group_by_shortcut] = !!(
168
+ mysql? && ::ActiveRecord::Base.connection.select_all(
169
+ "SELECT @@global.sql_mode, @@session.sql_mode;"
170
+ ).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
171
+ )
172
+ end
173
+
174
+ Thread.current[:thinking_sphinx_use_group_by_shortcut]
145
175
  end
146
176
 
147
- @@remote_sphinx = false
148
-
149
177
  # An indication of whether Sphinx is running on a remote machine instead of
150
178
  # the same machine.
151
179
  #
152
180
  def self.remote_sphinx?
153
- @@remote_sphinx
181
+ Thread.current[:thinking_sphinx_remote_sphinx] ||= false
154
182
  end
155
183
 
156
184
  # Tells Thinking Sphinx that Sphinx is running on a different machine, and
@@ -162,7 +190,7 @@ module ThinkingSphinx
162
190
  # ThinkingSphinx.remote_sphinx = true
163
191
  #
164
192
  def self.remote_sphinx=(value)
165
- @@remote_sphinx = value
193
+ Thread.current[:thinking_sphinx_remote_sphinx] = value
166
194
  end
167
195
 
168
196
  # Check if Sphinx is running. If remote_sphinx is set to true (indicating
@@ -190,6 +218,8 @@ module ThinkingSphinx
190
218
 
191
219
  def self.pid_active?(pid)
192
220
  !!Process.kill(0, pid.to_i)
221
+ rescue Errno::EPERM => e
222
+ true
193
223
  rescue Exception => e
194
224
  false
195
225
  end
@@ -211,3 +241,5 @@ module ThinkingSphinx
211
241
 
212
242
  extend ThinkingSphinx::SearchMethods::ClassMethods
213
243
  end
244
+
245
+ ThinkingSphinx::AutoVersion.detect
data/rails/init.rb CHANGED
@@ -1,3 +1,7 @@
1
+ Dir[File.join(File.dirname(__FILE__), '../vendor/*/lib')].each do |path|
2
+ $LOAD_PATH.unshift path
3
+ end
4
+
1
5
  require 'thinking_sphinx'
2
6
  require 'action_controller/dispatcher'
3
7
 
@@ -9,6 +13,4 @@ ActionController::Dispatcher.to_prepare :thinking_sphinx do
9
13
  elsif Rails::VERSION::STRING.to_f > 2.1
10
14
  I18n.backend.load_translations(*I18n.load_path)
11
15
  end
12
-
13
- ThinkingSphinx::Configuration.instance.load_models
14
16
  end
@@ -1,4 +1,4 @@
1
- require 'spec/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe "ThinkingSphinx::ActiveRecord::Delta" do
4
4
  it "should call the toggle_delta method after a save" do
@@ -73,14 +73,12 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
73
73
  Person.delta_object.stub!(:` => "", :toggled => true)
74
74
 
75
75
  @person = Person.new
76
- @person.stub!(
77
- :in_both_indexes? => false,
78
- :sphinx_document_id => 1
79
- )
76
+ Person.stub!(:search_for_id => false)
77
+ @person.stub!(:sphinx_document_id => 1)
80
78
 
81
79
  @client = Riddle::Client.new
82
80
  @client.stub!(:update => true)
83
- Riddle::Client.stub!(:new => @client)
81
+ ThinkingSphinx::Configuration.instance.stub!(:client => @client)
84
82
  end
85
83
 
86
84
  it "shouldn't index if delta indexing is disabled" do
@@ -108,7 +106,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
108
106
 
109
107
  it "should call indexer for the delta index" do
110
108
  Person.sphinx_indexes.first.delta_object.should_receive(:`).with(
111
- "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config #{ThinkingSphinx::Configuration.instance.config_file} --rotate person_delta"
109
+ "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config \"#{ThinkingSphinx::Configuration.instance.config_file}\" --rotate person_delta"
112
110
  )
113
111
 
114
112
  @person.send(:index_delta)
@@ -121,7 +119,7 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
121
119
  end
122
120
 
123
121
  it "should update the deleted attribute if in the core index" do
124
- @person.stub!(:in_both_indexes? => true)
122
+ Person.stub!(:search_for_id => true)
125
123
  @client.should_receive(:update)
126
124
 
127
125
  @person.send(:index_delta)
@@ -1,4 +1,4 @@
1
- require 'spec/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
4
4
  describe "search method" do
@@ -21,7 +21,13 @@ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
21
21
  end
22
22
 
23
23
  @person.friendships.search "test"
24
- end
24
+ end
25
+
26
+ it "should define indexes for the reflection class" do
27
+ Friendship.should_receive(:define_indexes)
28
+
29
+ @person.friendships.search 'test'
30
+ end
25
31
  end
26
32
 
27
33
  describe "search method for has_many :through" do
@@ -46,4 +52,21 @@ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
46
52
  @person.friends.search "test"
47
53
  end
48
54
  end
49
- end
55
+
56
+ describe 'filtering sphinx scopes' do
57
+ before :each do
58
+ Friendship.stub!(:search => Friendship)
59
+
60
+ @person = Person.find(:first)
61
+ end
62
+
63
+ it "should add a filter for the attribute in a sphinx scope call" do
64
+ Friendship.should_receive(:search).with do |options|
65
+ options[:with][:person_id].should == @person.id
66
+ Friendship
67
+ end
68
+
69
+ @person.friendships.reverse
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+
3
+ describe ThinkingSphinx::ActiveRecord::Scopes do
4
+ after :each do
5
+ Alpha.remove_sphinx_scopes
6
+ end
7
+
8
+ it "should be included into models with indexes" do
9
+ Alpha.included_modules.should include(ThinkingSphinx::ActiveRecord::Scopes)
10
+ end
11
+
12
+ it "should not be included into models without indexes" do
13
+ Gamma.included_modules.should_not include(
14
+ ThinkingSphinx::ActiveRecord::Scopes
15
+ )
16
+ end
17
+
18
+ describe '.sphinx_scope' do
19
+ before :each do
20
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
21
+ end
22
+
23
+ it "should define a method on the model" do
24
+ Alpha.should respond_to(:by_name)
25
+ end
26
+ end
27
+
28
+ describe '.sphinx_scopes' do
29
+ before :each do
30
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
31
+ end
32
+
33
+ it "should return an array of defined scope names as symbols" do
34
+ Alpha.sphinx_scopes.should == [:by_name]
35
+ end
36
+ end
37
+
38
+ describe '.default_sphinx_scope' do
39
+ before :each do
40
+ Alpha.sphinx_scope(:scope_used_as_default_scope) { {:conditions => {:name => 'name'}} }
41
+ Alpha.default_sphinx_scope :scope_used_as_default_scope
42
+ end
43
+
44
+ it "should return an array of defined scope names as symbols" do
45
+ Alpha.sphinx_scopes.should == [:scope_used_as_default_scope]
46
+ end
47
+
48
+ it "should have a default_sphinx_scope" do
49
+ Alpha.has_default_sphinx_scope?.should be_true
50
+ end
51
+ end
52
+
53
+ describe '.remove_sphinx_scopes' do
54
+ before :each do
55
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
56
+ Alpha.remove_sphinx_scopes
57
+ end
58
+
59
+ it "should remove sphinx scope methods" do
60
+ Alpha.should_not respond_to(:by_name)
61
+ end
62
+
63
+ it "should empty the list of sphinx scopes" do
64
+ Alpha.sphinx_scopes.should be_empty
65
+ end
66
+ end
67
+
68
+ describe '.example_default_scope' do
69
+ before :each do
70
+ Alpha.sphinx_scope(:foo_scope){ {:conditions => {:name => 'foo'}} }
71
+ Alpha.default_sphinx_scope :foo_scope
72
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
73
+ Alpha.sphinx_scope(:by_foo) { |foo| {:conditions => {:foo => foo}} }
74
+ end
75
+
76
+ it "should return a ThinkingSphinx::Search object" do
77
+ Alpha.search.should be_a(ThinkingSphinx::Search)
78
+ end
79
+
80
+ it "should apply the default scope options to the underlying search object" do
81
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
82
+ search.search.options[:conditions].should == {:name => 'foo'}
83
+ end
84
+
85
+ it "should apply the default scope options and scope options to the underlying search object" do
86
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
87
+ search.by_foo('foo').search.options[:conditions].should == {:foo => 'foo', :name => 'foo'}
88
+ end
89
+
90
+ it "should apply the default scope options before other scope options to the underlying search object" do
91
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
92
+ search.by_name('bar').search.options[:conditions].should == {:name => 'bar'}
93
+ end
94
+ end
95
+
96
+ describe '.example_scope' do
97
+ before :each do
98
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
99
+ Alpha.sphinx_scope(:by_foo) { |foo| {:conditions => {:foo => foo}} }
100
+ Alpha.sphinx_scope(:with_betas) { {:classes => [Beta]} }
101
+ end
102
+
103
+ it "should return a ThinkingSphinx::Search object" do
104
+ Alpha.by_name('foo').should be_a(ThinkingSphinx::Search)
105
+ end
106
+
107
+ it "should set the classes option" do
108
+ Alpha.by_name('foo').options[:classes].should == [Alpha]
109
+ end
110
+
111
+ it "should be able to be called on a ThinkingSphinx::Search object" do
112
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
113
+ lambda {
114
+ search.by_name('foo')
115
+ }.should_not raise_error
116
+ end
117
+
118
+ it "should return the search object it gets called upon" do
119
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
120
+ search.by_name('foo').should == search
121
+ end
122
+
123
+ it "should apply the scope options to the underlying search object" do
124
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
125
+ search.by_name('foo').options[:conditions].should == {:name => 'foo'}
126
+ end
127
+
128
+ it "should combine hash option scopes such as :conditions" do
129
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
130
+ search.by_name('foo').by_foo('bar').options[:conditions].
131
+ should == {:name => 'foo', :foo => 'bar'}
132
+ end
133
+
134
+ it "should combine array option scopes such as :classes" do
135
+ search = ThinkingSphinx::Search.new(:classes => [Alpha])
136
+ search.with_betas.options[:classes].should == [Alpha, Beta]
137
+ end
138
+ end
139
+
140
+ describe '.search_count_with_scope' do
141
+ before :each do
142
+ @config = ThinkingSphinx::Configuration.instance
143
+ @client = Riddle::Client.new
144
+
145
+ @config.stub!(:client => @client)
146
+ @client.stub!(:query => {:matches => [], :total_found => 43})
147
+ Alpha.sphinx_scope(:by_name) { |name| {:conditions => {:name => name}} }
148
+ Alpha.sphinx_scope(:ids_only) { {:ids_only => true} }
149
+ end
150
+
151
+ it "should return the total number of results" do
152
+ Alpha.by_name('foo').search_count.should == 43
153
+ end
154
+
155
+ it "should not make any calls to the database" do
156
+ Alpha.should_not_receive(:find)
157
+
158
+ Alpha.by_name('foo').search_count
159
+ end
160
+
161
+ it "should not leave the :ids_only option set and the results populated if it was not set before" do
162
+ stored_scope = Alpha.by_name('foo')
163
+ stored_scope.search_count
164
+ stored_scope.options[:ids_only].should be_false
165
+ stored_scope.populated?.should be_false
166
+ end
167
+
168
+ it "should leave the :ids_only option set and the results populated if it was set before" do
169
+ stored_scope = Alpha.by_name('foo').ids_only
170
+ stored_scope.search_count
171
+ stored_scope.options[:ids_only].should be_true
172
+ stored_scope.populated?.should be_true
173
+ end
174
+ end
175
+
176
+ end