thinking-sphinx 1.3.4 → 1.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/README.textile +15 -4
  2. data/VERSION +1 -0
  3. data/features/alternate_primary_key.feature +1 -1
  4. data/features/attribute_updates.feature +11 -5
  5. data/features/deleting_instances.feature +3 -0
  6. data/features/searching_by_index.feature +40 -0
  7. data/features/step_definitions/alpha_steps.rb +5 -1
  8. data/features/step_definitions/beta_steps.rb +1 -1
  9. data/features/step_definitions/common_steps.rb +12 -1
  10. data/features/step_definitions/sphinx_steps.rb +8 -4
  11. data/features/support/db/fixtures/tags.rb +1 -1
  12. data/features/support/env.rb +3 -0
  13. data/features/support/models/alpha.rb +11 -0
  14. data/lib/cucumber/thinking_sphinx/internal_world.rb +7 -6
  15. data/lib/thinking_sphinx.rb +40 -31
  16. data/lib/thinking_sphinx/active_record.rb +164 -195
  17. data/lib/thinking_sphinx/active_record/attribute_updates.rb +9 -6
  18. data/lib/thinking_sphinx/configuration.rb +1 -1
  19. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  20. data/lib/thinking_sphinx/index.rb +76 -19
  21. data/lib/thinking_sphinx/index/builder.rb +2 -2
  22. data/lib/thinking_sphinx/search.rb +7 -0
  23. data/lib/thinking_sphinx/search_methods.rb +22 -4
  24. data/lib/thinking_sphinx/source.rb +6 -6
  25. data/lib/thinking_sphinx/source/sql.rb +4 -2
  26. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +4 -6
  27. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +0 -0
  28. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/scopes_spec.rb +0 -0
  29. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record_spec.rb +254 -94
  30. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +0 -0
  31. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +0 -0
  32. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +2 -2
  33. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +0 -0
  34. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +0 -0
  35. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +0 -0
  36. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +0 -0
  37. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +0 -0
  38. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +0 -0
  39. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +10 -0
  40. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +0 -0
  41. data/spec/thinking_sphinx/index_spec.rb +177 -0
  42. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +0 -0
  43. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +0 -0
  44. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +33 -0
  45. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +0 -0
  46. data/spec/{lib/thinking_sphinx_spec.rb → thinking_sphinx_spec.rb} +62 -50
  47. data/tasks/distribution.rb +3 -3
  48. metadata +27 -31
  49. data/VERSION.yml +0 -5
  50. data/features/support/db/active_record.rb +0 -40
  51. data/features/support/db/database.yml +0 -5
  52. data/features/support/db/mysql.rb +0 -3
  53. data/features/support/db/postgresql.rb +0 -3
  54. data/features/support/post_database.rb +0 -43
  55. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
@@ -10,22 +10,25 @@ module ThinkingSphinx
10
10
  private
11
11
 
12
12
  def update_attribute_values
13
- return unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.sphinx_running?
13
+ return true unless ThinkingSphinx.updates_enabled? &&
14
+ ThinkingSphinx.sphinx_running?
14
15
 
15
16
  config = ThinkingSphinx::Configuration.instance
16
- client = Riddle::Client.new config.address, config.port
17
+ client = config.client
17
18
 
18
- self.sphinx_indexes.each do |index|
19
+ self.class.sphinx_indexes.each do |index|
19
20
  attribute_pairs = attribute_values_for_index(index)
20
21
  attribute_names = attribute_pairs.keys
21
22
  attribute_values = attribute_names.collect { |key|
22
23
  attribute_pairs[key]
23
24
  }
24
25
 
25
- client.update "#{index.name}_core", attribute_names, {
26
+ client.update "#{index.core_name}", attribute_names, {
26
27
  sphinx_document_id => attribute_values
27
- } if in_core_index?
28
+ } if self.class.search_for_id(sphinx_document_id, index.core_name)
28
29
  end
30
+
31
+ true
29
32
  end
30
33
 
31
34
  def updatable_attributes(index)
@@ -45,4 +48,4 @@ module ThinkingSphinx
45
48
  end
46
49
  end
47
50
  end
48
- end
51
+ end
@@ -124,7 +124,7 @@ module ThinkingSphinx
124
124
  end
125
125
 
126
126
  def self.environment
127
- @@environment ||= (
127
+ Thread.current[:thinking_sphinx_environment] ||= (
128
128
  defined?(Merb) ? Merb.environment : ENV['RAILS_ENV']
129
129
  ) || "development"
130
130
  end
@@ -13,18 +13,8 @@ module ThinkingSphinx
13
13
  ThinkingSphinx.deltas_enabled?
14
14
  return true if instance && !toggled(instance)
15
15
 
16
- config = ThinkingSphinx::Configuration.instance
17
- client = Riddle::Client.new config.address, config.port
18
- rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
19
-
20
- output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
21
- puts(output) unless ThinkingSphinx.suppress_delta_output?
22
-
23
- client.update(
24
- core_index_name(model),
25
- ['sphinx_deleted'],
26
- {instance.sphinx_document_id => [1]}
27
- ) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
16
+ update_delta_indexes model
17
+ delete_from_core model, instance if instance
28
18
 
29
19
  true
30
20
  end
@@ -48,17 +38,21 @@ module ThinkingSphinx
48
38
  " = #{adapter.boolean(toggled)}"
49
39
  end
50
40
 
51
- protected
41
+ private
52
42
 
53
- def core_index_name(model)
54
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
43
+ def update_delta_indexes(model)
44
+ config = ThinkingSphinx::Configuration.instance
45
+ rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
46
+
47
+ output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{model.delta_index_names.join(' ')}`
48
+ puts(output) unless ThinkingSphinx.suppress_delta_output?
55
49
  end
56
50
 
57
- def delta_index_name(model)
58
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
59
- end
60
-
61
- private
51
+ def delete_from_core(model, instance)
52
+ model.core_index_names.each do |index_name|
53
+ model.delete_in_index index_name, instance.sphinx_document_id
54
+ end
55
+ end
62
56
 
63
57
  def adapter
64
58
  @adapter = @index.model.sphinx_database_adapter
@@ -2,14 +2,8 @@ require 'thinking_sphinx/index/builder'
2
2
  require 'thinking_sphinx/index/faux_column'
3
3
 
4
4
  module ThinkingSphinx
5
- # The Index class is a ruby representation of a Sphinx source (not a Sphinx
6
- # index - yes, I know it's a little confusing. You'll manage). This is
7
- # another 'internal' Thinking Sphinx class - if you're using it directly,
8
- # you either know what you're doing, or messing with things beyond your ken.
9
- # Enjoy.
10
- #
11
5
  class Index
12
- attr_accessor :model, :sources, :delta_object
6
+ attr_accessor :name, :model, :sources, :delta_object
13
7
 
14
8
  # Create a new index instance by passing in the model it is tied to, and
15
9
  # a block to build it with (optional but recommended). For documentation
@@ -26,6 +20,7 @@ module ThinkingSphinx
26
20
  # end
27
21
  #
28
22
  def initialize(model, &block)
23
+ @name = self.class.name_for model
29
24
  @model = model
30
25
  @sources = []
31
26
  @options = {}
@@ -40,8 +35,19 @@ module ThinkingSphinx
40
35
  @sources.collect { |source| source.attributes }.flatten
41
36
  end
42
37
 
43
- def name
44
- self.class.name_for @model
38
+ def core_name
39
+ "#{name}_core"
40
+ end
41
+
42
+ def delta_name
43
+ "#{name}_delta"
44
+ end
45
+
46
+ def all_names
47
+ names = [core_name]
48
+ names << delta_name if delta?
49
+
50
+ names
45
51
  end
46
52
 
47
53
  def self.name_for(model)
@@ -61,7 +67,7 @@ module ThinkingSphinx
61
67
  end
62
68
 
63
69
  def options
64
- all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
70
+ all_index_options = config.index_options.clone
65
71
  @options.keys.select { |key|
66
72
  ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) ||
67
73
  ThinkingSphinx::Configuration::CustomOptions.include?(key.to_s)
@@ -73,6 +79,12 @@ module ThinkingSphinx
73
79
  !@delta_object.nil?
74
80
  end
75
81
 
82
+ def to_riddle(offset)
83
+ indexes = [to_riddle_for_core(offset)]
84
+ indexes << to_riddle_for_delta(offset) if delta?
85
+ indexes << to_riddle_for_distributed
86
+ end
87
+
76
88
  private
77
89
 
78
90
  def adapter
@@ -83,17 +95,62 @@ module ThinkingSphinx
83
95
  options[:charset_type] == "utf-8"
84
96
  end
85
97
 
86
- # Does all the magic with the block provided to the base #initialize.
87
- # Creates a new class subclassed from Builder, and evaluates the block
88
- # on it, then pulls all relevant settings - fields, attributes, conditions,
89
- # properties - into the new index.
90
- #
91
- def initialize_from_builder(&block)
92
- #
93
- end
94
-
95
98
  def sql_query_pre_for_delta
96
99
  [""]
97
100
  end
101
+
102
+ def config
103
+ @config ||= ThinkingSphinx::Configuration.instance
104
+ end
105
+
106
+ def to_riddle_for_core(offset)
107
+ index = Riddle::Configuration::Index.new core_name
108
+ index.path = File.join config.searchd_file_path, index.name
109
+
110
+ set_configuration_options_for_indexes index
111
+ set_field_settings_for_indexes index
112
+
113
+ sources.each_with_index do |source, i|
114
+ index.sources << source.to_riddle_for_core(offset, i)
115
+ end
116
+
117
+ index
118
+ end
119
+
120
+ def to_riddle_for_delta(offset)
121
+ index = Riddle::Configuration::Index.new delta_name
122
+ index.parent = core_name
123
+ index.path = File.join config.searchd_file_path, index.name
124
+
125
+ sources.each_with_index do |source, i|
126
+ index.sources << source.to_riddle_for_delta(offset, i)
127
+ end
128
+
129
+ index
130
+ end
131
+
132
+ def to_riddle_for_distributed
133
+ index = Riddle::Configuration::DistributedIndex.new name
134
+ index.local_indexes << core_name
135
+ index.local_indexes.unshift delta_name if delta?
136
+ index
137
+ end
138
+
139
+ def set_configuration_options_for_indexes(index)
140
+ config.index_options.each do |key, value|
141
+ index.send("#{key}=".to_sym, value)
142
+ end
143
+
144
+ options.each do |key, value|
145
+ index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
146
+ end
147
+ end
148
+
149
+ def set_field_settings_for_indexes(index)
150
+ field_names = lambda { |field| field.unique_name.to_s }
151
+
152
+ index.prefix_field_names += prefix_fields.collect(&field_names)
153
+ index.infix_field_names += infix_fields.collect(&field_names)
154
+ end
98
155
  end
99
156
  end
@@ -20,9 +20,9 @@ module ThinkingSphinx
20
20
  }
21
21
  }
22
22
 
23
- def self.generate(model, &block)
23
+ def self.generate(model, name = nil, &block)
24
24
  index = ThinkingSphinx::Index.new(model)
25
- model.sphinx_facets ||= []
25
+ index.name = name unless name.nil?
26
26
 
27
27
  Builder.new(index, &block) if block_given?
28
28
 
@@ -155,6 +155,8 @@ module ThinkingSphinx
155
155
  #
156
156
  def total_pages
157
157
  populate
158
+ return 0 if @results[:total].nil?
159
+
158
160
  @total_pages ||= (@results[:total] / per_page.to_f).ceil
159
161
  end
160
162
  # Compatibility with older versions of will_paginate
@@ -166,6 +168,8 @@ module ThinkingSphinx
166
168
  #
167
169
  def total_entries
168
170
  populate
171
+ return 0 if @results[:total_found].nil?
172
+
169
173
  @total_entries ||= @results[:total_found]
170
174
  end
171
175
 
@@ -313,6 +317,9 @@ module ThinkingSphinx
313
317
  # puts "value: #{value.inspect}"
314
318
  client.send("#{key}=", value) if value
315
319
  end
320
+
321
+ # treated non-standard as :select is already used for AR queries
322
+ client.select = options[:sphinx_select] || '*'
316
323
 
317
324
  client.limit = per_page
318
325
  client.offset = offset
@@ -155,7 +155,7 @@ module ThinkingSphinx
155
155
  # asterisks. You need to make the config/sphinx.yml changes yourself.
156
156
  #
157
157
  # By default, the tokens are assumed to match the regular expression
158
- # /\w+/u. If you've modified the charset_table, pass another regular
158
+ # /\w\+/u\+. If you've modified the charset_table, pass another regular
159
159
  # expression, e.g.
160
160
  #
161
161
  # User.search("oo@bar.c", :star => /[\w@.]+/u)
@@ -313,13 +313,31 @@ module ThinkingSphinx
313
313
  # Once you've got your results set, you can access the distances as
314
314
  # follows:
315
315
  #
316
- # @results.each_with_geodist do |result, distance|
317
- # # ...
318
- # end
316
+ # @results.each_with_geodist do |result, distance|
317
+ # # ...
318
+ # end
319
319
  #
320
320
  # The distance value is returned as a float, representing the distance in
321
321
  # metres.
322
322
  #
323
+ # == Filtering by custom attributes
324
+ #
325
+ # Do note that this applies only to sphinx 0.9.9
326
+ #
327
+ # Should you find yourself in desperate need of a filter that involves
328
+ # selecting either one of multiple conditions, one solution could be
329
+ # provided by the :sphinx_select option within the search.
330
+ # This handles which fields are selected by sphinx from its store.
331
+ #
332
+ # The default value is "*", and you can add custom fields using syntax
333
+ # similar to sql:
334
+ #
335
+ # Flower.search "foo",
336
+ # :sphinx_select => "*, petals < 1 or color = 2 as grass"
337
+ #
338
+ # This will add the 'grass' attribute, which will now be usable in your
339
+ # filters.
340
+ #
323
341
  # == Handling a Stale Index
324
342
  #
325
343
  # Especially if you don't use delta indexing, you risk having records in
@@ -33,12 +33,12 @@ module ThinkingSphinx
33
33
  end
34
34
 
35
35
  def name
36
- @model.sphinx_name
36
+ index.name
37
37
  end
38
38
 
39
- def to_riddle_for_core(offset, index)
39
+ def to_riddle_for_core(offset, position)
40
40
  source = Riddle::Configuration::SQLSource.new(
41
- "#{name}_core_#{index}", adapter.sphinx_identifier
41
+ "#{index.core_name}_#{position}", adapter.sphinx_identifier
42
42
  )
43
43
 
44
44
  set_source_database_settings source
@@ -49,11 +49,11 @@ module ThinkingSphinx
49
49
  source
50
50
  end
51
51
 
52
- def to_riddle_for_delta(offset, index)
52
+ def to_riddle_for_delta(offset, position)
53
53
  source = Riddle::Configuration::SQLSource.new(
54
- "#{name}_delta_#{index}", adapter.sphinx_identifier
54
+ "#{index.delta_name}_#{position}", adapter.sphinx_identifier
55
55
  )
56
- source.parent = "#{name}_core_#{index}"
56
+ source.parent = "#{index.core_name}_#{position}"
57
57
 
58
58
  set_source_database_settings source
59
59
  set_source_attributes source, offset, true
@@ -13,8 +13,10 @@ module ThinkingSphinx
13
13
  # source.to_sql(:delta => true)
14
14
  #
15
15
  def to_sql(options={})
16
- sql = <<-SQL
17
- SELECT #{ sql_select_clause options[:offset] }
16
+ sql = "SELECT "
17
+ sql += "SQL_NO_CACHE " if adapter.sphinx_identifier == "mysql"
18
+ sql += <<-SQL
19
+ #{ sql_select_clause options[:offset] }
18
20
  FROM #{ @model.quoted_table_name }
19
21
  #{ all_associations.collect { |assoc| assoc.to_sql }.join(' ') }
20
22
  #{ sql_where_clause(options) }
@@ -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
@@ -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,102 +1,59 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
3
  describe ThinkingSphinx::ActiveRecord do
4
- describe '.define_index' do
5
- before :each do
6
- module ::TestModule
7
- class TestModel < ActiveRecord::Base; end
8
- end
9
-
10
- TestModule::TestModel.stub!(
11
- :before_save => true,
12
- :after_commit => true,
13
- :after_destroy => true
14
- )
15
-
16
- @index = ThinkingSphinx::Index.new(TestModule::TestModel)
17
- @index.stub!(:delta? => false)
18
- ThinkingSphinx::Index::Builder.stub!(:generate => @index)
19
- end
20
-
21
- after :each do
22
- # Remove the class so we can redefine it
23
- TestModule.send(:remove_const, :TestModel)
24
-
25
- ThinkingSphinx.indexed_models.delete "TestModule::TestModel"
26
- end
4
+ before :each do
5
+ @existing_alpha_indexes = Alpha.sphinx_indexes.clone
6
+ @existing_beta_indexes = Beta.sphinx_indexes.clone
27
7
 
8
+ Alpha.sphinx_indexes.clear
9
+ Beta.sphinx_indexes.clear
10
+ end
11
+
12
+ after :each do
13
+ Alpha.sphinx_indexes.replace @existing_alpha_indexes
14
+ Beta.sphinx_indexes.replace @existing_beta_indexes
15
+ end
16
+
17
+ describe '.define_index' do
28
18
  it "should do nothing if indexes are disabled" do
29
19
  ThinkingSphinx.define_indexes = false
30
20
  ThinkingSphinx::Index.should_not_receive(:new)
31
21
 
32
- TestModule::TestModel.define_index {}
22
+ Alpha.define_index { }
33
23
 
34
24
  ThinkingSphinx.define_indexes = true
35
25
  end
36
26
 
37
27
  it "should add a new index to the model" do
38
- TestModule::TestModel.define_index {}
28
+ index = Alpha.define_index { indexes :name }
39
29
 
40
- TestModule::TestModel.sphinx_indexes.length.should == 1
30
+ Alpha.sphinx_indexes.should include(index)
41
31
  end
42
32
 
43
33
  it "should add to ThinkingSphinx.indexed_models if the model doesn't already exist in the array" do
44
- TestModule::TestModel.define_index do; end
34
+ Alpha.define_index { indexes :name }
45
35
 
46
- ThinkingSphinx.indexed_models.should include("TestModule::TestModel")
36
+ ThinkingSphinx.indexed_models.should include("Alpha")
47
37
  end
48
38
 
49
39
  it "shouldn't add to ThinkingSphinx.indexed_models if the model already exists in the array" do
50
- TestModule::TestModel.define_index do; end
40
+ Alpha.define_index { indexes :name }
41
+ Alpha.define_index { indexes :name }
51
42
 
52
43
  ThinkingSphinx.indexed_models.select { |model|
53
- model == "TestModule::TestModel"
44
+ model == "Alpha"
54
45
  }.length.should == 1
55
-
56
- TestModule::TestModel.define_index do; end
57
-
58
- ThinkingSphinx.indexed_models.select { |model|
59
- model == "TestModule::TestModel"
60
- }.length.should == 1
61
- end
62
-
63
- it "should add before_save and after_commit hooks to the model if delta indexing is enabled" do
64
- @index.stub!(:delta? => true)
65
- TestModule::TestModel.should_receive(:before_save).with(:toggle_delta)
66
- TestModule::TestModel.should_receive(:after_commit).with(:index_delta)
67
-
68
- TestModule::TestModel.define_index do; end
69
- end
70
-
71
- it "should not add before_save and after_commit hooks to the model if delta indexing is disabled" do
72
- TestModule::TestModel.should_not_receive(:before_save).with(:toggle_delta)
73
- TestModule::TestModel.should_not_receive(:after_commit).with(:index_delta)
74
-
75
- TestModule::TestModel.define_index do; end
76
- end
77
-
78
- it "should add an after_destroy hook with delta indexing enabled" do
79
- @index.stub!(:delta? => true)
80
- TestModule::TestModel.should_receive(:after_destroy).with(:toggle_deleted)
81
-
82
- TestModule::TestModel.define_index do; end
83
- end
84
-
85
- it "should add an after_destroy hook with delta indexing disabled" do
86
- TestModule::TestModel.should_receive(:after_destroy).with(:toggle_deleted)
87
-
88
- TestModule::TestModel.define_index do; end
89
46
  end
90
47
 
91
48
  it "should return the new index" do
92
- TestModule::TestModel.define_index.should == @index
49
+ Alpha.define_index.should be_a(ThinkingSphinx::Index)
93
50
  end
94
51
 
95
52
  it "should die quietly if there is a database error" do
96
53
  ThinkingSphinx::Index::Builder.stub(:generate) { raise Mysql::Error }
97
54
 
98
55
  lambda {
99
- TestModule::TestModel.define_index
56
+ Alpha.define_index { indexes :name }
100
57
  }.should_not raise_error
101
58
  end
102
59
 
@@ -104,9 +61,122 @@ describe ThinkingSphinx::ActiveRecord do
104
61
  ThinkingSphinx::Index::Builder.stub(:generate) { raise StandardError }
105
62
 
106
63
  lambda {
107
- TestModule::TestModel.define_index
64
+ Alpha.define_index { indexes :name }
108
65
  }.should raise_error
109
66
  end
67
+
68
+ it "should set the index's name using the parameter if provided" do
69
+ index = Alpha.define_index('custom') { indexes :name }
70
+
71
+ index.name.should == 'custom'
72
+ end
73
+
74
+ context 'callbacks' do
75
+ it "should add a toggle_deleted callback" do
76
+ Alpha.should_receive(:after_destroy).with(:toggle_deleted)
77
+
78
+ Alpha.define_index { indexes :name }
79
+ end
80
+
81
+ it "should not add toggle_deleted callback more than once" do
82
+ Alpha.should_receive(:after_destroy).with(:toggle_deleted).once
83
+
84
+ Alpha.define_index { indexes :name }
85
+ Alpha.define_index { indexes :name }
86
+ end
87
+
88
+ it "should add a update_attribute_values callback" do
89
+ Alpha.should_receive(:after_commit).with(:update_attribute_values)
90
+
91
+ Alpha.define_index { indexes :name }
92
+ end
93
+
94
+ it "should not add update_attribute_values callback more than once" do
95
+ Alpha.should_receive(:after_commit).with(:update_attribute_values).once
96
+
97
+ Alpha.define_index { indexes :name }
98
+ Alpha.define_index { indexes :name }
99
+ end
100
+
101
+ it "should add a toggle_delta callback if deltas are enabled" do
102
+ Beta.should_receive(:before_save).with(:toggle_delta)
103
+
104
+ Beta.define_index {
105
+ indexes :name
106
+ set_property :delta => true
107
+ }
108
+ end
109
+
110
+ it "should not add a toggle_delta callback if deltas are disabled" do
111
+ Alpha.should_not_receive(:before_save).with(:toggle_delta)
112
+
113
+ Alpha.define_index { indexes :name }
114
+ end
115
+
116
+ it "should add the toggle_delta callback if deltas are disabled in other indexes" do
117
+ Beta.should_receive(:before_save).with(:toggle_delta).once
118
+
119
+ Beta.define_index { indexes :name }
120
+ Beta.define_index {
121
+ indexes :name
122
+ set_property :delta => true
123
+ }
124
+ end
125
+
126
+ it "should only add the toggle_delta callback once" do
127
+ Beta.should_receive(:before_save).with(:toggle_delta).once
128
+
129
+ Beta.define_index {
130
+ indexes :name
131
+ set_property :delta => true
132
+ }
133
+ Beta.define_index {
134
+ indexes :name
135
+ set_property :delta => true
136
+ }
137
+ end
138
+
139
+ it "should add an index_delta callback if deltas are enabled" do
140
+ Beta.stub!(:after_commit => true)
141
+ Beta.should_receive(:after_commit).with(:index_delta)
142
+
143
+ Beta.define_index {
144
+ indexes :name
145
+ set_property :delta => true
146
+ }
147
+ end
148
+
149
+ it "should not add an index_delta callback if deltas are disabled" do
150
+ Alpha.should_not_receive(:after_commit).with(:index_delta)
151
+
152
+ Alpha.define_index { indexes :name }
153
+ end
154
+
155
+ it "should add the index_delta callback if deltas are disabled in other indexes" do
156
+ Beta.stub!(:after_commit => true)
157
+ Beta.should_receive(:after_commit).with(:index_delta).once
158
+
159
+ Beta.define_index { indexes :name }
160
+ Beta.define_index {
161
+ indexes :name
162
+ set_property :delta => true
163
+ }
164
+ end
165
+
166
+ it "should only add the index_delta callback once" do
167
+ Beta.stub!(:after_commit => true)
168
+ Beta.should_receive(:after_commit).with(:index_delta).once
169
+
170
+ Beta.define_index {
171
+ indexes :name
172
+ set_property :delta => true
173
+ }
174
+ Beta.define_index {
175
+ indexes :name
176
+ set_property :delta => true
177
+ }
178
+ end
179
+ end
110
180
  end
111
181
 
112
182
  describe "index methods" do
@@ -188,17 +258,9 @@ describe ThinkingSphinx::ActiveRecord do
188
258
  @client.stub!(:update => true)
189
259
  @person = Person.find(:first)
190
260
 
191
- Riddle::Client.stub!(:new => @client)
261
+ @configuration.stub!(:client => @client)
192
262
  Person.sphinx_indexes.each { |index| index.stub!(:delta? => false) }
193
- @person.stub!(:in_core_index? => true)
194
- end
195
-
196
- it "should create a client using the Configuration's address and port" do
197
- Riddle::Client.should_receive(:new).with(
198
- @configuration.address, @configuration.port
199
- )
200
-
201
- @person.toggle_deleted
263
+ Person.stub!(:search_for_id => true)
202
264
  end
203
265
 
204
266
  it "should update the core index's deleted flag if in core index" do
@@ -210,7 +272,7 @@ describe ThinkingSphinx::ActiveRecord do
210
272
  end
211
273
 
212
274
  it "shouldn't update the core index's deleted flag if the record isn't in it" do
213
- @person.stub!(:in_core_index? => false)
275
+ Person.stub!(:search_for_id => false)
214
276
  @client.should_not_receive(:update).with(
215
277
  "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => [1]}
216
278
  )
@@ -221,7 +283,7 @@ describe ThinkingSphinx::ActiveRecord do
221
283
  it "shouldn't attempt to update the deleted flag if sphinx isn't running" do
222
284
  ThinkingSphinx.stub!(:sphinx_running? => false)
223
285
  @client.should_not_receive(:update)
224
- @person.should_not_receive(:in_core_index?)
286
+ Person.should_not_receive(:search_for_id)
225
287
 
226
288
  @person.toggle_deleted
227
289
  end
@@ -294,22 +356,29 @@ describe ThinkingSphinx::ActiveRecord do
294
356
  end
295
357
  end
296
358
 
297
- it "should return the sphinx document id as expected" do
298
- person = Person.find(:first)
299
- model_count = ThinkingSphinx.indexed_models.length
300
- offset = ThinkingSphinx.indexed_models.index("Person")
301
-
302
- (person.id * model_count + offset).should == person.sphinx_document_id
303
-
304
- alpha = Alpha.find(:first)
305
- offset = ThinkingSphinx.indexed_models.index("Alpha")
306
-
307
- (alpha.id * model_count + offset).should == alpha.sphinx_document_id
308
-
309
- beta = Beta.find(:first)
310
- offset = ThinkingSphinx.indexed_models.index("Beta")
359
+ describe '#sphinx_document_id' do
360
+ before :each do
361
+ Alpha.define_index { indexes :name }
362
+ Beta.define_index { indexes :name }
363
+ end
311
364
 
312
- (beta.id * model_count + offset).should == beta.sphinx_document_id
365
+ it "should return values with the expected offset" do
366
+ person = Person.find(:first)
367
+ model_count = ThinkingSphinx.indexed_models.length
368
+ offset = ThinkingSphinx.indexed_models.index("Person")
369
+
370
+ (person.id * model_count + offset).should == person.sphinx_document_id
371
+
372
+ alpha = Alpha.find(:first)
373
+ offset = ThinkingSphinx.indexed_models.index("Alpha")
374
+
375
+ (alpha.id * model_count + offset).should == alpha.sphinx_document_id
376
+
377
+ beta = Beta.find(:first)
378
+ offset = ThinkingSphinx.indexed_models.index("Beta")
379
+
380
+ (beta.id * model_count + offset).should == beta.sphinx_document_id
381
+ end
313
382
  end
314
383
 
315
384
  describe '#primary_key_for_sphinx' do
@@ -339,10 +408,16 @@ describe ThinkingSphinx::ActiveRecord do
339
408
 
340
409
  describe '.sphinx_index_names' do
341
410
  it "should return the core index" do
411
+ Alpha.define_index { indexes :name }
342
412
  Alpha.sphinx_index_names.should == ['alpha_core']
343
413
  end
344
414
 
345
415
  it "should return the delta index if enabled" do
416
+ Beta.define_index {
417
+ indexes :name
418
+ set_property :delta => true
419
+ }
420
+
346
421
  Beta.sphinx_index_names.should == ['beta_core', 'beta_delta']
347
422
  end
348
423
 
@@ -350,4 +425,89 @@ describe ThinkingSphinx::ActiveRecord do
350
425
  Parent.sphinx_index_names.should == ['person_core', 'person_delta']
351
426
  end
352
427
  end
428
+
429
+ describe '.indexed_by_sphinx?' do
430
+ it "should return true if there is at least one index on the model" do
431
+ Alpha.define_index { indexes :name }
432
+
433
+ Alpha.should be_indexed_by_sphinx
434
+ end
435
+
436
+ it "should return false if there are no indexes on the model" do
437
+ Gamma.should_not be_indexed_by_sphinx
438
+ end
439
+ end
440
+
441
+ describe '.delta_indexed_by_sphinx?' do
442
+ it "should return true if there is at least one delta index on the model" do
443
+ Beta.define_index {
444
+ indexes :name
445
+ set_property :delta => true
446
+ }
447
+
448
+ Beta.should be_delta_indexed_by_sphinx
449
+ end
450
+
451
+ it "should return false if there are no delta indexes on the model" do
452
+ Alpha.define_index { indexes :name }
453
+
454
+ Alpha.should_not be_delta_indexed_by_sphinx
455
+ end
456
+ end
457
+
458
+ describe '.delete_in_index' do
459
+ before :each do
460
+ @client = stub('client')
461
+ ThinkingSphinx.stub!(:sphinx_running? => true)
462
+ ThinkingSphinx::Configuration.instance.stub!(:client => @client)
463
+ Alpha.stub!(:search_for_id => true)
464
+ end
465
+
466
+ it "should not update if the document isn't in the given index" do
467
+ Alpha.stub!(:search_for_id => false)
468
+ @client.should_not_receive(:update)
469
+
470
+ Alpha.delete_in_index('alpha_core', 42)
471
+ end
472
+
473
+ it "should direct the update to the supplied index" do
474
+ @client.should_receive(:update) do |index, attributes, values|
475
+ index.should == 'custom_index_core'
476
+ end
477
+
478
+ Alpha.delete_in_index('custom_index_core', 42)
479
+ end
480
+
481
+ it "should set the sphinx_deleted flag to true" do
482
+ @client.should_receive(:update) do |index, attributes, values|
483
+ attributes.should == ['sphinx_deleted']
484
+ values.should == {42 => [1]}
485
+ end
486
+
487
+ Alpha.delete_in_index('alpha_core', 42)
488
+ end
489
+ end
490
+
491
+ describe '.core_index_names' do
492
+ it "should return each index's core name" do
493
+ foo = Alpha.define_index { indexes :name }
494
+ foo.name = 'foo'
495
+ bar = Alpha.define_index { indexes :name }
496
+ bar.name = 'bar'
497
+
498
+ Alpha.core_index_names.should == ['foo_core', 'bar_core']
499
+ end
500
+ end
501
+
502
+ describe '.delta_index_names' do
503
+ it "should return index delta names, for indexes with deltas enabled" do
504
+ foo = Alpha.define_index { indexes :name }
505
+ foo.name = 'foo'
506
+ foo.delta_object = stub('delta')
507
+ bar = Alpha.define_index { indexes :name }
508
+ bar.name = 'bar'
509
+
510
+ Alpha.delta_index_names.should == ['foo_delta']
511
+ end
512
+ end
353
513
  end