DrMark-thinking-sphinx 0.9.8 → 0.9.9

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.
@@ -21,15 +21,17 @@ module ThinkingSphinx
21
21
  begin
22
22
  pager = WillPaginate::Collection.create(page,
23
23
  client.limit, results[:total_found] || 0) do |collection|
24
- collection.replace results[:matches].collect { |match| match[:doc] }
24
+ collection.replace results[:matches].collect { |match|
25
+ match[:attributes]["sphinx_internal_id"]
26
+ }
25
27
  collection.instance_variable_set :@total_entries, results[:total_found]
26
28
  end
27
29
  return (options[:include_raw] ? [pager, results] : pager)
28
30
  rescue
29
31
  if options[:include_raw]
30
- return results[:matches].collect { |match| match[:doc] }, results
32
+ results[:matches].collect { |match| match[:attributes]["sphinx_internal_id"] }, results
31
33
  else
32
- return results[:matches].collect { |match| match[:doc] }
34
+ results[:matches].collect { |match| match[:attributes]["sphinx_internal_id"] }
33
35
  end
34
36
  end
35
37
  end
@@ -198,22 +200,30 @@ module ThinkingSphinx
198
200
  klass = options[:class]
199
201
  page = options[:page] ? options[:page].to_i : 1
200
202
 
201
- begin
202
- pager = WillPaginate::Collection.create(page,
203
- client.limit, results[:total] || 0) do |collection|
204
- collection.replace instances_from_results(results[:matches], options, klass)
205
- collection.instance_variable_set :@total_entries, results[:total_found]
206
- end
203
+ # begin
204
+ pager = ThinkingSphinx::Collection.new(page, client.limit,
205
+ results[:total] || 0, results[:total_found] || 0)
206
+ pager.replace instances_from_results(results[:matches], options, klass)
207
+ # pager = WillPaginate::Collection.create(page,
208
+ # client.limit, results[:total] || 0) do |collection|
209
+ # collection.replace instances_from_results(results[:matches], options, klass)
210
+ # collection.instance_variable_set :@total_entries, results[:total_found]
211
+ # end
207
212
  return (options[:include_raw] ? [pager, results] : pager)
208
- rescue StandardError => err
209
- if options[:include_raw]
210
- return instances_from_results(results[:matches], options, klass), results
211
- else
212
- return instances_from_results(results[:matches], options, klass)
213
- end
213
+ # rescue StandardError => err
214
+ # if options[:include_raw]
215
+ # return instances_from_results(results[:matches], options, klass), results
216
+ # else
217
+ # return instances_from_results(results[:matches], options, klass)
218
+ # end
214
219
  end
215
220
  end
216
221
 
222
+ def count(*args)
223
+ results, client = search_results(*args.clone)
224
+ results[:total] || 0
225
+ end
226
+
217
227
  # Checks if a document with the given id exists within a specific index.
218
228
  # Expected parameters:
219
229
  #
@@ -269,7 +279,7 @@ module ThinkingSphinx
269
279
  begin
270
280
  ::ActiveRecord::Base.logger.debug "Sphinx: #{query}"
271
281
  results = client.query query
272
- ::ActiveRecord::Base.logger.debug "Sphinx Result: #{results[:matches].collect{|m| m[:doc]}.inspect}"
282
+ ::ActiveRecord::Base.logger.debug "Sphinx Result: #{results[:matches].collect{|m| m[:attributes]["sphinx_internal_id"]}.inspect}"
273
283
  rescue Errno::ECONNREFUSED => err
274
284
  raise ThinkingSphinx::ConnectionError, "Connection to Sphinx Daemon (searchd) failed."
275
285
  end
@@ -292,13 +302,13 @@ module ThinkingSphinx
292
302
  if klass.nil?
293
303
  results.collect { |result| instance_from_result result, options }
294
304
  else
295
- ids = results.collect { |result| result[:doc] }
296
- instances = klass.find(
305
+ ids = results.collect { |result| result[:attributes]["sphinx_internal_id"] }
306
+ instances = ids.length > 0 ? klass.find(
297
307
  :all,
298
308
  :conditions => {klass.primary_key.to_sym => ids},
299
309
  :include => options[:include],
300
310
  :select => options[:select]
301
- )
311
+ ) : []
302
312
  final_instances = ids.collect { |obj_id| instances.detect { |obj| obj.id == obj_id } }
303
313
 
304
314
  final_instances = append_distances(final_instances, results, options[:distance_name]) if options[:distance_name] && (results.collect { |result| result[:attributes]['@geodist'] }.length > 0)
@@ -312,7 +322,8 @@ module ThinkingSphinx
312
322
  #
313
323
  def instance_from_result(result, options)
314
324
  class_from_crc(result[:attributes]["class_crc"]).find(
315
- result[:doc], :include => options[:include], :select => options[:select]
325
+ result[:attributes]["sphinx_internal_id"],
326
+ :include => options[:include], :select => options[:select]
316
327
  )
317
328
  end
318
329
 
@@ -351,15 +362,18 @@ module ThinkingSphinx
351
362
  options[key] || index_options[key] || client.send(key)
352
363
  )
353
364
  end
354
-
365
+
366
+ options[:classes] = [klass] if klass
367
+
355
368
  client.anchor = anchor_conditions(klass, options) || {} if client.anchor.empty?
356
369
 
357
370
  client.filters << Riddle::Client::Filter.new(
358
371
  "sphinx_deleted", [0]
359
372
  )
373
+
360
374
  # class filters
361
375
  client.filters << Riddle::Client::Filter.new(
362
- "class_crc", options[:classes].collect { |klass| klass.to_crc32 }
376
+ "subclass_crcs", options[:classes].collect { |k| k.to_crc32s }.flatten
363
377
  ) if options[:classes]
364
378
 
365
379
  # normal attribute filters
@@ -400,18 +414,13 @@ module ThinkingSphinx
400
414
  conditions.each do |key,val|
401
415
  if attributes.include?(key.to_sym)
402
416
  filters << Riddle::Client::Filter.new(
403
- key.to_s,
404
- val.is_a?(Range) ? val : Array(val)
417
+ key.to_s, filter_value(val)
405
418
  )
406
419
  else
407
420
  search_string << "@#{key} #{val} "
408
421
  end
409
422
  end
410
-
411
- filters << Riddle::Client::Filter.new(
412
- "class_crc", [klass.to_crc32]
413
- ) if klass
414
-
423
+
415
424
  return search_string, filters
416
425
  end
417
426
 
@@ -4,6 +4,7 @@ require 'riddle'
4
4
  require 'thinking_sphinx/active_record'
5
5
  require 'thinking_sphinx/association'
6
6
  require 'thinking_sphinx/attribute'
7
+ require 'thinking_sphinx/collection'
7
8
  require 'thinking_sphinx/configuration'
8
9
  require 'thinking_sphinx/field'
9
10
  require 'thinking_sphinx/index'
@@ -20,7 +21,7 @@ module ThinkingSphinx
20
21
  module Version #:nodoc:
21
22
  Major = 0
22
23
  Minor = 9
23
- Tiny = 8
24
+ Tiny = 9
24
25
 
25
26
  String = [Major, Minor, Tiny].join('.')
26
27
  end
@@ -58,7 +59,7 @@ module ThinkingSphinx
58
59
  #
59
60
  def self.deltas_enabled?
60
61
  @@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil?
61
- @@deltas_enabled == true
62
+ @@deltas_enabled
62
63
  end
63
64
 
64
65
  # Enable/disable all delta indexing.
@@ -69,6 +70,24 @@ module ThinkingSphinx
69
70
  @@deltas_enabled = value
70
71
  end
71
72
 
73
+ @@updates_enabled = nil
74
+
75
+ # Check if updates are enabled. True by default, unless within the test
76
+ # environment.
77
+ #
78
+ def self.updates_enabled?
79
+ @@updates_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@updates_enabled.nil?
80
+ @@updates_enabled
81
+ end
82
+
83
+ # Enable/disable updates to Sphinx
84
+ #
85
+ # ThinkingSphinx.updates_enabled = false
86
+ #
87
+ def self.updates_enabled=(value)
88
+ @@updates_enabled = value
89
+ end
90
+
72
91
  # Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
73
92
  # or if not using MySQL, this will return false.
74
93
  #
@@ -150,12 +150,10 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
150
150
  ThinkingSphinx.stub_method(:deltas_enabled? => true)
151
151
 
152
152
  @person = Person.new
153
- @person.stub_method(:system => true)
154
- end
155
-
156
- after :each do
157
- ThinkingSphinx::Configuration.unstub_method(:environment)
158
- ThinkingSphinx.unstub_method(:deltas_enabled?)
153
+ @person.stub_method(:system => true, :in_core_index? => false)
154
+
155
+ @client = Riddle::Client.stub_instance(:update => true)
156
+ Riddle::Client.stub_method(:new => @client)
159
157
  end
160
158
 
161
159
  it "shouldn't index if delta indexing is disabled" do
@@ -163,6 +161,15 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
163
161
 
164
162
  @person.send(:index_delta)
165
163
 
164
+ @person.should_not have_received(:system)
165
+ @client.should_not have_received(:update)
166
+ end
167
+
168
+ it "shouldn't index if index updating is disabled" do
169
+ ThinkingSphinx.stub_method(:updates_enabled? => false)
170
+
171
+ @person.send(:index_delta)
172
+
166
173
  @person.should_not have_received(:system)
167
174
  end
168
175
 
@@ -182,5 +189,19 @@ describe "ThinkingSphinx::ActiveRecord::Delta" do
182
189
  "indexer --config #{ThinkingSphinx::Configuration.new.config_file} --rotate person_delta"
183
190
  )
184
191
  end
192
+
193
+ it "shouldn't update the deleted attribute if not in the index" do
194
+ @person.send(:index_delta)
195
+
196
+ @client.should_not have_received(:update)
197
+ end
198
+
199
+ it "should update the deleted attribute if in the core index" do
200
+ @person.stub_method(:in_core_index? => true)
201
+
202
+ @person.send(:index_delta)
203
+
204
+ @client.should have_received(:update)
205
+ end
185
206
  end
186
207
  end
@@ -9,6 +9,10 @@ describe "ThinkingSphinx::ActiveRecord::Search" do
9
9
  ActiveRecord::Base.methods.should include("search")
10
10
  end
11
11
 
12
+ it "should add search_count to ActiveRecord::Base" do
13
+ ActiveRecord::Base.methods.should include("search_count")
14
+ end
15
+
12
16
  it "should add search_for_id to ActiveRecord::Base" do
13
17
  ActiveRecord::Base.methods.should include("search_for_id")
14
18
  end
@@ -78,4 +82,26 @@ describe "ThinkingSphinx::ActiveRecord::Search" do
78
82
  )
79
83
  end
80
84
  end
85
+
86
+ describe "search_count method" do
87
+ before :each do
88
+ ThinkingSphinx::Search.stub_method(:count => true)
89
+ end
90
+
91
+ it "should call ThinkingSphinx::Search#search with the class option set" do
92
+ Person.search_count("search")
93
+
94
+ ThinkingSphinx::Search.should have_received(:count).with(
95
+ "search", :class => Person
96
+ )
97
+ end
98
+
99
+ it "should override the class option" do
100
+ Person.search_count("search", :class => Friendship)
101
+
102
+ ThinkingSphinx::Search.should have_received(:count).with(
103
+ "search", :class => Person
104
+ )
105
+ end
106
+ end
81
107
  end
@@ -21,7 +21,7 @@ describe "ThinkingSphinx::ActiveRecord" do
21
21
  # Remove the class so we can redefine it
22
22
  TestModule.send(:remove_const, :TestModel)
23
23
 
24
- ThinkingSphinx::Index.unstub_method(:new)
24
+ ThinkingSphinx.indexed_models.delete "TestModule::TestModel"
25
25
  end
26
26
 
27
27
  it "should return nil and do nothing if indexes are disabled" do
@@ -207,6 +207,19 @@ describe "ThinkingSphinx::ActiveRecord" do
207
207
  "person_delta", ["sphinx_deleted"], {@person.id => 1}
208
208
  )
209
209
  end
210
+
211
+ it "should not update either index if updates are disabled" do
212
+ ThinkingSphinx.stub_methods(
213
+ :updates_enabled? => false,
214
+ :deltas_enabled => true
215
+ )
216
+ Person.indexes.each { |index| index.stub_method(:delta? => true) }
217
+ @person.delta = true
218
+
219
+ @person.toggle_deleted
220
+
221
+ @client.should_not have_received(:update)
222
+ end
210
223
  end
211
224
 
212
225
  describe "indexes in the inheritance chain (STI)" do
@@ -199,8 +199,12 @@ describe ThinkingSphinx::Attribute do
199
199
  @first_join = Object.stub_instance(:aliased_table_name => "tabular")
200
200
  @second_join = Object.stub_instance(:aliased_table_name => "data")
201
201
 
202
- @first_assoc = ThinkingSphinx::Association.stub_instance(:join => @first_join)
203
- @second_assoc = ThinkingSphinx::Association.stub_instance(:join => @second_join)
202
+ @first_assoc = ThinkingSphinx::Association.stub_instance(
203
+ :join => @first_join, :has_column? => true
204
+ )
205
+ @second_assoc = ThinkingSphinx::Association.stub_instance(
206
+ :join => @second_join, :has_column? => true
207
+ )
204
208
  end
205
209
 
206
210
  it "should return the column name if the column is a string" do
@@ -36,7 +36,7 @@ describe ThinkingSphinx::Configuration do
36
36
  ThinkingSphinx::Configuration.stub_method(:environment => "spec")
37
37
  ThinkingSphinx::Configuration.new.environment.should == "spec"
38
38
  ThinkingSphinx::Configuration.should have_received(:environment)
39
- end
39
+ end
40
40
  end
41
41
 
42
42
  describe "build method" do
@@ -59,13 +59,16 @@ describe ThinkingSphinx::Configuration do
59
59
  })
60
60
 
61
61
  @person_index_a = ThinkingSphinx::Index.stub_instance(
62
- :to_config => "", :adapter => :mysql, :delta? => false, :name => "person"
62
+ :to_config => "", :adapter => :mysql, :delta? => false,
63
+ :name => "person", :model => Person
63
64
  )
64
65
  @person_index_b = ThinkingSphinx::Index.stub_instance(
65
- :to_config => "", :adapter => :mysql, :delta? => false, :name => "person"
66
+ :to_config => "", :adapter => :mysql, :delta? => false,
67
+ :name => "person", :model => Person
66
68
  )
67
69
  @friendship_index_a = ThinkingSphinx::Index.stub_instance(
68
- :to_config => "", :adapter => :mysql, :delta? => false, :name => "friendship"
70
+ :to_config => "", :adapter => :mysql, :delta? => false,
71
+ :name => "friendship", :model => Friendship
69
72
  )
70
73
 
71
74
  Person.stub_method(:indexes => [@person_index_a, @person_index_b])
@@ -81,8 +84,8 @@ describe ThinkingSphinx::Configuration do
81
84
 
82
85
  Person.unstub_method :indexes
83
86
  Friendship.unstub_method :indexes
84
-
85
- FileUtils.rm_rf "#{@config.app_root}/config"
87
+ #
88
+ # FileUtils.rm_rf "#{@config.app_root}/config"
86
89
  end
87
90
 
88
91
  it "should load the models" do
@@ -137,13 +140,13 @@ describe ThinkingSphinx::Configuration do
137
140
  @config.build
138
141
 
139
142
  @person_index_a.should have_received(:to_config).with(
140
- 0, {:option => "value"}, @config.charset_type
143
+ Person, 0, {:option => "value"}, @config.charset_type, 0
141
144
  )
142
145
  @person_index_b.should have_received(:to_config).with(
143
- 1, {:option => "value"}, @config.charset_type
146
+ Person, 1, {:option => "value"}, @config.charset_type, 0
144
147
  )
145
148
  @friendship_index_a.should have_received(:to_config).with(
146
- 0, {:option => "value"}, @config.charset_type
149
+ Friendship, 0, {:option => "value"}, @config.charset_type, 1
147
150
  )
148
151
  end
149
152
 
@@ -204,7 +207,7 @@ describe ThinkingSphinx::Configuration do
204
207
  before :each do
205
208
  @settings = {
206
209
  "development" => {
207
- "config_file" => "my_conf_file.conf",
210
+ "config_file" => "tmp/config/development.sphinx.conf",
208
211
  "searchd_log_file" => "searchd_log_file.log",
209
212
  "query_log_file" => "query_log_file.log",
210
213
  "pid_file" => "pid_file.pid",
@@ -222,7 +225,7 @@ describe ThinkingSphinx::Configuration do
222
225
  "ignore_chars" => "e"
223
226
  }
224
227
  }
225
- # puts YAML.dump(settings)
228
+
226
229
  open("#{RAILS_ROOT}/config/sphinx.yml", "w") do |f|
227
230
  f.write YAML.dump(@settings)
228
231
  end
@@ -234,14 +237,16 @@ describe ThinkingSphinx::Configuration do
234
237
  config.send(key).should == value
235
238
  end
236
239
  end
240
+
241
+ after :each do
242
+ FileUtils.rm "#{RAILS_ROOT}/config/sphinx.yml"
243
+ end
237
244
  end
238
245
 
239
246
  describe "core_index_for_model method" do
240
247
  before :each do
241
248
  @config = ThinkingSphinx::Configuration.new
242
- @model = Class.stub_instance(
243
- :indexes => [ThinkingSphinx::Index.new(Person)]
244
- )
249
+ @model = Person
245
250
  end
246
251
 
247
252
  it "should take its name from the model, with _core appended" do
@@ -312,12 +317,13 @@ describe ThinkingSphinx::Configuration do
312
317
  end
313
318
 
314
319
  it "should include the star-related settings when allow_star is true" do
315
- @config.allow_star = true
320
+ @config.allow_star = true
321
+ @config.min_prefix_len = 1
316
322
  text = @config.send(:core_index_for_model, @model, "my sources")
317
323
 
318
324
  text.should match(/enable_star\s+= 1/)
319
325
  text.should match(/min_prefix_len\s+= 1/)
320
- text.should match(/min_infix_len\s+= 1/)
326
+ # text.should match(/min_infix_len\s+= 1/)
321
327
  end
322
328
 
323
329
  it "should use the configuration's infix and prefix length values if set" do
@@ -327,7 +333,7 @@ describe ThinkingSphinx::Configuration do
327
333
  text = @config.send(:core_index_for_model, @model, "my sources")
328
334
 
329
335
  text.should match(/min_prefix_len\s+= 3/)
330
- text.should match(/min_infix_len\s+= 2/)
336
+ # text.should match(/min_infix_len\s+= 2/)
331
337
  end
332
338
 
333
339
  it "should not include the star-related settings when allow_star is false" do
@@ -341,8 +347,16 @@ describe ThinkingSphinx::Configuration do
341
347
 
342
348
  it "should set prefix_fields if any fields are flagged explicitly" do
343
349
  @model.indexes.first.stub_methods(
344
- :prefix_fields => ["a", "b", "c"],
345
- :infix_fields => ["d", "e", "f"]
350
+ :prefix_fields => [
351
+ ThinkingSphinx::Field.stub_instance(:unique_name => "a"),
352
+ ThinkingSphinx::Field.stub_instance(:unique_name => "b"),
353
+ ThinkingSphinx::Field.stub_instance(:unique_name => "c")
354
+ ],
355
+ :infix_fields => [
356
+ ThinkingSphinx::Field.stub_instance(:unique_name => "d"),
357
+ ThinkingSphinx::Field.stub_instance(:unique_name => "e"),
358
+ ThinkingSphinx::Field.stub_instance(:unique_name => "f")
359
+ ]
346
360
  )
347
361
 
348
362
  @config.send(:core_index_for_model, @model, "my sources").should match(
@@ -358,8 +372,16 @@ describe ThinkingSphinx::Configuration do
358
372
 
359
373
  it "should set infix_fields if any fields are flagged explicitly" do
360
374
  @model.indexes.first.stub_methods(
361
- :prefix_fields => ["a", "b", "c"],
362
- :infix_fields => ["d", "e", "f"]
375
+ :prefix_fields => [
376
+ ThinkingSphinx::Field.stub_instance(:unique_name => "a"),
377
+ ThinkingSphinx::Field.stub_instance(:unique_name => "b"),
378
+ ThinkingSphinx::Field.stub_instance(:unique_name => "c")
379
+ ],
380
+ :infix_fields => [
381
+ ThinkingSphinx::Field.stub_instance(:unique_name => "d"),
382
+ ThinkingSphinx::Field.stub_instance(:unique_name => "e"),
383
+ ThinkingSphinx::Field.stub_instance(:unique_name => "f")
384
+ ]
363
385
  )
364
386
 
365
387
  @config.send(:core_index_for_model, @model, "my sources").should match(
@@ -399,9 +421,7 @@ describe ThinkingSphinx::Configuration do
399
421
  describe "delta_index_for_model method" do
400
422
  before :each do
401
423
  @config = ThinkingSphinx::Configuration.new
402
- @model = Class.stub_instance(
403
- :indexes => [ThinkingSphinx::Index.new(Person)]
404
- )
424
+ @model = Person
405
425
  end
406
426
 
407
427
  it "should take its name from the model, with _delta appended" do
@@ -427,9 +447,7 @@ describe ThinkingSphinx::Configuration do
427
447
  describe "distributed_index_for_model method" do
428
448
  before :each do
429
449
  @config = ThinkingSphinx::Configuration.new
430
- @model = Class.stub_instance(
431
- :indexes => [ThinkingSphinx::Index.new(Person)]
432
- )
450
+ @model = Person
433
451
  end
434
452
 
435
453
  it "should take its name from the model" do
@@ -158,8 +158,17 @@ describe ThinkingSphinx::Field do
158
158
  @first_join = Object.stub_instance(:aliased_table_name => "tabular")
159
159
  @second_join = Object.stub_instance(:aliased_table_name => "data")
160
160
 
161
- @first_assoc = ThinkingSphinx::Association.stub_instance(:join => @first_join)
162
- @second_assoc = ThinkingSphinx::Association.stub_instance(:join => @second_join)
161
+ @first_assoc = ThinkingSphinx::Association.stub_instance(
162
+ :join => @first_join, :has_column? => true
163
+ )
164
+ @second_assoc = ThinkingSphinx::Association.stub_instance(
165
+ :join => @second_join, :has_column? => true
166
+ )
167
+ end
168
+
169
+ it "should return the column name if the column is a string" do
170
+ @field.columns = [ThinkingSphinx::Index::FauxColumn.new("string")]
171
+ @field.send(:column_with_prefix, @field.columns.first).should == "string"
163
172
  end
164
173
 
165
174
  it "should return the column with model's table prefix if there's no associations for the column" do
@@ -17,6 +17,33 @@ describe ThinkingSphinx::Index::FauxColumn do
17
17
  #
18
18
  end
19
19
 
20
+ describe "coerce class method" do
21
+ before :each do
22
+ @column = ThinkingSphinx::Index::FauxColumn.stub_instance
23
+ ThinkingSphinx::Index::FauxColumn.stub_method(:new => @column)
24
+ end
25
+
26
+ it "should return a single faux column if passed a string" do
27
+ ThinkingSphinx::Index::FauxColumn.coerce("string").should == @column
28
+ end
29
+
30
+ it "should return a single faux column if passed a symbol" do
31
+ ThinkingSphinx::Index::FauxColumn.coerce(:string).should == @column
32
+ end
33
+
34
+ it "should return an array of faux columns if passed an array of strings" do
35
+ ThinkingSphinx::Index::FauxColumn.coerce(["one", "two"]).should == [
36
+ @column, @column
37
+ ]
38
+ end
39
+
40
+ it "should return an array of faux columns if passed an array of symbols" do
41
+ ThinkingSphinx::Index::FauxColumn.coerce([:one, :two]).should == [
42
+ @column, @column
43
+ ]
44
+ end
45
+ end
46
+
20
47
  describe "method_missing calls with no arguments" do
21
48
  it "should push any further method calls into name, and the old name goes into the stack" do
22
49
  #