jaikoo-thinking-sphinx 0.9.10

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 (37) hide show
  1. data/LICENCE +20 -0
  2. data/README +76 -0
  3. data/lib/thinking_sphinx.rb +112 -0
  4. data/lib/thinking_sphinx/active_record.rb +153 -0
  5. data/lib/thinking_sphinx/active_record/delta.rb +80 -0
  6. data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
  7. data/lib/thinking_sphinx/active_record/search.rb +50 -0
  8. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +27 -0
  9. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +9 -0
  10. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +84 -0
  11. data/lib/thinking_sphinx/association.rb +144 -0
  12. data/lib/thinking_sphinx/attribute.rb +284 -0
  13. data/lib/thinking_sphinx/collection.rb +105 -0
  14. data/lib/thinking_sphinx/configuration.rb +314 -0
  15. data/lib/thinking_sphinx/field.rb +206 -0
  16. data/lib/thinking_sphinx/index.rb +432 -0
  17. data/lib/thinking_sphinx/index/builder.rb +220 -0
  18. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  19. data/lib/thinking_sphinx/rails_additions.rb +68 -0
  20. data/lib/thinking_sphinx/search.rb +436 -0
  21. data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +132 -0
  22. data/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb +53 -0
  23. data/spec/unit/thinking_sphinx/active_record/search_spec.rb +107 -0
  24. data/spec/unit/thinking_sphinx/active_record_spec.rb +295 -0
  25. data/spec/unit/thinking_sphinx/association_spec.rb +247 -0
  26. data/spec/unit/thinking_sphinx/attribute_spec.rb +360 -0
  27. data/spec/unit/thinking_sphinx/collection_spec.rb +71 -0
  28. data/spec/unit/thinking_sphinx/configuration_spec.rb +512 -0
  29. data/spec/unit/thinking_sphinx/field_spec.rb +224 -0
  30. data/spec/unit/thinking_sphinx/index/builder_spec.rb +34 -0
  31. data/spec/unit/thinking_sphinx/index/faux_column_spec.rb +68 -0
  32. data/spec/unit/thinking_sphinx/index_spec.rb +317 -0
  33. data/spec/unit/thinking_sphinx/search_spec.rb +203 -0
  34. data/spec/unit/thinking_sphinx_spec.rb +129 -0
  35. data/tasks/thinking_sphinx_tasks.rake +1 -0
  36. data/tasks/thinking_sphinx_tasks.rb +100 -0
  37. metadata +103 -0
@@ -0,0 +1,132 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "ThinkingSphinx::ActiveRecord::Delta" do
4
+ it "should call the toggle_delta method after a save" do
5
+ @beta = Beta.new(:name => 'beta')
6
+ @beta.stub_method(:toggle_delta => true)
7
+
8
+ @beta.save
9
+
10
+ @beta.should have_received(:toggle_delta)
11
+ end
12
+
13
+ it "should call the toggle_delta method after a save!" do
14
+ @beta = Beta.new(:name => 'beta')
15
+ @beta.stub_method(:toggle_delta => true)
16
+
17
+ @beta.save!
18
+
19
+ @beta.should have_received(:toggle_delta)
20
+ end
21
+
22
+ describe "suspended_delta method" do
23
+ before :each do
24
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
25
+ end
26
+
27
+ it "should execute the argument block with deltas disabled" do
28
+ ThinkingSphinx.should_receive(:deltas_enabled=).once.with(false)
29
+ ThinkingSphinx.should_receive(:deltas_enabled=).once.with(true)
30
+ lambda { Person.suspended_delta { raise 'i was called' } }.should(
31
+ raise_error(Exception)
32
+ )
33
+ end
34
+
35
+ it "should restore deltas_enabled to its original setting" do
36
+ ThinkingSphinx.stub_method(:deltas_enabled? => false)
37
+ ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false)
38
+ Person.suspended_delta { 'no-op' }
39
+ end
40
+
41
+ it "should restore deltas_enabled to its original setting even if there was an exception" do
42
+ ThinkingSphinx.stub_method(:deltas_enabled? => false)
43
+ ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false)
44
+ lambda { Person.suspended_delta { raise 'bad error' } }.should(
45
+ raise_error(Exception)
46
+ )
47
+ end
48
+
49
+ it "should reindex by default after the code block is run" do
50
+ Person.should_receive(:index_delta)
51
+ Person.suspended_delta { 'no-op' }
52
+ end
53
+
54
+ it "should not reindex after the code block if false is passed in" do
55
+ Person.should_not_receive(:index_delta)
56
+ Person.suspended_delta(false) { 'no-op' }
57
+ end
58
+ end
59
+
60
+ describe "toggle_delta method" do
61
+ it "should set the delta value to true" do
62
+ @person = Person.new
63
+
64
+ @person.delta.should be_false
65
+ @person.send(:toggle_delta)
66
+ @person.delta.should be_true
67
+ end
68
+ end
69
+
70
+ describe "index_delta method" do
71
+ before :each do
72
+ ThinkingSphinx::Configuration.stub_method(:environment => "spec")
73
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
74
+
75
+ @person = Person.find(:first)
76
+ Person.stub_method(:system => true)
77
+ @person.stub_method(:in_core_index? => false)
78
+
79
+ @client = Riddle::Client.stub_instance(:update => true)
80
+ Riddle::Client.stub_method(:new => @client)
81
+ end
82
+
83
+ it "shouldn't index if delta indexing is disabled" do
84
+ ThinkingSphinx.stub_method(:deltas_enabled? => false)
85
+
86
+ @person.send(:index_delta)
87
+
88
+ Person.should_not have_received(:system)
89
+ @client.should_not have_received(:update)
90
+ end
91
+
92
+ it "shouldn't index if index updating is disabled" do
93
+ ThinkingSphinx.stub_method(:updates_enabled? => false)
94
+
95
+ @person.send(:index_delta)
96
+
97
+ Person.should_not have_received(:system)
98
+ end
99
+
100
+ it "shouldn't index if the environment is 'test'" do
101
+ ThinkingSphinx.unstub_method(:deltas_enabled?)
102
+ ThinkingSphinx.deltas_enabled = nil
103
+ ThinkingSphinx::Configuration.stub_method(:environment => "test")
104
+
105
+ @person.send(:index_delta)
106
+
107
+ Person.should_not have_received(:system)
108
+ end
109
+
110
+ it "should call indexer for the delta index" do
111
+ @person.send(:index_delta)
112
+
113
+ Person.should have_received(:system).with(
114
+ "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config #{ThinkingSphinx::Configuration.instance.config_file} --rotate person_delta"
115
+ )
116
+ end
117
+
118
+ it "shouldn't update the deleted attribute if not in the index" do
119
+ @person.send(:index_delta)
120
+
121
+ @client.should_not have_received(:update)
122
+ end
123
+
124
+ it "should update the deleted attribute if in the core index" do
125
+ @person.stub_method(:in_core_index? => true)
126
+
127
+ @person.send(:index_delta)
128
+
129
+ @client.should have_received(:update)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do
4
+ describe "search method" do
5
+ before :each do
6
+ Friendship.stub_method(:search => true)
7
+
8
+ @person = Person.find(:first)
9
+ @index = Friendship.sphinx_indexes.first
10
+ end
11
+
12
+ it "should raise an error if the required attribute doesn't exist" do
13
+ @index.stub_method(:attributes => [])
14
+
15
+ lambda { @person.friendships.search "test" }.should raise_error(RuntimeError)
16
+
17
+ @index.unstub_method(:attributes)
18
+ end
19
+
20
+ it "should add a filter for the attribute into a normal search call" do
21
+ @person.friendships.search "test"
22
+
23
+ Friendship.should have_received(:search).with(
24
+ "test", :with => {:person_id => @person.id}
25
+ )
26
+ end
27
+ end
28
+
29
+ describe "search method for has_many :through" do
30
+ before :each do
31
+ Person.stub_method(:search => true)
32
+
33
+ @person = Person.find(:first)
34
+ @index = Person.sphinx_indexes.first
35
+ end
36
+
37
+ it "should raise an error if the required attribute doesn't exist" do
38
+ @index.stub_method(:attributes => [])
39
+
40
+ lambda { @person.friends.search "test" }.should raise_error(RuntimeError)
41
+
42
+ @index.unstub_method(:attributes)
43
+ end
44
+
45
+ it "should add a filter for the attribute into a normal search call" do
46
+ @person.friends.search "test"
47
+
48
+ Person.should have_received(:search).with(
49
+ "test", :with => {:friendly_ids => @person.id}
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "ThinkingSphinx::ActiveRecord::Search" do
4
+ it "should add search_for_ids to ActiveRecord::Base" do
5
+ ActiveRecord::Base.methods.should include("search_for_ids")
6
+ end
7
+
8
+ it "should add search_for_ids to ActiveRecord::Base" do
9
+ ActiveRecord::Base.methods.should include("search")
10
+ end
11
+
12
+ it "should add search_count to ActiveRecord::Base" do
13
+ ActiveRecord::Base.methods.should include("search_count")
14
+ end
15
+
16
+ it "should add search_for_id to ActiveRecord::Base" do
17
+ ActiveRecord::Base.methods.should include("search_for_id")
18
+ end
19
+
20
+ describe "search_for_ids method" do
21
+ before :each do
22
+ ThinkingSphinx::Search.stub_method(:search_for_ids => true)
23
+ end
24
+
25
+ it "should call ThinkingSphinx::Search#search_for_ids with the class option set" do
26
+ Person.search_for_ids("search")
27
+
28
+ ThinkingSphinx::Search.should have_received(:search_for_ids).with(
29
+ "search", :class => Person
30
+ )
31
+ end
32
+
33
+ it "should override the class option" do
34
+ Person.search_for_ids("search", :class => Friendship)
35
+
36
+ ThinkingSphinx::Search.should have_received(:search_for_ids).with(
37
+ "search", :class => Person
38
+ )
39
+ end
40
+ end
41
+
42
+ describe "search method" do
43
+ before :each do
44
+ ThinkingSphinx::Search.stub_method(:search => true)
45
+ end
46
+
47
+ it "should call ThinkingSphinx::Search#search with the class option set" do
48
+ Person.search("search")
49
+
50
+ ThinkingSphinx::Search.should have_received(:search).with(
51
+ "search", :class => Person
52
+ )
53
+ end
54
+
55
+ it "should override the class option" do
56
+ Person.search("search", :class => Friendship)
57
+
58
+ ThinkingSphinx::Search.should have_received(:search).with(
59
+ "search", :class => Person
60
+ )
61
+ end
62
+ end
63
+
64
+ describe "search_for_id method" do
65
+ before :each do
66
+ ThinkingSphinx::Search.stub_method(:search_for_id => true)
67
+ end
68
+
69
+ it "should call ThinkingSphinx::Search#search with the class option set" do
70
+ Person.search_for_id(10)
71
+
72
+ ThinkingSphinx::Search.should have_received(:search_for_id).with(
73
+ 10, :class => Person
74
+ )
75
+ end
76
+
77
+ it "should override the class option" do
78
+ Person.search_for_id(10, :class => Friendship)
79
+
80
+ ThinkingSphinx::Search.should have_received(:search_for_id).with(
81
+ 10, :class => Person
82
+ )
83
+ end
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
107
+ end
@@ -0,0 +1,295 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "ThinkingSphinx::ActiveRecord" do
4
+ before :all do
5
+ @sphinx.setup_sphinx
6
+ @sphinx.start
7
+ end
8
+
9
+ after :all do
10
+ @sphinx.stop
11
+ end
12
+
13
+ describe "define_index method" do
14
+ before :each do
15
+ module TestModule
16
+ class TestModel < ActiveRecord::Base; end
17
+ end
18
+
19
+ TestModule::TestModel.stub_methods(
20
+ :before_save => true,
21
+ :after_commit => true,
22
+ :after_destroy => true
23
+ )
24
+
25
+ @index = ThinkingSphinx::Index.stub_instance(:delta? => false)
26
+ ThinkingSphinx::Index.stub_method(:new => @index)
27
+ end
28
+
29
+ after :each do
30
+ # Remove the class so we can redefine it
31
+ TestModule.send(:remove_const, :TestModel)
32
+
33
+ ThinkingSphinx.indexed_models.delete "TestModule::TestModel"
34
+ end
35
+
36
+ it "should return nil and do nothing if indexes are disabled" do
37
+ ThinkingSphinx.stub_method(:define_indexes? => false)
38
+
39
+ TestModule::TestModel.define_index {}.should be_nil
40
+ ThinkingSphinx::Index.should_not have_received(:new)
41
+
42
+ ThinkingSphinx.unstub_method(:define_indexes?)
43
+ end
44
+
45
+ it "should add a new index to the model" do
46
+ TestModule::TestModel.define_index do; end
47
+
48
+ TestModule::TestModel.sphinx_indexes.length.should == 1
49
+ end
50
+
51
+ it "should add to ThinkingSphinx.indexed_models if the model doesn't already exist in the array" do
52
+ TestModule::TestModel.define_index do; end
53
+
54
+ ThinkingSphinx.indexed_models.should include("TestModule::TestModel")
55
+ end
56
+
57
+ it "shouldn't add to ThinkingSphinx.indexed_models if the model already exists in the array" do
58
+ TestModule::TestModel.define_index do; end
59
+
60
+ ThinkingSphinx.indexed_models.select { |model|
61
+ model == "TestModule::TestModel"
62
+ }.length.should == 1
63
+
64
+ TestModule::TestModel.define_index do; end
65
+
66
+ ThinkingSphinx.indexed_models.select { |model|
67
+ model == "TestModule::TestModel"
68
+ }.length.should == 1
69
+ end
70
+
71
+ it "should add before_save and after_commit hooks to the model if delta indexing is enabled" do
72
+ @index.stub_method(:delta? => true)
73
+
74
+ TestModule::TestModel.define_index do; end
75
+
76
+ TestModule::TestModel.should have_received(:before_save)
77
+ TestModule::TestModel.should have_received(:after_commit)
78
+ end
79
+
80
+ it "should not add before_save and after_commit hooks to the model if delta indexing is disabled" do
81
+ TestModule::TestModel.define_index do; end
82
+
83
+ TestModule::TestModel.should_not have_received(:before_save)
84
+ TestModule::TestModel.should_not have_received(:after_commit)
85
+ end
86
+
87
+ it "should add an after_destroy hook with delta indexing enabled" do
88
+ @index.stub_method(:delta? => true)
89
+
90
+ TestModule::TestModel.define_index do; end
91
+
92
+ TestModule::TestModel.should have_received(:after_destroy).with(:toggle_deleted)
93
+ end
94
+
95
+ it "should add an after_destroy hook with delta indexing disabled" do
96
+ TestModule::TestModel.define_index do; end
97
+
98
+ TestModule::TestModel.should have_received(:after_destroy).with(:toggle_deleted)
99
+ end
100
+
101
+ it "should return the new index" do
102
+ TestModule::TestModel.define_index.should == @index
103
+ end
104
+ end
105
+
106
+ describe "to_crc32 method" do
107
+ it "should return an integer" do
108
+ Person.to_crc32.should be_a_kind_of(Integer)
109
+ end
110
+ end
111
+
112
+ describe "in_core_index? method" do
113
+ it "should return the model's corresponding search_for_id value" do
114
+ Person.stub_method(:search_for_id => :searching_for_id)
115
+
116
+ person = Person.find(:first)
117
+ person.in_core_index?.should == :searching_for_id
118
+ Person.should have_received(:search_for_id).with(person.sphinx_document_id, "person_core")
119
+ end
120
+ end
121
+
122
+ describe "toggle_deleted method" do
123
+ before :each do
124
+ @configuration = ThinkingSphinx::Configuration.instance
125
+ @configuration.stub_methods(
126
+ :address => "an address",
127
+ :port => 123
128
+ )
129
+ @client = Riddle::Client.stub_instance(:update => true)
130
+ @person = Person.find(:first)
131
+
132
+ Riddle::Client.stub_method(:new => @client)
133
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => false) }
134
+ @person.stub_method(:in_core_index? => true)
135
+ end
136
+
137
+ it "should create a client using the Configuration's address and port" do
138
+ @person.toggle_deleted
139
+
140
+ Riddle::Client.should have_received(:new).with(
141
+ @configuration.address, @configuration.port
142
+ )
143
+ end
144
+
145
+ it "should update the core index's deleted flag if in core index" do
146
+ @person.toggle_deleted
147
+
148
+ @client.should have_received(:update).with(
149
+ "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
150
+ )
151
+ end
152
+
153
+ it "shouldn't update the core index's deleted flag if the record isn't in it" do
154
+ @person.stub_method(:in_core_index? => false)
155
+
156
+ @person.toggle_deleted
157
+
158
+ @client.should_not have_received(:update).with(
159
+ "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
160
+ )
161
+ end
162
+
163
+ it "should update the delta index's deleted flag if delta indexes are enabled and the instance's delta is true" do
164
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
165
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) }
166
+ @person.delta = true
167
+
168
+ @person.toggle_deleted
169
+
170
+ @client.should have_received(:update).with(
171
+ "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
172
+ )
173
+ end
174
+
175
+ it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is false" do
176
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
177
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) }
178
+ @person.delta = false
179
+
180
+ @person.toggle_deleted
181
+
182
+ @client.should_not have_received(:update).with(
183
+ "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
184
+ )
185
+ end
186
+
187
+ it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is equivalent to false" do
188
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
189
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) }
190
+ @person.delta = 0
191
+
192
+ @person.toggle_deleted
193
+
194
+ @client.should_not have_received(:update).with(
195
+ "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
196
+ )
197
+ end
198
+
199
+ it "shouldn't update the delta index if delta indexes are disabled" do
200
+ ThinkingSphinx.stub_method(:deltas_enabled? => true)
201
+ @person.toggle_deleted
202
+
203
+ @client.should_not have_received(:update).with(
204
+ "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
205
+ )
206
+ end
207
+
208
+ it "should not update the delta index if delta indexing is disabled" do
209
+ ThinkingSphinx.stub_method(:deltas_enabled? => false)
210
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) }
211
+ @person.delta = true
212
+
213
+ @person.toggle_deleted
214
+
215
+ @client.should_not have_received(:update).with(
216
+ "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1}
217
+ )
218
+ end
219
+
220
+ it "should not update either index if updates are disabled" do
221
+ ThinkingSphinx.stub_methods(
222
+ :updates_enabled? => false,
223
+ :deltas_enabled => true
224
+ )
225
+ Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) }
226
+ @person.delta = true
227
+
228
+ @person.toggle_deleted
229
+
230
+ @client.should_not have_received(:update)
231
+ end
232
+ end
233
+
234
+ describe "sphinx_indexes in the inheritance chain (STI)" do
235
+ it "should hand defined indexes on a class down to its child classes" do
236
+ Child.sphinx_indexes.should include(*Person.sphinx_indexes)
237
+ end
238
+
239
+ it "should allow associations to other STI models" do
240
+ Child.sphinx_indexes.last.link!
241
+ sql = Child.sphinx_indexes.last.to_sql.gsub('$start', '0').gsub('$end', '100')
242
+ lambda { Child.connection.execute(sql) }.should_not raise_error(ActiveRecord::StatementInvalid)
243
+ end
244
+ end
245
+
246
+ it "should return the sphinx document id as expected" do
247
+ person = Person.find(:first)
248
+ model_count = ThinkingSphinx.indexed_models.length
249
+ offset = ThinkingSphinx.indexed_models.index("Person")
250
+
251
+ (person.id * model_count + offset).should == person.sphinx_document_id
252
+
253
+ alpha = Alpha.find(:first)
254
+ offset = ThinkingSphinx.indexed_models.index("Alpha")
255
+
256
+ (alpha.id * model_count + offset).should == alpha.sphinx_document_id
257
+
258
+ beta = Beta.find(:first)
259
+ offset = ThinkingSphinx.indexed_models.index("Beta")
260
+
261
+ (beta.id * model_count + offset).should == beta.sphinx_document_id
262
+ end
263
+
264
+ it "should remove instances from the core index if they're in it" do
265
+ Beta.search("three").should_not be_empty
266
+
267
+ beta = Beta.find(:first, :conditions => {:name => "three"})
268
+ beta.destroy
269
+
270
+ Beta.search("three").should be_empty
271
+ end
272
+
273
+ it "should remove destroyed new instances from the delta index if they're in it" do
274
+ beta = Beta.create!(:name => "eleven")
275
+ sleep(1) # wait for Sphinx to catch up
276
+
277
+ Beta.search("eleven").should_not be_empty
278
+
279
+ beta.destroy
280
+
281
+ Beta.search("eleven").should be_empty
282
+ end
283
+
284
+ it "should remove destroyed edited instances from the delta index if they're in it" do
285
+ beta = Beta.find(:first, :conditions => {:name => "four"})
286
+ beta.update_attributes(:name => "fourteen")
287
+ sleep(1) # wait for Sphinx to catch up
288
+
289
+ Beta.search("fourteen").should_not be_empty
290
+
291
+ beta.destroy
292
+
293
+ Beta.search("fourteen").should be_empty
294
+ end
295
+ end