jaikoo-thinking-sphinx 0.9.10

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