thinking-sphinx 1.5.0 → 2.0.0.rc1

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 (104) hide show
  1. data/README.textile +15 -48
  2. data/VERSION +1 -0
  3. data/features/attribute_transformation.feature +7 -7
  4. data/features/attribute_updates.feature +16 -18
  5. data/features/deleting_instances.feature +13 -16
  6. data/features/excerpts.feature +0 -8
  7. data/features/facets.feature +19 -25
  8. data/features/handling_edits.feature +20 -25
  9. data/features/searching_across_models.feature +1 -1
  10. data/features/searching_by_index.feature +5 -6
  11. data/features/searching_by_model.feature +29 -29
  12. data/features/sphinx_scopes.feature +0 -26
  13. data/features/step_definitions/common_steps.rb +6 -18
  14. data/features/step_definitions/scope_steps.rb +0 -4
  15. data/features/step_definitions/search_steps.rb +4 -9
  16. data/features/support/env.rb +10 -3
  17. data/features/thinking_sphinx/db/fixtures/alphas.rb +10 -8
  18. data/features/thinking_sphinx/db/fixtures/cats.rb +1 -1
  19. data/features/thinking_sphinx/db/fixtures/dogs.rb +1 -1
  20. data/features/thinking_sphinx/db/fixtures/foxes.rb +1 -1
  21. data/features/thinking_sphinx/db/fixtures/people.rb +1 -1
  22. data/features/thinking_sphinx/db/fixtures/posts.rb +1 -5
  23. data/features/thinking_sphinx/db/migrations/create_posts.rb +0 -1
  24. data/features/thinking_sphinx/models/alpha.rb +0 -1
  25. data/features/thinking_sphinx/models/beta.rb +0 -5
  26. data/features/thinking_sphinx/models/developer.rb +1 -6
  27. data/features/thinking_sphinx/models/music.rb +1 -3
  28. data/features/thinking_sphinx/models/person.rb +1 -2
  29. data/features/thinking_sphinx/models/post.rb +0 -1
  30. data/lib/cucumber/thinking_sphinx/external_world.rb +4 -8
  31. data/lib/cucumber/thinking_sphinx/internal_world.rb +27 -36
  32. data/lib/thinking_sphinx.rb +60 -132
  33. data/lib/thinking_sphinx/active_record.rb +98 -124
  34. data/lib/thinking_sphinx/active_record/attribute_updates.rb +13 -17
  35. data/lib/thinking_sphinx/active_record/delta.rb +15 -21
  36. data/lib/thinking_sphinx/active_record/has_many_association.rb +23 -16
  37. data/lib/thinking_sphinx/active_record/scopes.rb +0 -18
  38. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +15 -63
  39. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +0 -4
  40. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +24 -65
  41. data/lib/thinking_sphinx/association.rb +11 -36
  42. data/lib/thinking_sphinx/attribute.rb +85 -92
  43. data/lib/thinking_sphinx/auto_version.rb +3 -21
  44. data/lib/thinking_sphinx/class_facet.rb +3 -8
  45. data/lib/thinking_sphinx/configuration.rb +58 -114
  46. data/lib/thinking_sphinx/context.rb +20 -22
  47. data/lib/thinking_sphinx/core/array.rb +13 -0
  48. data/lib/thinking_sphinx/deltas.rb +0 -2
  49. data/lib/thinking_sphinx/deltas/default_delta.rb +22 -18
  50. data/lib/thinking_sphinx/deploy/capistrano.rb +31 -30
  51. data/lib/thinking_sphinx/excerpter.rb +1 -2
  52. data/lib/thinking_sphinx/facet.rb +35 -45
  53. data/lib/thinking_sphinx/facet_search.rb +24 -58
  54. data/lib/thinking_sphinx/field.rb +0 -18
  55. data/lib/thinking_sphinx/index.rb +36 -38
  56. data/lib/thinking_sphinx/index/builder.rb +59 -74
  57. data/lib/thinking_sphinx/property.rb +45 -66
  58. data/lib/thinking_sphinx/railtie.rb +35 -0
  59. data/lib/thinking_sphinx/search.rb +250 -506
  60. data/lib/thinking_sphinx/source.rb +31 -50
  61. data/lib/thinking_sphinx/source/internal_properties.rb +3 -8
  62. data/lib/thinking_sphinx/source/sql.rb +31 -71
  63. data/lib/thinking_sphinx/tasks.rb +27 -48
  64. data/spec/thinking_sphinx/active_record/delta_spec.rb +41 -36
  65. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +0 -96
  66. data/spec/thinking_sphinx/active_record/scopes_spec.rb +29 -29
  67. data/spec/thinking_sphinx/active_record_spec.rb +169 -140
  68. data/spec/thinking_sphinx/association_spec.rb +2 -20
  69. data/spec/thinking_sphinx/attribute_spec.rb +97 -101
  70. data/spec/thinking_sphinx/auto_version_spec.rb +11 -75
  71. data/spec/thinking_sphinx/configuration_spec.rb +62 -63
  72. data/spec/thinking_sphinx/context_spec.rb +66 -66
  73. data/spec/thinking_sphinx/facet_search_spec.rb +99 -99
  74. data/spec/thinking_sphinx/facet_spec.rb +4 -30
  75. data/spec/thinking_sphinx/field_spec.rb +3 -17
  76. data/spec/thinking_sphinx/index/builder_spec.rb +132 -169
  77. data/spec/thinking_sphinx/index_spec.rb +39 -45
  78. data/spec/thinking_sphinx/search_methods_spec.rb +33 -37
  79. data/spec/thinking_sphinx/search_spec.rb +269 -491
  80. data/spec/thinking_sphinx/source_spec.rb +48 -62
  81. data/spec/thinking_sphinx_spec.rb +49 -49
  82. data/tasks/distribution.rb +46 -0
  83. data/tasks/testing.rb +74 -0
  84. metadata +123 -199
  85. data/features/field_sorting.feature +0 -18
  86. data/features/thinking_sphinx/db/.gitignore +0 -1
  87. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +0 -1
  88. data/features/thinking_sphinx/models/andrew.rb +0 -17
  89. data/lib/thinking-sphinx.rb +0 -1
  90. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +0 -21
  91. data/lib/thinking_sphinx/bundled_search.rb +0 -40
  92. data/lib/thinking_sphinx/connection.rb +0 -71
  93. data/lib/thinking_sphinx/deltas/delete_job.rb +0 -16
  94. data/lib/thinking_sphinx/deltas/index_job.rb +0 -17
  95. data/lib/thinking_sphinx/rails_additions.rb +0 -181
  96. data/spec/fixtures/data.sql +0 -32
  97. data/spec/fixtures/database.yml.default +0 -3
  98. data/spec/fixtures/models.rb +0 -161
  99. data/spec/fixtures/structure.sql +0 -146
  100. data/spec/spec_helper.rb +0 -54
  101. data/spec/sphinx_helper.rb +0 -67
  102. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +0 -163
  103. data/spec/thinking_sphinx/connection_spec.rb +0 -77
  104. data/spec/thinking_sphinx/rails_additions_spec.rb +0 -203
@@ -5,35 +5,17 @@ module ThinkingSphinx
5
5
  case version
6
6
  when '0.9.8', '0.9.9'
7
7
  require "riddle/#{version}"
8
- when /1.10/
9
- require 'riddle/1.10'
10
- when /2.0.[12]/
11
- require 'riddle/2.0.1'
12
- when /2.0.[^12]/, /2.1.\d/
13
- require 'riddle/2.1.0'
14
8
  else
15
- documentation_link = %Q{
16
- For more information, read the documentation:
17
- http://pat.github.io/thinking-sphinx/advanced_config.html
18
- }
19
-
20
- if version.nil? || version.empty?
21
- STDERR.puts %Q{
9
+ STDERR.puts %Q{
22
10
  Sphinx cannot be found on your system. You may need to configure the following
23
11
  settings in your config/sphinx.yml file:
24
12
  * bin_path
25
13
  * searchd_binary_name
26
14
  * indexer_binary_name
27
15
 
28
- #{documentation_link}
29
- }
30
- else
31
- STDERR.puts %Q{
32
- Unsupported version: #{version}
33
-
34
- #{documentation_link}
16
+ For more information, read the documentation:
17
+ http://freelancing-god.github.com/ts/en/advanced_config.html
35
18
  }
36
- end
37
19
  end
38
20
  end
39
21
  end
@@ -5,16 +5,11 @@ module ThinkingSphinx
5
5
  end
6
6
 
7
7
  def attribute_name
8
- Riddle.loaded_version.to_i < 2 ? 'class_crc' : 'sphinx_internal_class'
8
+ "class_crc"
9
9
  end
10
10
 
11
- def value(object, attribute_hash)
12
- if Riddle.loaded_version.to_i < 2
13
- crc = attribute_hash['class_crc']
14
- ThinkingSphinx::Configuration.instance.models_by_crc[crc]
15
- else
16
- attribute_hash['sphinx_internal_class']
17
- end
11
+ def value(object, attribute_value)
12
+ object.class.name
18
13
  end
19
14
  end
20
15
  end
@@ -5,7 +5,7 @@ module ThinkingSphinx
5
5
  # This class both keeps track of the configuration settings for Sphinx and
6
6
  # also generates the resulting file for Sphinx to use.
7
7
  #
8
- # Here are the default settings, relative to RAILS_ROOT where relevant:
8
+ # Here are the default settings, relative to Rails.root where relevant:
9
9
  #
10
10
  # config file:: config/#{environment}.sphinx.conf
11
11
  # searchd log file:: log/searchd.log
@@ -15,7 +15,6 @@ module ThinkingSphinx
15
15
  # address:: 127.0.0.1
16
16
  # port:: 9312
17
17
  # allow star:: false
18
- # stop timeout:: 5
19
18
  # min prefix length:: 1
20
19
  # min infix length:: 1
21
20
  # mem limit:: 64M
@@ -28,7 +27,6 @@ module ThinkingSphinx
28
27
  # html remove elements:: ''
29
28
  # searchd_binary_name:: searchd
30
29
  # indexer_binary_name:: indexer
31
- # hard_retry_count:: 0
32
30
  #
33
31
  # If you want to change these settings, create a YAML file at
34
32
  # config/sphinx.yml with settings for each environment, in a similar
@@ -51,28 +49,28 @@ module ThinkingSphinx
51
49
  class Configuration
52
50
  include Singleton
53
51
 
54
- SourceOptions = Riddle::Configuration::SQLSource.settings.map { |setting|
55
- setting.to_s
56
- } - %w( type sql_query sql_joined_field sql_file_field
57
- sql_query_range sql_attr_uint sql_attr_bool sql_attr_bigint sql_query_info
58
- sql_attr_timestamp sql_attr_str2ordinal sql_attr_float sql_attr_multi
59
- sql_attr_string sql_attr_str2wordcount sql_column_buffers sql_field_string
60
- sql_field_str2wordcount )
61
- IndexOptions = Riddle::Configuration::Index.settings.map { |setting|
62
- setting.to_s
63
- } - %w( source prefix_fields infix_fields )
64
- CustomOptions = %w( disable_range use_64_bit )
65
-
66
- attr_accessor :searchd_file_path, :allow_star, :app_root,
67
- :model_directories, :delayed_job_priority, :indexed_models, :use_64_bit,
68
- :touched_reindex_file, :stop_timeout, :version, :shuffle,
69
- :hard_retry_count
52
+ SourceOptions = %w( mysql_connect_flags mysql_ssl_cert mysql_ssl_key
53
+ mysql_ssl_ca sql_range_step sql_query_pre sql_query_post
54
+ sql_query_killlist sql_ranged_throttle sql_query_post_index unpack_zlib
55
+ unpack_mysqlcompress unpack_mysqlcompress_maxsize )
70
56
 
57
+ IndexOptions = %w( charset_table charset_type charset_dictpath docinfo
58
+ enable_star exceptions html_index_attrs html_remove_elements html_strip
59
+ index_exact_words ignore_chars inplace_docinfo_gap inplace_enable
60
+ inplace_hit_gap inplace_reloc_factor inplace_write_factor min_infix_len
61
+ min_prefix_len min_stemming_len min_word_len mlock morphology ngram_chars
62
+ ngram_len ondisk_dict overshort_step phrase_boundary phrase_boundary_step
63
+ preopen stopwords stopwords_step wordforms )
64
+
65
+ CustomOptions = %w( disable_range )
66
+
67
+ attr_accessor :searchd_file_path, :allow_star, :database_yml_file,
68
+ :app_root, :model_directories, :delayed_job_priority, :indexed_models
69
+
71
70
  attr_accessor :source_options, :index_options
72
- attr_reader :configuration
73
- attr_writer :controller
71
+ attr_accessor :version
74
72
 
75
- @@environment = nil
73
+ attr_reader :environment, :configuration, :controller
76
74
 
77
75
  # Load in the configuration settings - this will look for config/sphinx.yml
78
76
  # and parse it according to the current environment.
@@ -90,30 +88,29 @@ module ThinkingSphinx
90
88
  if custom_app_root
91
89
  self.app_root = custom_app_root
92
90
  else
93
- self.app_root = Merb.root if defined?(Merb)
94
- self.app_root = Sinatra::Application.root if defined?(Sinatra)
95
- self.app_root = RAILS_ROOT if defined?(RAILS_ROOT)
96
- self.app_root ||= app_root
91
+ self.app_root = Rails.root if defined?(Rails)
92
+ self.app_root = Merb.root if defined?(Merb)
93
+ self.app_root ||= Dir.pwd
97
94
  end
98
95
 
99
- @controller = nil
100
96
  @configuration = Riddle::Configuration.new
101
97
  @configuration.searchd.pid_file = "#{self.app_root}/log/searchd.#{environment}.pid"
102
98
  @configuration.searchd.log = "#{self.app_root}/log/searchd.log"
103
99
  @configuration.searchd.query_log = "#{self.app_root}/log/searchd.query.log"
104
100
 
101
+ @controller = Riddle::Controller.new @configuration,
102
+ "#{self.app_root}/config/#{environment}.sphinx.conf"
103
+
105
104
  self.address = "127.0.0.1"
106
105
  self.port = 9312
106
+ self.database_yml_file = "#{self.app_root}/config/database.yml"
107
107
  self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
108
108
  self.allow_star = false
109
- self.stop_timeout = 5
110
109
  self.model_directories = ["#{app_root}/app/models/"] +
111
110
  Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
112
111
  self.delayed_job_priority = 0
113
112
  self.indexed_models = []
114
- self.shuffle = false
115
- self.hard_retry_count = 0
116
-
113
+
117
114
  self.source_options = {}
118
115
  self.index_options = {
119
116
  :charset_type => "utf-8"
@@ -121,32 +118,20 @@ module ThinkingSphinx
121
118
 
122
119
  self.version = nil
123
120
  parse_config
124
- if controller.respond_to?(:sphinx_version)
125
- self.version ||= controller.sphinx_version
126
- end
127
-
128
- ThinkingSphinx::Attribute::SphinxTypeMappings.merge!(
129
- :string => :sql_attr_string
130
- ) if Riddle.loaded_version.to_i > 1
121
+ self.version ||= @controller.sphinx_version
131
122
 
132
123
  self
133
124
  end
134
125
 
135
126
  def self.environment
136
- @@environment ||= if defined?(Merb)
137
- Merb.environment
138
- elsif defined?(RAILS_ENV)
139
- RAILS_ENV
140
- elsif defined?(Sinatra)
141
- Sinatra::Application.environment.to_s
142
- else
143
- ENV['RAILS_ENV'] || 'development'
144
- end
145
- end
146
-
147
- def self.reset_environment
148
- ThinkingSphinx.mutex.synchronize do
149
- @@environment = nil
127
+ Thread.current[:thinking_sphinx_environment] ||= begin
128
+ if defined?(Merb)
129
+ Merb.environment
130
+ elsif defined?(Rails)
131
+ Rails.env
132
+ else
133
+ ENV['RAILS_ENV'] || 'development'
134
+ end
150
135
  end
151
136
  end
152
137
 
@@ -154,23 +139,6 @@ module ThinkingSphinx
154
139
  self.class.environment
155
140
  end
156
141
 
157
- def controller
158
- @controller ||= Riddle::Controller.new @configuration,
159
- "#{self.app_root}/config/#{environment}.sphinx.conf"
160
- end
161
-
162
- def generate
163
- @configuration.indices.clear
164
-
165
- ThinkingSphinx.context.indexed_models.each do |model|
166
- model = model.constantize
167
- model.define_indexes
168
- @configuration.indices.concat model.to_riddle
169
-
170
- enforce_common_attribute_types
171
- end
172
- end
173
-
174
142
  # Generate the config file for Sphinx by using all the settings defined and
175
143
  # looping through all the models with indexes to build the relevant
176
144
  # indexer and searchd configuration, and sources and indexes details.
@@ -178,7 +146,13 @@ module ThinkingSphinx
178
146
  def build(file_path=nil)
179
147
  file_path ||= "#{self.config_file}"
180
148
 
181
- generate
149
+ @configuration.indexes.clear
150
+
151
+ ThinkingSphinx.context.indexed_models.each do |model|
152
+ model = model.constantize
153
+ model.define_indexes
154
+ @configuration.indexes.concat model.to_riddle
155
+ end
182
156
 
183
157
  open(file_path, "w") do |file|
184
158
  file.write @configuration.render
@@ -203,14 +177,6 @@ module ThinkingSphinx
203
177
  @configuration.searchd.port = port
204
178
  end
205
179
 
206
- def use_socket=(use_socket)
207
- if use_socket
208
- socket = "#{app_root}/tmp/sockets/searchd.#{self.environment}.sock"
209
- @configuration.searchd.listen = socket
210
- self.address = socket
211
- end
212
- end
213
-
214
180
  def pid_file
215
181
  @configuration.searchd.pid_file
216
182
  end
@@ -236,44 +202,48 @@ module ThinkingSphinx
236
202
  end
237
203
 
238
204
  def config_file
239
- controller.path
205
+ @controller.path
240
206
  end
241
207
 
242
208
  def config_file=(file)
243
- controller.path = file
209
+ @controller.path = file
244
210
  end
245
211
 
246
212
  def bin_path
247
- controller.bin_path
213
+ @controller.bin_path
248
214
  end
249
215
 
250
216
  def bin_path=(path)
251
- controller.bin_path = path
217
+ @controller.bin_path = path
252
218
  end
253
219
 
254
220
  def searchd_binary_name
255
- controller.searchd_binary_name
221
+ @controller.searchd_binary_name
256
222
  end
257
223
 
258
224
  def searchd_binary_name=(name)
259
- controller.searchd_binary_name = name
225
+ @controller.searchd_binary_name = name
260
226
  end
261
227
 
262
228
  def indexer_binary_name
263
- controller.indexer_binary_name
229
+ @controller.indexer_binary_name
264
230
  end
265
231
 
266
232
  def indexer_binary_name=(name)
267
- controller.indexer_binary_name = name
233
+ @controller.indexer_binary_name = name
268
234
  end
269
235
 
270
- attr_accessor :timeout
236
+ def client
237
+ client = Riddle::Client.new address, port
238
+ client.max_matches = configuration.searchd.max_matches || 1000
239
+ client
240
+ end
271
241
 
272
242
  def models_by_crc
273
243
  @models_by_crc ||= begin
274
244
  ThinkingSphinx.context.indexed_models.inject({}) do |hash, model|
275
245
  hash[model.constantize.to_crc32] = model
276
- Object.subclasses_of(model.constantize).each { |subclass|
246
+ model.constantize.subclasses.each { |subclass|
277
247
  hash[subclass.to_crc32] = subclass.name
278
248
  }
279
249
  hash
@@ -281,11 +251,6 @@ module ThinkingSphinx
281
251
  end
282
252
  end
283
253
 
284
- def touch_reindex_file(output)
285
- return FileUtils.touch(@touched_reindex_file) if @touched_reindex_file and output =~ /succesfully sent SIGHUP to searchd/
286
- false
287
- end
288
-
289
254
  private
290
255
 
291
256
  # Parse the config/sphinx.yml file - if it exists - then use the attribute
@@ -323,26 +288,5 @@ module ThinkingSphinx
323
288
  send("#{key}=", value) if self.respond_to?("#{key}")
324
289
  end
325
290
  end
326
-
327
- def enforce_common_attribute_types
328
- sql_indexes = configuration.indices.reject { |index|
329
- index.is_a? Riddle::Configuration::DistributedIndex
330
- }
331
-
332
- return unless sql_indexes.any? { |index|
333
- index.sources.any? { |source|
334
- source.sql_attr_bigint.include? :sphinx_internal_id
335
- }
336
- }
337
-
338
- sql_indexes.each { |index|
339
- index.sources.each { |source|
340
- next if source.sql_attr_bigint.include? :sphinx_internal_id
341
-
342
- source.sql_attr_bigint << :sphinx_internal_id
343
- source.sql_attr_uint.delete :sphinx_internal_id
344
- }
345
- }
346
- end
347
291
  end
348
292
  end
@@ -1,50 +1,50 @@
1
1
  class ThinkingSphinx::Context
2
2
  attr_reader :indexed_models
3
-
3
+
4
4
  def initialize(*models)
5
5
  @indexed_models = []
6
6
  end
7
-
7
+
8
8
  def prepare
9
9
  ThinkingSphinx::Configuration.instance.indexed_models.each do |model|
10
10
  add_indexed_model model
11
11
  end
12
-
12
+
13
13
  return unless indexed_models.empty?
14
-
14
+
15
15
  load_models
16
16
  add_indexed_models
17
17
  end
18
-
18
+
19
19
  def define_indexes
20
20
  indexed_models.each { |model|
21
21
  model.constantize.define_indexes
22
22
  }
23
23
  end
24
-
24
+
25
25
  def add_indexed_model(model)
26
26
  model = model.name if model.is_a?(Class)
27
-
27
+
28
28
  indexed_models << model
29
29
  indexed_models.uniq!
30
30
  indexed_models.sort!
31
31
  end
32
-
32
+
33
33
  def superclass_indexed_models
34
34
  klasses = indexed_models.collect { |name| name.constantize }
35
35
  klasses.reject { |klass|
36
36
  klass.superclass.ancestors.any? { |ancestor| klasses.include?(ancestor) }
37
37
  }.collect { |klass| klass.name }
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  def add_indexed_models
43
- Object.subclasses_of(ActiveRecord::Base).each do |klass|
43
+ ActiveRecord::Base.subclasses.each do |klass|
44
44
  add_indexed_model klass if klass.has_sphinx_indexes?
45
45
  end
46
46
  end
47
-
47
+
48
48
  # Make sure all models are loaded - without reloading any that
49
49
  # ActiveRecord::Base is already aware of (otherwise we start to hit some
50
50
  # messy dependencies issues).
@@ -53,22 +53,20 @@ class ThinkingSphinx::Context
53
53
  ThinkingSphinx::Configuration.instance.model_directories.each do |base|
54
54
  Dir["#{base}**/*.rb"].each do |file|
55
55
  model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
56
-
56
+
57
57
  next if model_name.nil?
58
- camelized_model = model_name.camelize
59
58
  next if ::ActiveRecord::Base.send(:subclasses).detect { |model|
60
- model.name == camelized_model
59
+ model.name == model_name.camelize
61
60
  }
62
-
61
+
63
62
  begin
64
- camelized_model.constantize
63
+ model_name.camelize.constantize
65
64
  rescue LoadError
66
- # Make sure that STI subclasses in subfolders are loaded.
67
65
  model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry
68
- rescue Exception => err
69
- STDERR.puts "Warning: Error loading #{file}:"
70
- STDERR.puts err.message
71
- STDERR.puts err.backtrace.join("\n"), ''
66
+ rescue NameError
67
+ next
68
+ rescue StandardError
69
+ STDERR.puts "Warning: Error loading #{file}"
72
70
  end
73
71
  end
74
72
  end