thinking-sphinx 1.2.12
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.
- data/LICENCE +20 -0
- data/README.textile +157 -0
- data/VERSION.yml +4 -0
- data/lib/thinking_sphinx.rb +211 -0
- data/lib/thinking_sphinx/active_record.rb +307 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +87 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +28 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
- data/lib/thinking_sphinx/association.rb +164 -0
- data/lib/thinking_sphinx/attribute.rb +342 -0
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +282 -0
- data/lib/thinking_sphinx/core/array.rb +7 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas.rb +30 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +30 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +125 -0
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/field.rb +82 -0
- data/lib/thinking_sphinx/index.rb +99 -0
- data/lib/thinking_sphinx/index/builder.rb +286 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/property.rb +162 -0
- data/lib/thinking_sphinx/rails_additions.rb +150 -0
- data/lib/thinking_sphinx/search.rb +707 -0
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +128 -0
- data/lib/thinking_sphinx/tasks.rb +165 -0
- data/rails/init.rb +14 -0
- data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +130 -0
- data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
- data/spec/lib/thinking_sphinx/active_record_spec.rb +364 -0
- data/spec/lib/thinking_sphinx/association_spec.rb +239 -0
- data/spec/lib/thinking_sphinx/attribute_spec.rb +500 -0
- data/spec/lib/thinking_sphinx/configuration_spec.rb +268 -0
- data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/lib/thinking_sphinx/facet_spec.rb +333 -0
- data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
- data/spec/lib/thinking_sphinx/index/builder_spec.rb +455 -0
- data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
- data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +1092 -0
- data/spec/lib/thinking_sphinx/source_spec.rb +227 -0
- data/spec/lib/thinking_sphinx_spec.rb +162 -0
- data/tasks/distribution.rb +50 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +83 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +8 -0
- data/vendor/after_commit/lib/after_commit.rb +45 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/vendor/riddle/lib/riddle.rb +30 -0
- data/vendor/riddle/lib/riddle/client.rb +635 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
- data/vendor/riddle/lib/riddle/client/message.rb +66 -0
- data/vendor/riddle/lib/riddle/client/response.rb +84 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +53 -0
- metadata +172 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::Index::FauxColumn do
|
|
4
|
+
describe "coerce class method" do
|
|
5
|
+
before :each do
|
|
6
|
+
@column = stub('column')
|
|
7
|
+
ThinkingSphinx::Index::FauxColumn.stub!(:new => @column)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should return a single faux column if passed a string" do
|
|
11
|
+
ThinkingSphinx::Index::FauxColumn.coerce("string").should == @column
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should return a single faux column if passed a symbol" do
|
|
15
|
+
ThinkingSphinx::Index::FauxColumn.coerce(:string).should == @column
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should return an array of faux columns if passed an array of strings" do
|
|
19
|
+
ThinkingSphinx::Index::FauxColumn.coerce(["one", "two"]).should == [
|
|
20
|
+
@column, @column
|
|
21
|
+
]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should return an array of faux columns if passed an array of symbols" do
|
|
25
|
+
ThinkingSphinx::Index::FauxColumn.coerce([:one, :two]).should == [
|
|
26
|
+
@column, @column
|
|
27
|
+
]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::Index do
|
|
4
|
+
describe "prefix_fields method" do
|
|
5
|
+
before :each do
|
|
6
|
+
@index = ThinkingSphinx::Index.new(Person)
|
|
7
|
+
|
|
8
|
+
@field_a = stub('field', :prefixes => true)
|
|
9
|
+
@field_b = stub('field', :prefixes => false)
|
|
10
|
+
@field_c = stub('field', :prefixes => true)
|
|
11
|
+
|
|
12
|
+
@index.stub!(:fields => [@field_a, @field_b, @field_c])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should return fields that are flagged as prefixed" do
|
|
16
|
+
@index.prefix_fields.should include(@field_a)
|
|
17
|
+
@index.prefix_fields.should include(@field_c)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should not return fields that aren't flagged as prefixed" do
|
|
21
|
+
@index.prefix_fields.should_not include(@field_b)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "infix_fields method" do
|
|
26
|
+
before :each do
|
|
27
|
+
@index = ThinkingSphinx::Index.new(Person)
|
|
28
|
+
|
|
29
|
+
@field_a = stub('field', :infixes => true)
|
|
30
|
+
@field_b = stub('field', :infixes => false)
|
|
31
|
+
@field_c = stub('field', :infixes => true)
|
|
32
|
+
|
|
33
|
+
@index.stub!(:fields => [@field_a, @field_b, @field_c])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should return fields that are flagged as infixed" do
|
|
37
|
+
@index.infix_fields.should include(@field_a)
|
|
38
|
+
@index.infix_fields.should include(@field_c)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should not return fields that aren't flagged as infixed" do
|
|
42
|
+
@index.infix_fields.should_not include(@field_b)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::HashExcept do
|
|
4
|
+
before(:each) do
|
|
5
|
+
@hash = { :number => 20, :letter => 'b', :shape => 'rectangle' }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "except method" do
|
|
9
|
+
it "returns a hash without the specified keys" do
|
|
10
|
+
new_hash = @hash.except(:number)
|
|
11
|
+
new_hash.should_not have_key(:number)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe "except! method" do
|
|
16
|
+
it "modifies hash removing specified keys" do
|
|
17
|
+
@hash.except!(:number)
|
|
18
|
+
@hash.should_not have_key(:number)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "extends Hash" do
|
|
23
|
+
before :each do
|
|
24
|
+
@instance_methods = Hash.instance_methods.collect { |m| m.to_s }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'with except' do
|
|
28
|
+
@instance_methods.include?('except').should be_true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'with except!' do
|
|
32
|
+
@instance_methods.include?('except!').should be_true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe ThinkingSphinx::ArrayExtractOptions do
|
|
38
|
+
describe 'extract_options! method' do
|
|
39
|
+
it 'returns a hash' do
|
|
40
|
+
array = []
|
|
41
|
+
array.extract_options!.should be_kind_of(Hash)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'returns the last option if it is a hash' do
|
|
45
|
+
array = ['a', 'b', {:c => 'd'}]
|
|
46
|
+
array.extract_options!.should == {:c => 'd'}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "extends Array" do
|
|
51
|
+
it 'with extract_options!' do
|
|
52
|
+
Array.instance_methods.collect { |m| m.to_s }.include?('extract_options!').should be_true
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe ThinkingSphinx::AbstractQuotedTableName do
|
|
58
|
+
describe 'quote_table_name method' do
|
|
59
|
+
it 'calls quote_column_name' do
|
|
60
|
+
adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql')
|
|
61
|
+
adapter.should_receive(:quote_column_name).with('messages')
|
|
62
|
+
adapter.quote_table_name('messages')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "extends ActiveRecord::ConnectionAdapters::AbstractAdapter" do
|
|
67
|
+
it 'with quote_table_name' do
|
|
68
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_methods.collect { |m|
|
|
69
|
+
m.to_s
|
|
70
|
+
}.include?('quote_table_name').should be_true
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe ThinkingSphinx::MysqlQuotedTableName do
|
|
76
|
+
describe "quote_table_name method" do
|
|
77
|
+
it 'correctly quotes' do
|
|
78
|
+
adapter = ActiveRecord::Base.connection
|
|
79
|
+
adapter.quote_table_name('thinking_sphinx.messages').should == "`thinking_sphinx`.`messages`"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe "extends ActiveRecord::ConnectionAdapters::MysqlAdapter" do
|
|
84
|
+
it 'with quote_table_name' do
|
|
85
|
+
adapter = defined?(JRUBY_VERSION) ? :JdbcAdapter : :MysqlAdapter
|
|
86
|
+
ActiveRecord::ConnectionAdapters.const_get(adapter).instance_methods.collect { |m|
|
|
87
|
+
m.to_s
|
|
88
|
+
}.include?("quote_table_name").should be_true
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe ThinkingSphinx::ActiveRecordQuotedName do
|
|
94
|
+
describe "quoted_table_name method" do
|
|
95
|
+
it 'returns table name wrappd in quotes' do
|
|
96
|
+
Person.quoted_table_name.should == '`people`'
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "extends ActiveRecord::Base" do
|
|
101
|
+
it 'with quoted_table_name' do
|
|
102
|
+
ActiveRecord::Base.respond_to?("quoted_table_name").should be_true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe ThinkingSphinx::ActiveRecordStoreFullSTIClass do
|
|
108
|
+
describe "store_full_sti_class method" do
|
|
109
|
+
it 'returns false' do
|
|
110
|
+
Person.store_full_sti_class.should be_false
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe "extends ActiveRecord::Base" do
|
|
115
|
+
it 'with store_full_sti_class' do
|
|
116
|
+
ActiveRecord::Base.respond_to?(:store_full_sti_class).should be_true
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
describe ThinkingSphinx::MetaClass do
|
|
122
|
+
describe 'metaclass' do
|
|
123
|
+
it "should exist as an instance method in Object" do
|
|
124
|
+
Object.new.should respond_to('metaclass')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "should return the meta/eigen/singleton class" do
|
|
128
|
+
Object.new.metaclass.should be_a(Class)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class TestModel
|
|
134
|
+
@@squares = 89
|
|
135
|
+
@@circles = 43
|
|
136
|
+
|
|
137
|
+
def number_of_polygons
|
|
138
|
+
@@polygons
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe ThinkingSphinx::ClassAttributeMethods do
|
|
143
|
+
describe "cattr_reader method" do
|
|
144
|
+
it 'creates getters' do
|
|
145
|
+
TestModel.cattr_reader :herbivores
|
|
146
|
+
test_model = TestModel.new
|
|
147
|
+
test_model.respond_to?(:herbivores).should be_true
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'sets the initial value to nil' do
|
|
151
|
+
TestModel.cattr_reader :carnivores
|
|
152
|
+
test_model = TestModel.new
|
|
153
|
+
test_model.carnivores.should be_nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'does not override an existing definition' do
|
|
157
|
+
TestModel.cattr_reader :squares
|
|
158
|
+
test_model = TestModel.new
|
|
159
|
+
test_model.squares.should == 89
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
describe "cattr_writer method" do
|
|
164
|
+
it 'creates setters' do
|
|
165
|
+
TestModel.cattr_writer :herbivores
|
|
166
|
+
test_model = TestModel.new
|
|
167
|
+
test_model.respond_to?(:herbivores=).should be_true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'does not override an existing definition' do
|
|
171
|
+
TestModel.cattr_writer :polygons
|
|
172
|
+
test_model = TestModel.new
|
|
173
|
+
test_model.polygons = 100
|
|
174
|
+
test_model.number_of_polygons.should == 100
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
describe "cattr_accessor method" do
|
|
179
|
+
it 'calls cattr_reader' do
|
|
180
|
+
Class.should_receive(:cattr_reader).with('polygons')
|
|
181
|
+
Class.cattr_accessor('polygons')
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'calls cattr_writer' do
|
|
185
|
+
Class.should_receive(:cattr_writer).with('polygons')
|
|
186
|
+
Class.cattr_accessor('polygons')
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
describe "extends Class" do
|
|
191
|
+
it 'with cattr_reader' do
|
|
192
|
+
Class.respond_to?('cattr_reader').should be_true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'with cattr_writer' do
|
|
196
|
+
Class.respond_to?('cattr_writer').should be_true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'with cattr_accessor' do
|
|
200
|
+
Class.respond_to?('cattr_accessor').should be_true
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ThinkingSphinx::SearchMethods do
|
|
4
|
+
it "should be included into models with indexes" do
|
|
5
|
+
Alpha.included_modules.should include(ThinkingSphinx::SearchMethods)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should not be included into models that don't have indexes" do
|
|
9
|
+
Gamma.included_modules.should_not include(ThinkingSphinx::SearchMethods)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '.search_context' do
|
|
13
|
+
it "should return nil if not within a model" do
|
|
14
|
+
ThinkingSphinx.search_context.should be_nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should return the model if within one" do
|
|
18
|
+
Alpha.search_context.should == Alpha
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '.search' do
|
|
23
|
+
it "should return an instance of ThinkingSphinx::Search" do
|
|
24
|
+
Alpha.search.class.should == ThinkingSphinx::Search
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should set the classes option if not already set" do
|
|
28
|
+
search = Alpha.search
|
|
29
|
+
search.options[:classes].should == [Alpha]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "shouldn't set the classes option if already defined" do
|
|
33
|
+
search = Alpha.search :classes => [Beta]
|
|
34
|
+
search.options[:classes].should == [Beta]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should default to nil for the classes options" do
|
|
38
|
+
ThinkingSphinx.search.options[:classes].should be_nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '.search_for_ids' do
|
|
43
|
+
it "should return an instance of ThinkingSphinx::Search" do
|
|
44
|
+
Alpha.search.class.should == ThinkingSphinx::Search
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should set the classes option if not already set" do
|
|
48
|
+
search = Alpha.search_for_ids
|
|
49
|
+
search.options[:classes].should == [Alpha]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "shouldn't set the classes option if already defined" do
|
|
53
|
+
search = Alpha.search_for_ids :classes => [Beta]
|
|
54
|
+
search.options[:classes].should == [Beta]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should set ids_only to true" do
|
|
58
|
+
search = Alpha.search_for_ids
|
|
59
|
+
search.options[:ids_only].should be_true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '.search_for_id' do
|
|
64
|
+
before :each do
|
|
65
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
66
|
+
@client = Riddle::Client.new
|
|
67
|
+
|
|
68
|
+
@config.stub!(:client => @client)
|
|
69
|
+
@client.stub!(:query => {:matches => [], :total_found => 0})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should set the id range to the given id value" do
|
|
73
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core')
|
|
74
|
+
|
|
75
|
+
@client.id_range.should == (101..101)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should not make any calls to the database" do
|
|
79
|
+
Alpha.should_not_receive(:find)
|
|
80
|
+
|
|
81
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core', :classes => [Alpha])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should return true if there is a record" do
|
|
85
|
+
@client.stub!(:query => {:matches => [
|
|
86
|
+
{:attributes => {'sphinx_internal_id' => 100}}
|
|
87
|
+
], :total_found => 1})
|
|
88
|
+
|
|
89
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core').should be_true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should return false if there isn't a record" do
|
|
93
|
+
ThinkingSphinx.search_for_id(101, 'alpha_core').should be_false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '.count' do
|
|
98
|
+
before :each do
|
|
99
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
100
|
+
@client = Riddle::Client.new
|
|
101
|
+
|
|
102
|
+
@config.stub!(:client => @client)
|
|
103
|
+
@client.stub!(:query => {:matches => [], :total_found => 42})
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should fall through to ActiveRecord if called on a class" do
|
|
107
|
+
@client.should_not_receive(:query)
|
|
108
|
+
|
|
109
|
+
Alpha.count
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it "should return the total number of results if called globally" do
|
|
113
|
+
ThinkingSphinx.count.should == 42
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe '.search_count' do
|
|
118
|
+
before :each do
|
|
119
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
120
|
+
@client = Riddle::Client.new
|
|
121
|
+
|
|
122
|
+
@config.stub!(:client => @client)
|
|
123
|
+
@client.stub!(:query => {:matches => [], :total_found => 42})
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "should return the total number of results" do
|
|
127
|
+
Alpha.search_count.should == 42
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "should not make any calls to the database" do
|
|
131
|
+
Alpha.should_not_receive(:find)
|
|
132
|
+
|
|
133
|
+
Alpha.search_count
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '.facets' do
|
|
138
|
+
it "should return a FacetSearch instance" do
|
|
139
|
+
Alpha.facets.should be_a(ThinkingSphinx::FacetSearch)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should set the classes option if not already set" do
|
|
143
|
+
facets = Alpha.facets
|
|
144
|
+
facets.options[:classes].should == [Alpha]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it "shouldn't set the classes option if already defined" do
|
|
148
|
+
facets = Alpha.facets :classes => [Beta]
|
|
149
|
+
facets.options[:classes].should == [Beta]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,1092 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
require 'will_paginate/collection'
|
|
3
|
+
|
|
4
|
+
describe ThinkingSphinx::Search do
|
|
5
|
+
before :each do
|
|
6
|
+
@config = ThinkingSphinx::Configuration.instance
|
|
7
|
+
@client = Riddle::Client.new
|
|
8
|
+
|
|
9
|
+
@config.stub!(:client => @client)
|
|
10
|
+
@client.stub!(:query => {:matches => [], :total_found => 41, :total => 41})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "not request results from the client if not accessing items" do
|
|
14
|
+
@config.should_not_receive(:client)
|
|
15
|
+
|
|
16
|
+
ThinkingSphinx::Search.new.class
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should request results if access is required" do
|
|
20
|
+
@config.should_receive(:client)
|
|
21
|
+
|
|
22
|
+
ThinkingSphinx::Search.new.first
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#respond_to?' do
|
|
26
|
+
it "should respond to Array methods" do
|
|
27
|
+
ThinkingSphinx::Search.new.respond_to?(:each).should be_true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should respond to Search methods" do
|
|
31
|
+
ThinkingSphinx::Search.new.respond_to?(:per_page).should be_true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#populated?' do
|
|
36
|
+
before :each do
|
|
37
|
+
@search = ThinkingSphinx::Search.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should be false if the client request has not been made" do
|
|
41
|
+
@search.populated?.should be_false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should be true once the client request has been made" do
|
|
45
|
+
@search.first
|
|
46
|
+
@search.populated?.should be_true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe '#results' do
|
|
51
|
+
it "should populate search results before returning" do
|
|
52
|
+
@search = ThinkingSphinx::Search.new
|
|
53
|
+
@search.populated?.should be_false
|
|
54
|
+
|
|
55
|
+
@search.results
|
|
56
|
+
@search.populated?.should be_true
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe '#method_missing' do
|
|
61
|
+
before :each do
|
|
62
|
+
Alpha.sphinx_scope(:by_name) { |name|
|
|
63
|
+
{:conditions => {:name => name}}
|
|
64
|
+
}
|
|
65
|
+
Alpha.sphinx_scope(:ids_only) { {:ids_only => true} }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
after :each do
|
|
69
|
+
Alpha.remove_sphinx_scopes
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should handle Array methods" do
|
|
73
|
+
ThinkingSphinx::Search.new.private_methods.should be_an(Array)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should raise a NoMethodError exception if unknown method" do
|
|
77
|
+
lambda {
|
|
78
|
+
ThinkingSphinx::Search.new.foo
|
|
79
|
+
}.should raise_error(NoMethodError)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should not request results from client if method does not exist" do
|
|
83
|
+
@client.should_not_receive(:query)
|
|
84
|
+
|
|
85
|
+
lambda {
|
|
86
|
+
ThinkingSphinx::Search.new.foo
|
|
87
|
+
}.should raise_error(NoMethodError)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "should accept sphinx scopes" do
|
|
91
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
92
|
+
|
|
93
|
+
lambda {
|
|
94
|
+
search.by_name('Pat')
|
|
95
|
+
}.should_not raise_error(NoMethodError)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should return itself when using a sphinx scope" do
|
|
99
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
100
|
+
search.by_name('Pat').object_id.should == search.object_id
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should keep the same search object when chaining multiple scopes" do
|
|
104
|
+
search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
105
|
+
search.by_name('Pat').ids_only.object_id.should == search.object_id
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe '.search' do
|
|
110
|
+
it "return the output of ThinkingSphinx.search" do
|
|
111
|
+
@results = [] # to confirm same object
|
|
112
|
+
ThinkingSphinx.stub!(:search => @results)
|
|
113
|
+
|
|
114
|
+
ThinkingSphinx::Search.search.object_id.should == @results.object_id
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '.search_for_ids' do
|
|
119
|
+
it "return the output of ThinkingSphinx.search_for_ids" do
|
|
120
|
+
@results = [] # to confirm same object
|
|
121
|
+
ThinkingSphinx.stub!(:search_for_ids => @results)
|
|
122
|
+
|
|
123
|
+
ThinkingSphinx::Search.search_for_ids.object_id.
|
|
124
|
+
should == @results.object_id
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe '.search_for_id' do
|
|
129
|
+
it "return the output of ThinkingSphinx.search_for_ids" do
|
|
130
|
+
@results = [] # to confirm same object
|
|
131
|
+
ThinkingSphinx.stub!(:search_for_id => @results)
|
|
132
|
+
|
|
133
|
+
ThinkingSphinx::Search.search_for_id.object_id.
|
|
134
|
+
should == @results.object_id
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe '.count' do
|
|
139
|
+
it "return the output of ThinkingSphinx.search" do
|
|
140
|
+
@results = [] # to confirm same object
|
|
141
|
+
ThinkingSphinx.stub!(:count => @results)
|
|
142
|
+
|
|
143
|
+
ThinkingSphinx::Search.count.object_id.should == @results.object_id
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
describe '.facets' do
|
|
148
|
+
it "return the output of ThinkingSphinx.facets" do
|
|
149
|
+
@results = [] # to confirm same object
|
|
150
|
+
ThinkingSphinx.stub!(:facets => @results)
|
|
151
|
+
|
|
152
|
+
ThinkingSphinx::Search.facets.object_id.should == @results.object_id
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
describe '#populate' do
|
|
157
|
+
before :each do
|
|
158
|
+
@alpha_a, @alpha_b = Alpha.new, Alpha.new
|
|
159
|
+
@beta_a, @beta_b = Beta.new, Beta.new
|
|
160
|
+
|
|
161
|
+
@alpha_a.stub! :id => 1, :read_attribute => 1
|
|
162
|
+
@alpha_b.stub! :id => 2, :read_attribute => 2
|
|
163
|
+
@beta_a.stub! :id => 1, :read_attribute => 1
|
|
164
|
+
@beta_b.stub! :id => 2, :read_attribute => 2
|
|
165
|
+
|
|
166
|
+
@client.stub! :query => {
|
|
167
|
+
:matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a)
|
|
168
|
+
}
|
|
169
|
+
Alpha.stub! :find => [@alpha_a, @alpha_b]
|
|
170
|
+
Beta.stub! :find => [@beta_a, @beta_b]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "should issue only one select per model" do
|
|
174
|
+
Alpha.should_receive(:find).once.and_return([@alpha_a, @alpha_b])
|
|
175
|
+
Beta.should_receive(:find).once.and_return([@beta_a, @beta_b])
|
|
176
|
+
|
|
177
|
+
ThinkingSphinx::Search.new.first
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "should mix the results from different models" do
|
|
181
|
+
search = ThinkingSphinx::Search.new
|
|
182
|
+
search[0].should be_a(Alpha)
|
|
183
|
+
search[1].should be_a(Beta)
|
|
184
|
+
search[2].should be_a(Alpha)
|
|
185
|
+
search[3].should be_a(Beta)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "should maintain the Xoopit ordering for results" do
|
|
189
|
+
search = ThinkingSphinx::Search.new
|
|
190
|
+
search[0].id.should == 1
|
|
191
|
+
search[1].id.should == 2
|
|
192
|
+
search[2].id.should == 2
|
|
193
|
+
search[3].id.should == 1
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "should use the requested classes to generate the index argument" do
|
|
197
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
198
|
+
index.should == 'alpha_core,beta_core,beta_delta'
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe 'query' do
|
|
205
|
+
it "should concatenate arguments with spaces" do
|
|
206
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
207
|
+
query.should == 'two words'
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
ThinkingSphinx::Search.new('two', 'words').first
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "should append conditions to the query" do
|
|
214
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
215
|
+
query.should == 'general @focused specific'
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
219
|
+
:focused => 'specific'
|
|
220
|
+
}).first
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it "append multiple conditions together" do
|
|
224
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
225
|
+
query.should match(/general.+@foo word/)
|
|
226
|
+
query.should match(/general.+@bar word/)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
230
|
+
:foo => 'word', :bar => 'word'
|
|
231
|
+
}).first
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it "should apply stars if requested, and handle full extended syntax" do
|
|
235
|
+
input = %{a b* c (d | e) 123 5&6 (f_f g) !h "i j" "k l"~10 "m n"/3 @o p -(q|r)}
|
|
236
|
+
expected = %{*a* b* *c* (*d* | *e*) *123* *5*&*6* (*f_f* *g*) !*h* "i j" "k l"~10 "m n"/3 @o *p* -(*q*|*r*)}
|
|
237
|
+
|
|
238
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
239
|
+
query.should == expected
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
ThinkingSphinx::Search.new(input, :star => true).first
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "should default to /\w+/ as token for auto-starring" do
|
|
246
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
247
|
+
query.should == '*foo*@*bar*.*com*'
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
ThinkingSphinx::Search.new('foo@bar.com', :star => true).first
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "should honour custom star tokens" do
|
|
254
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
255
|
+
query.should == '*foo@bar.com* -*foo-bar*'
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
ThinkingSphinx::Search.new(
|
|
259
|
+
'foo@bar.com -foo-bar', :star => /[\w@.-]+/u
|
|
260
|
+
).first
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
describe 'comment' do
|
|
265
|
+
it "should add comment if explicitly provided" do
|
|
266
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
267
|
+
comment.should == 'custom log'
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
ThinkingSphinx::Search.new(:comment => 'custom log').first
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it "should default to a blank comment" do
|
|
274
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
275
|
+
comment.should == ''
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
ThinkingSphinx::Search.new.first
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
describe 'match mode' do
|
|
283
|
+
it "should default to :all" do
|
|
284
|
+
ThinkingSphinx::Search.new.first
|
|
285
|
+
|
|
286
|
+
@client.match_mode.should == :all
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "should default to :extended if conditions are supplied" do
|
|
290
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
291
|
+
:foo => 'word', :bar => 'word'
|
|
292
|
+
}).first
|
|
293
|
+
|
|
294
|
+
@client.match_mode.should == :extended
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
it "should use explicit match modes" do
|
|
298
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
|
299
|
+
:foo => 'word', :bar => 'word'
|
|
300
|
+
}, :match_mode => :extended2).first
|
|
301
|
+
|
|
302
|
+
@client.match_mode.should == :extended2
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
describe 'pagination' do
|
|
307
|
+
it "should set the limit using per_page" do
|
|
308
|
+
ThinkingSphinx::Search.new(:per_page => 30).first
|
|
309
|
+
@client.limit.should == 30
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it "should set the offset if pagination is requested" do
|
|
313
|
+
ThinkingSphinx::Search.new(:page => 3).first
|
|
314
|
+
@client.offset.should == 40
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it "should set the offset by the per_page value" do
|
|
318
|
+
ThinkingSphinx::Search.new(:page => 3, :per_page => 30).first
|
|
319
|
+
@client.offset.should == 60
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
describe 'filters' do
|
|
324
|
+
it "should filter out deleted values by default" do
|
|
325
|
+
ThinkingSphinx::Search.new.first
|
|
326
|
+
|
|
327
|
+
filter = @client.filters.last
|
|
328
|
+
filter.values.should == [0]
|
|
329
|
+
filter.attribute.should == 'sphinx_deleted'
|
|
330
|
+
filter.exclude?.should be_false
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "should add class filters for explicit classes" do
|
|
334
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
|
|
335
|
+
|
|
336
|
+
filter = @client.filters.last
|
|
337
|
+
filter.values.should == [Alpha.to_crc32, Beta.to_crc32]
|
|
338
|
+
filter.attribute.should == 'class_crc'
|
|
339
|
+
filter.exclude?.should be_false
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it "should add class filters for subclasses of requested classes" do
|
|
343
|
+
ThinkingSphinx::Search.new(:classes => [Person]).first
|
|
344
|
+
|
|
345
|
+
filter = @client.filters.last
|
|
346
|
+
filter.values.should == [
|
|
347
|
+
Parent.to_crc32, Admin::Person.to_crc32,
|
|
348
|
+
Child.to_crc32, Person.to_crc32
|
|
349
|
+
]
|
|
350
|
+
filter.attribute.should == 'class_crc'
|
|
351
|
+
filter.exclude?.should be_false
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
it "should append inclusive filters of integers" do
|
|
355
|
+
ThinkingSphinx::Search.new(:with => {:int => 1}).first
|
|
356
|
+
|
|
357
|
+
filter = @client.filters.last
|
|
358
|
+
filter.values.should == [1]
|
|
359
|
+
filter.attribute.should == 'int'
|
|
360
|
+
filter.exclude?.should be_false
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "should append inclusive filters of floats" do
|
|
364
|
+
ThinkingSphinx::Search.new(:with => {:float => 1.5}).first
|
|
365
|
+
|
|
366
|
+
filter = @client.filters.last
|
|
367
|
+
filter.values.should == [1.5]
|
|
368
|
+
filter.attribute.should == 'float'
|
|
369
|
+
filter.exclude?.should be_false
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it "should append inclusive filters of booleans" do
|
|
373
|
+
ThinkingSphinx::Search.new(:with => {:boolean => true}).first
|
|
374
|
+
|
|
375
|
+
filter = @client.filters.last
|
|
376
|
+
filter.values.should == [true]
|
|
377
|
+
filter.attribute.should == 'boolean'
|
|
378
|
+
filter.exclude?.should be_false
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it "should append inclusive filters of arrays" do
|
|
382
|
+
ThinkingSphinx::Search.new(:with => {:ints => [1, 2, 3]}).first
|
|
383
|
+
|
|
384
|
+
filter = @client.filters.last
|
|
385
|
+
filter.values.should == [1, 2, 3]
|
|
386
|
+
filter.attribute.should == 'ints'
|
|
387
|
+
filter.exclude?.should be_false
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it "should treat nils in arrays as 0" do
|
|
391
|
+
ThinkingSphinx::Search.new(:with => {:ints => [nil, 1, 2, 3]}).first
|
|
392
|
+
|
|
393
|
+
filter = @client.filters.last
|
|
394
|
+
filter.values.should == [0, 1, 2, 3]
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it "should append inclusive filters of time ranges" do
|
|
398
|
+
first, last = 1.week.ago, Time.now
|
|
399
|
+
ThinkingSphinx::Search.new(:with => {
|
|
400
|
+
:time => first..last
|
|
401
|
+
}).first
|
|
402
|
+
|
|
403
|
+
filter = @client.filters.last
|
|
404
|
+
filter.values.should == (first.to_i..last.to_i)
|
|
405
|
+
filter.attribute.should == 'time'
|
|
406
|
+
filter.exclude?.should be_false
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
it "should append exclusive filters of integers" do
|
|
410
|
+
ThinkingSphinx::Search.new(:without => {:int => 1}).first
|
|
411
|
+
|
|
412
|
+
filter = @client.filters.last
|
|
413
|
+
filter.values.should == [1]
|
|
414
|
+
filter.attribute.should == 'int'
|
|
415
|
+
filter.exclude?.should be_true
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
it "should append exclusive filters of floats" do
|
|
419
|
+
ThinkingSphinx::Search.new(:without => {:float => 1.5}).first
|
|
420
|
+
|
|
421
|
+
filter = @client.filters.last
|
|
422
|
+
filter.values.should == [1.5]
|
|
423
|
+
filter.attribute.should == 'float'
|
|
424
|
+
filter.exclude?.should be_true
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
it "should append exclusive filters of booleans" do
|
|
428
|
+
ThinkingSphinx::Search.new(:without => {:boolean => true}).first
|
|
429
|
+
|
|
430
|
+
filter = @client.filters.last
|
|
431
|
+
filter.values.should == [true]
|
|
432
|
+
filter.attribute.should == 'boolean'
|
|
433
|
+
filter.exclude?.should be_true
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
it "should append exclusive filters of arrays" do
|
|
437
|
+
ThinkingSphinx::Search.new(:without => {:ints => [1, 2, 3]}).first
|
|
438
|
+
|
|
439
|
+
filter = @client.filters.last
|
|
440
|
+
filter.values.should == [1, 2, 3]
|
|
441
|
+
filter.attribute.should == 'ints'
|
|
442
|
+
filter.exclude?.should be_true
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it "should append exclusive filters of time ranges" do
|
|
446
|
+
first, last = 1.week.ago, Time.now
|
|
447
|
+
ThinkingSphinx::Search.new(:without => {
|
|
448
|
+
:time => first..last
|
|
449
|
+
}).first
|
|
450
|
+
|
|
451
|
+
filter = @client.filters.last
|
|
452
|
+
filter.values.should == (first.to_i..last.to_i)
|
|
453
|
+
filter.attribute.should == 'time'
|
|
454
|
+
filter.exclude?.should be_true
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
it "should add separate filters for each item in a with_all value" do
|
|
458
|
+
ThinkingSphinx::Search.new(:with_all => {:ints => [1, 2, 3]}).first
|
|
459
|
+
|
|
460
|
+
filters = @client.filters[-3, 3]
|
|
461
|
+
filters.each do |filter|
|
|
462
|
+
filter.attribute.should == 'ints'
|
|
463
|
+
filter.exclude?.should be_false
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
filters[0].values.should == [1]
|
|
467
|
+
filters[1].values.should == [2]
|
|
468
|
+
filters[2].values.should == [3]
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
it "should filter out specific ids using :without_ids" do
|
|
472
|
+
ThinkingSphinx::Search.new(:without_ids => [4, 5, 6]).first
|
|
473
|
+
|
|
474
|
+
filter = @client.filters.last
|
|
475
|
+
filter.values.should == [4, 5, 6]
|
|
476
|
+
filter.attribute.should == 'sphinx_internal_id'
|
|
477
|
+
filter.exclude?.should be_true
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
describe 'in :conditions' do
|
|
481
|
+
it "should add as filters for known attributes in :conditions option" do
|
|
482
|
+
ThinkingSphinx::Search.new('general',
|
|
483
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
|
484
|
+
:classes => [Alpha]
|
|
485
|
+
).first
|
|
486
|
+
|
|
487
|
+
filter = @client.filters.last
|
|
488
|
+
filter.values.should == [1.5]
|
|
489
|
+
filter.attribute.should == 'lat'
|
|
490
|
+
filter.exclude?.should be_false
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
it "should not add the filter to the query string" do
|
|
494
|
+
@client.should_receive(:query) do |query, index, comment|
|
|
495
|
+
query.should == 'general @word specific'
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
ThinkingSphinx::Search.new('general',
|
|
499
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
|
500
|
+
:classes => [Alpha]
|
|
501
|
+
).first
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
describe 'sort mode' do
|
|
507
|
+
it "should use :relevance as a default" do
|
|
508
|
+
ThinkingSphinx::Search.new.first
|
|
509
|
+
@client.sort_mode.should == :relevance
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "should use :attr_asc if a symbol is supplied to :order" do
|
|
513
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
|
514
|
+
@client.sort_mode.should == :attr_asc
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
it "should use :attr_desc if :desc is the mode" do
|
|
518
|
+
ThinkingSphinx::Search.new(
|
|
519
|
+
:order => :created_at, :sort_mode => :desc
|
|
520
|
+
).first
|
|
521
|
+
@client.sort_mode.should == :attr_desc
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
it "should use :extended if a string is supplied to :order" do
|
|
525
|
+
ThinkingSphinx::Search.new(:order => "created_at ASC").first
|
|
526
|
+
@client.sort_mode.should == :extended
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
it "should use :expr if explicitly requested" do
|
|
530
|
+
ThinkingSphinx::Search.new(
|
|
531
|
+
:order => "created_at ASC", :sort_mode => :expr
|
|
532
|
+
).first
|
|
533
|
+
@client.sort_mode.should == :expr
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
it "should use :attr_desc if explicitly requested" do
|
|
537
|
+
ThinkingSphinx::Search.new(
|
|
538
|
+
:order => "created_at", :sort_mode => :desc
|
|
539
|
+
).first
|
|
540
|
+
@client.sort_mode.should == :attr_desc
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
describe 'sort by' do
|
|
545
|
+
it "should presume order symbols are attributes" do
|
|
546
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
|
547
|
+
@client.sort_by.should == 'created_at'
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
it "replace field names with their sortable attributes" do
|
|
551
|
+
ThinkingSphinx::Search.new(:order => :name, :classes => [Alpha]).first
|
|
552
|
+
@client.sort_by.should == 'name_sort'
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
it "should replace field names in strings" do
|
|
556
|
+
ThinkingSphinx::Search.new(
|
|
557
|
+
:order => "created_at ASC, name DESC", :classes => [Alpha]
|
|
558
|
+
).first
|
|
559
|
+
@client.sort_by.should == 'created_at ASC, name_sort DESC'
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
describe 'max matches' do
|
|
564
|
+
it "should use the global setting by default" do
|
|
565
|
+
ThinkingSphinx::Search.new.first
|
|
566
|
+
@client.max_matches.should == 1000
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
it "should use explicit setting" do
|
|
570
|
+
ThinkingSphinx::Search.new(:max_matches => 2000).first
|
|
571
|
+
@client.max_matches.should == 2000
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
describe 'field weights' do
|
|
576
|
+
it "should set field weights as provided" do
|
|
577
|
+
ThinkingSphinx::Search.new(
|
|
578
|
+
:field_weights => {'foo' => 10, 'bar' => 5}
|
|
579
|
+
).first
|
|
580
|
+
|
|
581
|
+
@client.field_weights.should == {
|
|
582
|
+
'foo' => 10, 'bar' => 5
|
|
583
|
+
}
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it "should use field weights set in the index" do
|
|
587
|
+
ThinkingSphinx::Search.new(:classes => [Alpha]).first
|
|
588
|
+
|
|
589
|
+
@client.field_weights.should == {'name' => 10}
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
describe 'index weights' do
|
|
594
|
+
it "should send index weights through to the client" do
|
|
595
|
+
ThinkingSphinx::Search.new(:index_weights => {'foo' => 100}).first
|
|
596
|
+
@client.index_weights.should == {'foo' => 100}
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
it "should convert classes to their core and delta index names" do
|
|
600
|
+
ThinkingSphinx::Search.new(:index_weights => {Alpha => 100}).first
|
|
601
|
+
@client.index_weights.should == {
|
|
602
|
+
'alpha_core' => 100,
|
|
603
|
+
'alpha_delta' => 100
|
|
604
|
+
}
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
describe 'grouping' do
|
|
609
|
+
it "should convert group into group_by and group_function" do
|
|
610
|
+
ThinkingSphinx::Search.new(:group => :edition).first
|
|
611
|
+
|
|
612
|
+
@client.group_function.should == :attr
|
|
613
|
+
@client.group_by.should == "edition"
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it "should pass on explicit grouping arguments" do
|
|
617
|
+
ThinkingSphinx::Search.new(
|
|
618
|
+
:group_by => 'created_at',
|
|
619
|
+
:group_function => :attr,
|
|
620
|
+
:group_clause => 'clause',
|
|
621
|
+
:group_distinct => 'distinct'
|
|
622
|
+
).first
|
|
623
|
+
|
|
624
|
+
@client.group_by.should == 'created_at'
|
|
625
|
+
@client.group_function.should == :attr
|
|
626
|
+
@client.group_clause.should == 'clause'
|
|
627
|
+
@client.group_distinct.should == 'distinct'
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
describe 'anchor' do
|
|
632
|
+
it "should detect lat and lng attributes on the given model" do
|
|
633
|
+
ThinkingSphinx::Search.new(
|
|
634
|
+
:geo => [1.0, -1.0],
|
|
635
|
+
:classes => [Alpha]
|
|
636
|
+
).first
|
|
637
|
+
|
|
638
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
|
639
|
+
@client.anchor[:longitude_attribute].should == 'lng'
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
it "should detect lat and lon attributes on the given model" do
|
|
643
|
+
ThinkingSphinx::Search.new(
|
|
644
|
+
:geo => [1.0, -1.0],
|
|
645
|
+
:classes => [Beta]
|
|
646
|
+
).first
|
|
647
|
+
|
|
648
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
|
649
|
+
@client.anchor[:longitude_attribute].should == 'lon'
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
it "should detect latitude and longitude attributes on the given model" do
|
|
653
|
+
ThinkingSphinx::Search.new(
|
|
654
|
+
:geo => [1.0, -1.0],
|
|
655
|
+
:classes => [Person]
|
|
656
|
+
).first
|
|
657
|
+
|
|
658
|
+
@client.anchor[:latitude_attribute].should == 'latitude'
|
|
659
|
+
@client.anchor[:longitude_attribute].should == 'longitude'
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
it "should accept manually defined latitude and longitude attributes" do
|
|
663
|
+
ThinkingSphinx::Search.new(
|
|
664
|
+
:geo => [1.0, -1.0],
|
|
665
|
+
:classes => [Alpha],
|
|
666
|
+
:latitude_attr => :updown,
|
|
667
|
+
:longitude_attr => :leftright
|
|
668
|
+
).first
|
|
669
|
+
|
|
670
|
+
@client.anchor[:latitude_attribute].should == 'updown'
|
|
671
|
+
@client.anchor[:longitude_attribute].should == 'leftright'
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it "should accept manually defined latitude and longitude attributes in the given model" do
|
|
675
|
+
ThinkingSphinx::Search.new(
|
|
676
|
+
:geo => [1.0, -1.0],
|
|
677
|
+
:classes => [Friendship]
|
|
678
|
+
).first
|
|
679
|
+
|
|
680
|
+
@client.anchor[:latitude_attribute].should == 'person_id'
|
|
681
|
+
@client.anchor[:longitude_attribute].should == 'person_id'
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
it "should accept geo array for geo-position values" do
|
|
685
|
+
ThinkingSphinx::Search.new(
|
|
686
|
+
:geo => [1.0, -1.0],
|
|
687
|
+
:classes => [Alpha]
|
|
688
|
+
).first
|
|
689
|
+
|
|
690
|
+
@client.anchor[:latitude].should == 1.0
|
|
691
|
+
@client.anchor[:longitude].should == -1.0
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
it "should accept lat and lng options for geo-position values" do
|
|
695
|
+
ThinkingSphinx::Search.new(
|
|
696
|
+
:lat => 1.0,
|
|
697
|
+
:lng => -1.0,
|
|
698
|
+
:classes => [Alpha]
|
|
699
|
+
).first
|
|
700
|
+
|
|
701
|
+
@client.anchor[:latitude].should == 1.0
|
|
702
|
+
@client.anchor[:longitude].should == -1.0
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
describe 'sql ordering' do
|
|
707
|
+
before :each do
|
|
708
|
+
@client.stub! :query => {
|
|
709
|
+
:matches => minimal_result_hashes(@alpha_b, @alpha_a)
|
|
710
|
+
}
|
|
711
|
+
Alpha.stub! :find => [@alpha_a, @alpha_b]
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
it "shouldn't re-sort SQL results based on Sphinx information" do
|
|
715
|
+
search = ThinkingSphinx::Search.new(
|
|
716
|
+
:classes => [Alpha],
|
|
717
|
+
:sql_order => 'id'
|
|
718
|
+
)
|
|
719
|
+
search.first.should == @alpha_a
|
|
720
|
+
search.last.should == @alpha_b
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
it "should use the option for the ActiveRecord::Base#find calls" do
|
|
724
|
+
Alpha.should_receive(:find) do |mode, options|
|
|
725
|
+
options[:order].should == 'id'
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
ThinkingSphinx::Search.new(
|
|
729
|
+
:classes => [Alpha],
|
|
730
|
+
:sql_order => 'id'
|
|
731
|
+
).first
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
context 'result objects' do
|
|
736
|
+
describe '#excerpts' do
|
|
737
|
+
before :each do
|
|
738
|
+
@search = ThinkingSphinx::Search.new
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
it "should add excerpts method if objects don't already have one" do
|
|
742
|
+
@search.first.should respond_to(:excerpts)
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
it "should return an instance of ThinkingSphinx::Excerpter" do
|
|
746
|
+
@search.first.excerpts.should be_a(ThinkingSphinx::Excerpter)
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
it "should not add excerpts method if objects already have one" do
|
|
750
|
+
@search.last.excerpts.should_not be_a(ThinkingSphinx::Excerpter)
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
it "should set up the excerpter with the instances and search" do
|
|
754
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_a)
|
|
755
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_b)
|
|
756
|
+
|
|
757
|
+
@search.first
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
describe '#sphinx_attributes' do
|
|
762
|
+
before :each do
|
|
763
|
+
@search = ThinkingSphinx::Search.new
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
it "should add sphinx_attributes method if objects don't already have one" do
|
|
767
|
+
@search.last.should respond_to(:sphinx_attributes)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
it "should return a hash" do
|
|
771
|
+
@search.last.sphinx_attributes.should be_a(Hash)
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
it "should not add sphinx_attributes if objects have a method of that name already" do
|
|
775
|
+
@search.first.sphinx_attributes.should_not be_a(Hash)
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
it "should pair sphinx_attributes with the correct hash" do
|
|
779
|
+
hash = @search.last.sphinx_attributes
|
|
780
|
+
hash['sphinx_internal_id'].should == @search.last.id
|
|
781
|
+
hash['class_crc'].should == @search.last.class.to_crc32
|
|
782
|
+
end
|
|
783
|
+
end
|
|
784
|
+
end
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
describe '#current_page' do
|
|
788
|
+
it "should return 1 by default" do
|
|
789
|
+
ThinkingSphinx::Search.new.current_page.should == 1
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
it "should handle string page values" do
|
|
793
|
+
ThinkingSphinx::Search.new(:page => '2').current_page.should == 2
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
it "should handle empty string page values" do
|
|
797
|
+
ThinkingSphinx::Search.new(:page => '').current_page.should == 1
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
it "should return the requested page" do
|
|
801
|
+
ThinkingSphinx::Search.new(:page => 10).current_page.should == 10
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
describe '#per_page' do
|
|
806
|
+
it "should return 20 by default" do
|
|
807
|
+
ThinkingSphinx::Search.new.per_page.should == 20
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
it "should allow for custom values" do
|
|
811
|
+
ThinkingSphinx::Search.new(:per_page => 30).per_page.should == 30
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
it "should prioritise :limit over :per_page if given" do
|
|
815
|
+
ThinkingSphinx::Search.new(
|
|
816
|
+
:per_page => 30, :limit => 40
|
|
817
|
+
).per_page.should == 40
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
describe '#total_pages' do
|
|
822
|
+
it "should calculate the total pages depending on per_page and total_entries" do
|
|
823
|
+
ThinkingSphinx::Search.new.total_pages.should == 3
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
it "should allow for custom per_page values" do
|
|
827
|
+
ThinkingSphinx::Search.new(:per_page => 30).total_pages.should == 2
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
it "should not overstep the max_matches implied limit" do
|
|
831
|
+
@client.stub!(:query => {
|
|
832
|
+
:matches => [], :total_found => 41, :total => 40
|
|
833
|
+
})
|
|
834
|
+
|
|
835
|
+
ThinkingSphinx::Search.new.total_pages.should == 2
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
describe '#next_page' do
|
|
840
|
+
it "should return one more than the current page" do
|
|
841
|
+
ThinkingSphinx::Search.new.next_page.should == 2
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
it "should return nil if on the last page" do
|
|
845
|
+
ThinkingSphinx::Search.new(:page => 3).next_page.should be_nil
|
|
846
|
+
end
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
describe '#previous_page' do
|
|
850
|
+
it "should return one less than the current page" do
|
|
851
|
+
ThinkingSphinx::Search.new(:page => 2).previous_page.should == 1
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
it "should return nil if on the first page" do
|
|
855
|
+
ThinkingSphinx::Search.new.previous_page.should be_nil
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
describe '#total_entries' do
|
|
860
|
+
it "should return the total number of results, not just the amount on the page" do
|
|
861
|
+
ThinkingSphinx::Search.new.total_entries.should == 41
|
|
862
|
+
end
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
describe '#offset' do
|
|
866
|
+
it "should default to 0" do
|
|
867
|
+
ThinkingSphinx::Search.new.offset.should == 0
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
it "should increase by the per_page value for each page in" do
|
|
871
|
+
ThinkingSphinx::Search.new(:per_page => 25, :page => 2).offset.should == 25
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
describe '#indexes' do
|
|
876
|
+
it "should default to '*'" do
|
|
877
|
+
ThinkingSphinx::Search.new.indexes.should == '*'
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
it "should use given class to determine index name" do
|
|
881
|
+
ThinkingSphinx::Search.new(:classes => [Alpha]).indexes.
|
|
882
|
+
should == 'alpha_core'
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
it "should add both core and delta indexes for given classes" do
|
|
886
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).indexes.
|
|
887
|
+
should == 'alpha_core,beta_core,beta_delta'
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
it "should respect the :index option" do
|
|
891
|
+
ThinkingSphinx::Search.new(:classes => [Alpha], :index => '*').indexes.
|
|
892
|
+
should == '*'
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
describe '.each_with_groupby_and_count' do
|
|
897
|
+
before :each do
|
|
898
|
+
@alpha = Alpha.new
|
|
899
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
900
|
+
|
|
901
|
+
@client.stub! :query => {
|
|
902
|
+
:matches => [{
|
|
903
|
+
:attributes => {
|
|
904
|
+
'sphinx_internal_id' => @alpha.id,
|
|
905
|
+
'class_crc' => Alpha.to_crc32,
|
|
906
|
+
'@groupby' => 101,
|
|
907
|
+
'@count' => 5
|
|
908
|
+
}
|
|
909
|
+
}]
|
|
910
|
+
}
|
|
911
|
+
Alpha.stub!(:find => [@alpha])
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
it "should yield the match, group and count" do
|
|
915
|
+
search = ThinkingSphinx::Search.new
|
|
916
|
+
search.each_with_groupby_and_count do |obj, group, count|
|
|
917
|
+
obj.should == @alpha
|
|
918
|
+
group.should == 101
|
|
919
|
+
count.should == 5
|
|
920
|
+
end
|
|
921
|
+
end
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
describe '.each_with_weighting' do
|
|
925
|
+
before :each do
|
|
926
|
+
@alpha = Alpha.new
|
|
927
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
928
|
+
|
|
929
|
+
@client.stub! :query => {
|
|
930
|
+
:matches => [{
|
|
931
|
+
:attributes => {
|
|
932
|
+
'sphinx_internal_id' => @alpha.id,
|
|
933
|
+
'class_crc' => Alpha.to_crc32
|
|
934
|
+
}, :weight => 12
|
|
935
|
+
}]
|
|
936
|
+
}
|
|
937
|
+
Alpha.stub!(:find => [@alpha])
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
it "should yield the match and weight" do
|
|
941
|
+
search = ThinkingSphinx::Search.new
|
|
942
|
+
search.each_with_weighting do |obj, weight|
|
|
943
|
+
obj.should == @alpha
|
|
944
|
+
weight.should == 12
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
describe '.each_with_*' do
|
|
950
|
+
before :each do
|
|
951
|
+
@alpha = Alpha.new
|
|
952
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
|
953
|
+
|
|
954
|
+
@client.stub! :query => {
|
|
955
|
+
:matches => [{
|
|
956
|
+
:attributes => {
|
|
957
|
+
'sphinx_internal_id' => @alpha.id,
|
|
958
|
+
'class_crc' => Alpha.to_crc32,
|
|
959
|
+
'@geodist' => 101,
|
|
960
|
+
'@groupby' => 102,
|
|
961
|
+
'@count' => 103
|
|
962
|
+
}, :weight => 12
|
|
963
|
+
}]
|
|
964
|
+
}
|
|
965
|
+
Alpha.stub!(:find => [@alpha])
|
|
966
|
+
|
|
967
|
+
@search = ThinkingSphinx::Search.new
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
it "should yield geodist if requested" do
|
|
971
|
+
@search.each_with_geodist do |obj, distance|
|
|
972
|
+
obj.should == @alpha
|
|
973
|
+
distance.should == 101
|
|
974
|
+
end
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
it "should yield count if requested" do
|
|
978
|
+
@search.each_with_count do |obj, count|
|
|
979
|
+
obj.should == @alpha
|
|
980
|
+
count.should == 103
|
|
981
|
+
end
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
it "should yield groupby if requested" do
|
|
985
|
+
@search.each_with_groupby do |obj, group|
|
|
986
|
+
obj.should == @alpha
|
|
987
|
+
group.should == 102
|
|
988
|
+
end
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
it "should still use the array's each_with_index" do
|
|
992
|
+
@search.each_with_index do |obj, index|
|
|
993
|
+
obj.should == @alpha
|
|
994
|
+
index.should == 0
|
|
995
|
+
end
|
|
996
|
+
end
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
describe '#excerpt_for' do
|
|
1000
|
+
before :each do
|
|
1001
|
+
@client.stub!(:excerpts => ['excerpted string'])
|
|
1002
|
+
@client.stub!(:query => {
|
|
1003
|
+
:matches => [],
|
|
1004
|
+
:words => {'one' => {}, 'two' => {}}
|
|
1005
|
+
})
|
|
1006
|
+
@search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
it "should return the Sphinx excerpt value" do
|
|
1010
|
+
@search.excerpt_for('string').should == 'excerpted string'
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
it "should use the given model's core index" do
|
|
1014
|
+
@client.should_receive(:excerpts) do |options|
|
|
1015
|
+
options[:index].should == 'alpha_core'
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
@search.excerpt_for('string')
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
it "should optionally take a second argument to allow for multi-model searches" do
|
|
1022
|
+
@client.should_receive(:excerpts) do |options|
|
|
1023
|
+
options[:index].should == 'beta_core'
|
|
1024
|
+
end
|
|
1025
|
+
|
|
1026
|
+
@search.excerpt_for('string', Beta)
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
it "should join the words together" do
|
|
1030
|
+
@client.should_receive(:excerpts) do |options|
|
|
1031
|
+
options[:words].should == @search.results[:words].keys.join(' ')
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
@search.excerpt_for('string', Beta)
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
it "should use the correct index in STI situations" do
|
|
1038
|
+
@client.should_receive(:excerpts) do |options|
|
|
1039
|
+
options[:index].should == 'person_core'
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
@search.excerpt_for('string', Parent)
|
|
1043
|
+
end
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
describe '#search' do
|
|
1047
|
+
before :each do
|
|
1048
|
+
@search = ThinkingSphinx::Search.new('word',
|
|
1049
|
+
:conditions => {:field => 'field'},
|
|
1050
|
+
:with => {:int => 5}
|
|
1051
|
+
)
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
it "should return itself" do
|
|
1055
|
+
@search.search.object_id.should == @search.object_id
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
it "should merge in arguments" do
|
|
1059
|
+
@client.should_receive(:query) do |query, index, comments|
|
|
1060
|
+
query.should == 'word more @field field'
|
|
1061
|
+
end
|
|
1062
|
+
|
|
1063
|
+
@search.search('more').first
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
it "should merge conditions" do
|
|
1067
|
+
@client.should_receive(:query) do |query, index, comments|
|
|
1068
|
+
query.should match(/@name plato/)
|
|
1069
|
+
query.should match(/@field field/)
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
@search.search(:conditions => {:name => 'plato'}).first
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
it "should merge filters" do
|
|
1076
|
+
@search.search(:with => {:float => 1.5}).first
|
|
1077
|
+
|
|
1078
|
+
@client.filters.detect { |filter|
|
|
1079
|
+
filter.attribute == 'float'
|
|
1080
|
+
}.should_not be_nil
|
|
1081
|
+
@client.filters.detect { |filter|
|
|
1082
|
+
filter.attribute == 'int'
|
|
1083
|
+
}.should_not be_nil
|
|
1084
|
+
end
|
|
1085
|
+
end
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
describe ThinkingSphinx::Search, "playing nice with Search model" do
|
|
1089
|
+
it "should not conflict with models called Search" do
|
|
1090
|
+
lambda { Search.find(:all) }.should_not raise_error
|
|
1091
|
+
end
|
|
1092
|
+
end
|