thinking-sphinx 1.5.0 → 2.0.0.rc1

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