skalee-thinking-sphinx 1.3.14.1
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 +201 -0
- data/Rakefile +3 -0
- data/VERSION +1 -0
- data/contribute.rb +385 -0
- data/cucumber.yml +1 -0
- data/features/abstract_inheritance.feature +10 -0
- data/features/alternate_primary_key.feature +27 -0
- data/features/attribute_transformation.feature +22 -0
- data/features/attribute_updates.feature +51 -0
- data/features/deleting_instances.feature +67 -0
- data/features/direct_attributes.feature +11 -0
- data/features/excerpts.feature +13 -0
- data/features/extensible_delta_indexing.feature +9 -0
- data/features/facets.feature +82 -0
- data/features/facets_across_model.feature +29 -0
- data/features/handling_edits.feature +92 -0
- data/features/retry_stale_indexes.feature +24 -0
- data/features/searching_across_models.feature +20 -0
- data/features/searching_by_index.feature +40 -0
- data/features/searching_by_model.feature +175 -0
- data/features/searching_with_find_arguments.feature +56 -0
- data/features/sphinx_detection.feature +25 -0
- data/features/sphinx_scopes.feature +42 -0
- data/features/step_definitions/alpha_steps.rb +16 -0
- data/features/step_definitions/beta_steps.rb +7 -0
- data/features/step_definitions/common_steps.rb +188 -0
- data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
- data/features/step_definitions/facet_steps.rb +96 -0
- data/features/step_definitions/find_arguments_steps.rb +36 -0
- data/features/step_definitions/gamma_steps.rb +15 -0
- data/features/step_definitions/scope_steps.rb +15 -0
- data/features/step_definitions/search_steps.rb +89 -0
- data/features/step_definitions/sphinx_steps.rb +35 -0
- data/features/sti_searching.feature +19 -0
- data/features/support/database.example.yml +3 -0
- data/features/support/db/.gitignore +1 -0
- data/features/support/db/fixtures/alphas.rb +10 -0
- data/features/support/db/fixtures/authors.rb +1 -0
- data/features/support/db/fixtures/betas.rb +10 -0
- data/features/support/db/fixtures/boxes.rb +9 -0
- data/features/support/db/fixtures/categories.rb +1 -0
- data/features/support/db/fixtures/cats.rb +3 -0
- data/features/support/db/fixtures/comments.rb +24 -0
- data/features/support/db/fixtures/developers.rb +29 -0
- data/features/support/db/fixtures/dogs.rb +3 -0
- data/features/support/db/fixtures/extensible_betas.rb +10 -0
- data/features/support/db/fixtures/foxes.rb +3 -0
- data/features/support/db/fixtures/gammas.rb +10 -0
- data/features/support/db/fixtures/music.rb +4 -0
- data/features/support/db/fixtures/people.rb +1001 -0
- data/features/support/db/fixtures/posts.rb +6 -0
- data/features/support/db/fixtures/robots.rb +14 -0
- data/features/support/db/fixtures/tags.rb +27 -0
- data/features/support/db/migrations/create_alphas.rb +8 -0
- data/features/support/db/migrations/create_animals.rb +5 -0
- data/features/support/db/migrations/create_authors.rb +3 -0
- data/features/support/db/migrations/create_authors_posts.rb +6 -0
- data/features/support/db/migrations/create_betas.rb +5 -0
- data/features/support/db/migrations/create_boxes.rb +5 -0
- data/features/support/db/migrations/create_categories.rb +3 -0
- data/features/support/db/migrations/create_comments.rb +10 -0
- data/features/support/db/migrations/create_developers.rb +9 -0
- data/features/support/db/migrations/create_extensible_betas.rb +5 -0
- data/features/support/db/migrations/create_gammas.rb +3 -0
- data/features/support/db/migrations/create_genres.rb +3 -0
- data/features/support/db/migrations/create_music.rb +6 -0
- data/features/support/db/migrations/create_people.rb +13 -0
- data/features/support/db/migrations/create_posts.rb +5 -0
- data/features/support/db/migrations/create_robots.rb +4 -0
- data/features/support/db/migrations/create_taggings.rb +5 -0
- data/features/support/db/migrations/create_tags.rb +4 -0
- data/features/support/env.rb +21 -0
- data/features/support/lib/generic_delta_handler.rb +8 -0
- data/features/support/models/alpha.rb +22 -0
- data/features/support/models/animal.rb +5 -0
- data/features/support/models/author.rb +3 -0
- data/features/support/models/beta.rb +8 -0
- data/features/support/models/box.rb +8 -0
- data/features/support/models/cat.rb +3 -0
- data/features/support/models/category.rb +4 -0
- data/features/support/models/comment.rb +10 -0
- data/features/support/models/developer.rb +16 -0
- data/features/support/models/dog.rb +3 -0
- data/features/support/models/extensible_beta.rb +9 -0
- data/features/support/models/fox.rb +5 -0
- data/features/support/models/gamma.rb +5 -0
- data/features/support/models/genre.rb +3 -0
- data/features/support/models/medium.rb +5 -0
- data/features/support/models/music.rb +8 -0
- data/features/support/models/person.rb +23 -0
- data/features/support/models/post.rb +21 -0
- data/features/support/models/robot.rb +12 -0
- data/features/support/models/tag.rb +3 -0
- data/features/support/models/tagging.rb +4 -0
- data/ginger_scenarios.rb +28 -0
- data/init.rb +5 -0
- data/install.rb +5 -0
- data/lib/cucumber/thinking_sphinx/external_world.rb +8 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +126 -0
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +19 -0
- data/lib/thinking_sphinx/active_record/delta.rb +47 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +75 -0
- data/lib/thinking_sphinx/active_record.rb +348 -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 +143 -0
- data/lib/thinking_sphinx/association.rb +164 -0
- data/lib/thinking_sphinx/attribute.rb +362 -0
- data/lib/thinking_sphinx/auto_version.rb +22 -0
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +300 -0
- data/lib/thinking_sphinx/context.rb +68 -0
- data/lib/thinking_sphinx/core/array.rb +7 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
- data/lib/thinking_sphinx/deltas.rb +28 -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 +136 -0
- data/lib/thinking_sphinx/field.rb +82 -0
- data/lib/thinking_sphinx/index/builder.rb +296 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/index.rb +157 -0
- data/lib/thinking_sphinx/property.rb +162 -0
- data/lib/thinking_sphinx/rails_additions.rb +150 -0
- data/lib/thinking_sphinx/search.rb +769 -0
- data/lib/thinking_sphinx/search_methods.rb +439 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +130 -0
- data/lib/thinking_sphinx/source.rb +153 -0
- data/lib/thinking_sphinx/tasks.rb +131 -0
- data/lib/thinking_sphinx/test.rb +52 -0
- data/lib/thinking_sphinx.rb +225 -0
- data/rails/init.rb +16 -0
- data/recipes/thinking_sphinx.rb +3 -0
- data/spec/fixtures/data.sql +32 -0
- data/spec/fixtures/database.yml.default +3 -0
- data/spec/fixtures/models.rb +145 -0
- data/spec/fixtures/structure.sql +125 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/sphinx_helper.rb +81 -0
- data/spec/thinking_sphinx/active_record/delta_spec.rb +128 -0
- data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +55 -0
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +177 -0
- data/spec/thinking_sphinx/active_record_spec.rb +622 -0
- data/spec/thinking_sphinx/association_spec.rb +239 -0
- data/spec/thinking_sphinx/attribute_spec.rb +570 -0
- data/spec/thinking_sphinx/auto_version_spec.rb +39 -0
- data/spec/thinking_sphinx/configuration_spec.rb +234 -0
- data/spec/thinking_sphinx/context_spec.rb +119 -0
- data/spec/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/thinking_sphinx/excerpter_spec.rb +57 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/thinking_sphinx/facet_spec.rb +333 -0
- data/spec/thinking_sphinx/field_spec.rb +154 -0
- data/spec/thinking_sphinx/index/builder_spec.rb +479 -0
- data/spec/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/thinking_sphinx/index_spec.rb +183 -0
- data/spec/thinking_sphinx/rails_additions_spec.rb +203 -0
- data/spec/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/thinking_sphinx/search_spec.rb +1181 -0
- data/spec/thinking_sphinx/source_spec.rb +235 -0
- data/spec/thinking_sphinx_spec.rb +204 -0
- data/tasks/distribution.rb +41 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +72 -0
- data/vendor/after_commit/.gitignore +1 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +122 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +168 -0
- data/vendor/after_commit/lib/after_commit/test_bypass.rb +30 -0
- data/vendor/after_commit/lib/after_commit.rb +70 -0
- data/vendor/riddle/lib/riddle/0.9.8.rb +1 -0
- data/vendor/riddle/lib/riddle/0.9.9/client/filter.rb +22 -0
- data/vendor/riddle/lib/riddle/0.9.9/client.rb +49 -0
- data/vendor/riddle/lib/riddle/0.9.9/configuration/searchd.rb +28 -0
- data/vendor/riddle/lib/riddle/0.9.9.rb +7 -0
- data/vendor/riddle/lib/riddle/auto_version.rb +11 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +62 -0
- data/vendor/riddle/lib/riddle/client/message.rb +70 -0
- data/vendor/riddle/lib/riddle/client/response.rb +94 -0
- data/vendor/riddle/lib/riddle/client.rb +745 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +49 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +149 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +20 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +28 -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 +53 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +29 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/controller.rb +78 -0
- data/vendor/riddle/lib/riddle.rb +51 -0
- metadata +312 -0
@@ -0,0 +1,1181 @@
|
|
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 '.matching_fields' do
|
157
|
+
it "should return objects with indexes matching 1's in the bitmask" do
|
158
|
+
fields = ['alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta']
|
159
|
+
ThinkingSphinx::Search.matching_fields(fields, 85).
|
160
|
+
should == ['alpha', 'gamma', 'epsilon', 'eta']
|
161
|
+
|
162
|
+
ThinkingSphinx::Search.matching_fields(fields, 42).
|
163
|
+
should == ['beta', 'delta', 'zeta']
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#populate' do
|
168
|
+
before :each do
|
169
|
+
@alpha_a, @alpha_b = Alpha.new, Alpha.new
|
170
|
+
@beta_a, @beta_b = Beta.new, Beta.new
|
171
|
+
|
172
|
+
@alpha_a.stub! :id => 1, :read_attribute => 1
|
173
|
+
@alpha_b.stub! :id => 2, :read_attribute => 2
|
174
|
+
@beta_a.stub! :id => 1, :read_attribute => 1
|
175
|
+
@beta_b.stub! :id => 2, :read_attribute => 2
|
176
|
+
|
177
|
+
@client.stub! :query => {
|
178
|
+
:matches => minimal_result_hashes(@alpha_a, @beta_b, @alpha_b, @beta_a),
|
179
|
+
:fields => ["one", "two", "three", "four", "five"]
|
180
|
+
}
|
181
|
+
Alpha.stub! :find => [@alpha_a, @alpha_b]
|
182
|
+
Beta.stub! :find => [@beta_a, @beta_b]
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should issue only one select per model" do
|
186
|
+
Alpha.should_receive(:find).once.and_return([@alpha_a, @alpha_b])
|
187
|
+
Beta.should_receive(:find).once.and_return([@beta_a, @beta_b])
|
188
|
+
|
189
|
+
ThinkingSphinx::Search.new.first
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should mix the results from different models" do
|
193
|
+
search = ThinkingSphinx::Search.new
|
194
|
+
search[0].should be_a(Alpha)
|
195
|
+
search[1].should be_a(Beta)
|
196
|
+
search[2].should be_a(Alpha)
|
197
|
+
search[3].should be_a(Beta)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should maintain the Xoopit ordering for results" do
|
201
|
+
search = ThinkingSphinx::Search.new
|
202
|
+
search[0].id.should == 1
|
203
|
+
search[1].id.should == 2
|
204
|
+
search[2].id.should == 2
|
205
|
+
search[3].id.should == 1
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should use the requested classes to generate the index argument" do
|
209
|
+
@client.should_receive(:query) do |query, index, comment|
|
210
|
+
index.should == 'alpha_core,beta_core,beta_delta'
|
211
|
+
end
|
212
|
+
|
213
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
|
214
|
+
end
|
215
|
+
|
216
|
+
describe 'query' do
|
217
|
+
it "should concatenate arguments with spaces" do
|
218
|
+
@client.should_receive(:query) do |query, index, comment|
|
219
|
+
query.should == 'two words'
|
220
|
+
end
|
221
|
+
|
222
|
+
ThinkingSphinx::Search.new('two', 'words').first
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should append conditions to the query" do
|
226
|
+
@client.should_receive(:query) do |query, index, comment|
|
227
|
+
query.should == 'general @focused specific'
|
228
|
+
end
|
229
|
+
|
230
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
231
|
+
:focused => 'specific'
|
232
|
+
}).first
|
233
|
+
end
|
234
|
+
|
235
|
+
it "append multiple conditions together" do
|
236
|
+
@client.should_receive(:query) do |query, index, comment|
|
237
|
+
query.should match(/general.+@foo word/)
|
238
|
+
query.should match(/general.+@bar word/)
|
239
|
+
end
|
240
|
+
|
241
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
242
|
+
:foo => 'word', :bar => 'word'
|
243
|
+
}).first
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should apply stars if requested, and handle full extended syntax" do
|
247
|
+
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)}
|
248
|
+
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*)}
|
249
|
+
|
250
|
+
@client.should_receive(:query) do |query, index, comment|
|
251
|
+
query.should == expected
|
252
|
+
end
|
253
|
+
|
254
|
+
ThinkingSphinx::Search.new(input, :star => true).first
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should default to /\w+/ as token for auto-starring" do
|
258
|
+
@client.should_receive(:query) do |query, index, comment|
|
259
|
+
query.should == '*foo*@*bar*.*com*'
|
260
|
+
end
|
261
|
+
|
262
|
+
ThinkingSphinx::Search.new('foo@bar.com', :star => true).first
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should honour custom star tokens" do
|
266
|
+
@client.should_receive(:query) do |query, index, comment|
|
267
|
+
query.should == '*foo@bar.com* -*foo-bar*'
|
268
|
+
end
|
269
|
+
|
270
|
+
ThinkingSphinx::Search.new(
|
271
|
+
'foo@bar.com -foo-bar', :star => /[\w@.-]+/u
|
272
|
+
).first
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe 'comment' do
|
277
|
+
it "should add comment if explicitly provided" do
|
278
|
+
@client.should_receive(:query) do |query, index, comment|
|
279
|
+
comment.should == 'custom log'
|
280
|
+
end
|
281
|
+
|
282
|
+
ThinkingSphinx::Search.new(:comment => 'custom log').first
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should default to a blank comment" do
|
286
|
+
@client.should_receive(:query) do |query, index, comment|
|
287
|
+
comment.should == ''
|
288
|
+
end
|
289
|
+
|
290
|
+
ThinkingSphinx::Search.new.first
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe 'match mode' do
|
295
|
+
it "should default to :all" do
|
296
|
+
ThinkingSphinx::Search.new.first
|
297
|
+
|
298
|
+
@client.match_mode.should == :all
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should default to :extended if conditions are supplied" do
|
302
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
303
|
+
:foo => 'word', :bar => 'word'
|
304
|
+
}).first
|
305
|
+
|
306
|
+
@client.match_mode.should == :extended
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should use explicit match modes" do
|
310
|
+
ThinkingSphinx::Search.new('general', :conditions => {
|
311
|
+
:foo => 'word', :bar => 'word'
|
312
|
+
}, :match_mode => :extended2).first
|
313
|
+
|
314
|
+
@client.match_mode.should == :extended2
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe 'sphinx_select' do
|
319
|
+
it "should default to *" do
|
320
|
+
ThinkingSphinx::Search.new.first
|
321
|
+
|
322
|
+
@client.select.should == "*"
|
323
|
+
end
|
324
|
+
|
325
|
+
it "should get set on the client if specified" do
|
326
|
+
ThinkingSphinx::Search.new('general',
|
327
|
+
:sphinx_select => "*, foo as bar"
|
328
|
+
).first
|
329
|
+
|
330
|
+
@client.select.should == "*, foo as bar"
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
describe 'pagination' do
|
336
|
+
it "should set the limit using per_page" do
|
337
|
+
ThinkingSphinx::Search.new(:per_page => 30).first
|
338
|
+
@client.limit.should == 30
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should set the offset if pagination is requested" do
|
342
|
+
ThinkingSphinx::Search.new(:page => 3).first
|
343
|
+
@client.offset.should == 40
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should set the offset by the per_page value" do
|
347
|
+
ThinkingSphinx::Search.new(:page => 3, :per_page => 30).first
|
348
|
+
@client.offset.should == 60
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe 'filters' do
|
353
|
+
it "should filter out deleted values by default" do
|
354
|
+
ThinkingSphinx::Search.new.first
|
355
|
+
|
356
|
+
filter = @client.filters.last
|
357
|
+
filter.values.should == [0]
|
358
|
+
filter.attribute.should == 'sphinx_deleted'
|
359
|
+
filter.exclude?.should be_false
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should add class filters for explicit classes" do
|
363
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).first
|
364
|
+
|
365
|
+
filter = @client.filters.last
|
366
|
+
filter.values.should == [Alpha.to_crc32, Beta.to_crc32]
|
367
|
+
filter.attribute.should == 'class_crc'
|
368
|
+
filter.exclude?.should be_false
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should add class filters for subclasses of requested classes" do
|
372
|
+
ThinkingSphinx::Search.new(:classes => [Person]).first
|
373
|
+
|
374
|
+
filter = @client.filters.last
|
375
|
+
filter.values.should == [
|
376
|
+
Parent.to_crc32, Admin::Person.to_crc32,
|
377
|
+
Child.to_crc32, Person.to_crc32
|
378
|
+
]
|
379
|
+
filter.attribute.should == 'class_crc'
|
380
|
+
filter.exclude?.should be_false
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should append inclusive filters of integers" do
|
384
|
+
ThinkingSphinx::Search.new(:with => {:int => 1}).first
|
385
|
+
|
386
|
+
filter = @client.filters.last
|
387
|
+
filter.values.should == [1]
|
388
|
+
filter.attribute.should == 'int'
|
389
|
+
filter.exclude?.should be_false
|
390
|
+
end
|
391
|
+
|
392
|
+
it "should append inclusive filters of floats" do
|
393
|
+
ThinkingSphinx::Search.new(:with => {:float => 1.5}).first
|
394
|
+
|
395
|
+
filter = @client.filters.last
|
396
|
+
filter.values.should == [1.5]
|
397
|
+
filter.attribute.should == 'float'
|
398
|
+
filter.exclude?.should be_false
|
399
|
+
end
|
400
|
+
|
401
|
+
it "should append inclusive filters of booleans" do
|
402
|
+
ThinkingSphinx::Search.new(:with => {:boolean => true}).first
|
403
|
+
|
404
|
+
filter = @client.filters.last
|
405
|
+
filter.values.should == [true]
|
406
|
+
filter.attribute.should == 'boolean'
|
407
|
+
filter.exclude?.should be_false
|
408
|
+
end
|
409
|
+
|
410
|
+
it "should append inclusive filters of arrays" do
|
411
|
+
ThinkingSphinx::Search.new(:with => {:ints => [1, 2, 3]}).first
|
412
|
+
|
413
|
+
filter = @client.filters.last
|
414
|
+
filter.values.should == [1, 2, 3]
|
415
|
+
filter.attribute.should == 'ints'
|
416
|
+
filter.exclude?.should be_false
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should treat nils in arrays as 0" do
|
420
|
+
ThinkingSphinx::Search.new(:with => {:ints => [nil, 1, 2, 3]}).first
|
421
|
+
|
422
|
+
filter = @client.filters.last
|
423
|
+
filter.values.should == [0, 1, 2, 3]
|
424
|
+
end
|
425
|
+
|
426
|
+
it "should append inclusive filters of time ranges" do
|
427
|
+
first, last = 1.week.ago, Time.now
|
428
|
+
ThinkingSphinx::Search.new(:with => {
|
429
|
+
:time => first..last
|
430
|
+
}).first
|
431
|
+
|
432
|
+
filter = @client.filters.last
|
433
|
+
filter.values.should == (first.to_i..last.to_i)
|
434
|
+
filter.attribute.should == 'time'
|
435
|
+
filter.exclude?.should be_false
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should append exclusive filters of integers" do
|
439
|
+
ThinkingSphinx::Search.new(:without => {:int => 1}).first
|
440
|
+
|
441
|
+
filter = @client.filters.last
|
442
|
+
filter.values.should == [1]
|
443
|
+
filter.attribute.should == 'int'
|
444
|
+
filter.exclude?.should be_true
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should append exclusive filters of floats" do
|
448
|
+
ThinkingSphinx::Search.new(:without => {:float => 1.5}).first
|
449
|
+
|
450
|
+
filter = @client.filters.last
|
451
|
+
filter.values.should == [1.5]
|
452
|
+
filter.attribute.should == 'float'
|
453
|
+
filter.exclude?.should be_true
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should append exclusive filters of booleans" do
|
457
|
+
ThinkingSphinx::Search.new(:without => {:boolean => true}).first
|
458
|
+
|
459
|
+
filter = @client.filters.last
|
460
|
+
filter.values.should == [true]
|
461
|
+
filter.attribute.should == 'boolean'
|
462
|
+
filter.exclude?.should be_true
|
463
|
+
end
|
464
|
+
|
465
|
+
it "should append exclusive filters of arrays" do
|
466
|
+
ThinkingSphinx::Search.new(:without => {:ints => [1, 2, 3]}).first
|
467
|
+
|
468
|
+
filter = @client.filters.last
|
469
|
+
filter.values.should == [1, 2, 3]
|
470
|
+
filter.attribute.should == 'ints'
|
471
|
+
filter.exclude?.should be_true
|
472
|
+
end
|
473
|
+
|
474
|
+
it "should append exclusive filters of time ranges" do
|
475
|
+
first, last = 1.week.ago, Time.now
|
476
|
+
ThinkingSphinx::Search.new(:without => {
|
477
|
+
:time => first..last
|
478
|
+
}).first
|
479
|
+
|
480
|
+
filter = @client.filters.last
|
481
|
+
filter.values.should == (first.to_i..last.to_i)
|
482
|
+
filter.attribute.should == 'time'
|
483
|
+
filter.exclude?.should be_true
|
484
|
+
end
|
485
|
+
|
486
|
+
it "should add separate filters for each item in a with_all value" do
|
487
|
+
ThinkingSphinx::Search.new(:with_all => {:ints => [1, 2, 3]}).first
|
488
|
+
|
489
|
+
filters = @client.filters[-3, 3]
|
490
|
+
filters.each do |filter|
|
491
|
+
filter.attribute.should == 'ints'
|
492
|
+
filter.exclude?.should be_false
|
493
|
+
end
|
494
|
+
|
495
|
+
filters[0].values.should == [1]
|
496
|
+
filters[1].values.should == [2]
|
497
|
+
filters[2].values.should == [3]
|
498
|
+
end
|
499
|
+
|
500
|
+
it "should filter out specific ids using :without_ids" do
|
501
|
+
ThinkingSphinx::Search.new(:without_ids => [4, 5, 6]).first
|
502
|
+
|
503
|
+
filter = @client.filters.last
|
504
|
+
filter.values.should == [4, 5, 6]
|
505
|
+
filter.attribute.should == 'sphinx_internal_id'
|
506
|
+
filter.exclude?.should be_true
|
507
|
+
end
|
508
|
+
|
509
|
+
describe 'in :conditions' do
|
510
|
+
it "should add as filters for known attributes in :conditions option" do
|
511
|
+
ThinkingSphinx::Search.new('general',
|
512
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
513
|
+
:classes => [Alpha]
|
514
|
+
).first
|
515
|
+
|
516
|
+
filter = @client.filters.last
|
517
|
+
filter.values.should == [1.5]
|
518
|
+
filter.attribute.should == 'lat'
|
519
|
+
filter.exclude?.should be_false
|
520
|
+
end
|
521
|
+
|
522
|
+
it "should not add the filter to the query string" do
|
523
|
+
@client.should_receive(:query) do |query, index, comment|
|
524
|
+
query.should == 'general @word specific'
|
525
|
+
end
|
526
|
+
|
527
|
+
ThinkingSphinx::Search.new('general',
|
528
|
+
:conditions => {:word => 'specific', :lat => 1.5},
|
529
|
+
:classes => [Alpha]
|
530
|
+
).first
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
describe 'sort mode' do
|
536
|
+
it "should use :relevance as a default" do
|
537
|
+
ThinkingSphinx::Search.new.first
|
538
|
+
@client.sort_mode.should == :relevance
|
539
|
+
end
|
540
|
+
|
541
|
+
it "should use :attr_asc if a symbol is supplied to :order" do
|
542
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
543
|
+
@client.sort_mode.should == :attr_asc
|
544
|
+
end
|
545
|
+
|
546
|
+
it "should use :attr_desc if :desc is the mode" do
|
547
|
+
ThinkingSphinx::Search.new(
|
548
|
+
:order => :created_at, :sort_mode => :desc
|
549
|
+
).first
|
550
|
+
@client.sort_mode.should == :attr_desc
|
551
|
+
end
|
552
|
+
|
553
|
+
it "should use :extended if a string is supplied to :order" do
|
554
|
+
ThinkingSphinx::Search.new(:order => "created_at ASC").first
|
555
|
+
@client.sort_mode.should == :extended
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should use :expr if explicitly requested" do
|
559
|
+
ThinkingSphinx::Search.new(
|
560
|
+
:order => "created_at ASC", :sort_mode => :expr
|
561
|
+
).first
|
562
|
+
@client.sort_mode.should == :expr
|
563
|
+
end
|
564
|
+
|
565
|
+
it "should use :attr_desc if explicitly requested" do
|
566
|
+
ThinkingSphinx::Search.new(
|
567
|
+
:order => "created_at", :sort_mode => :desc
|
568
|
+
).first
|
569
|
+
@client.sort_mode.should == :attr_desc
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
describe 'sort by' do
|
574
|
+
it "should presume order symbols are attributes" do
|
575
|
+
ThinkingSphinx::Search.new(:order => :created_at).first
|
576
|
+
@client.sort_by.should == 'created_at'
|
577
|
+
end
|
578
|
+
|
579
|
+
it "replace field names with their sortable attributes" do
|
580
|
+
ThinkingSphinx::Search.new(:order => :name, :classes => [Alpha]).first
|
581
|
+
@client.sort_by.should == 'name_sort'
|
582
|
+
end
|
583
|
+
|
584
|
+
it "should replace field names in strings" do
|
585
|
+
ThinkingSphinx::Search.new(
|
586
|
+
:order => "created_at ASC, name DESC", :classes => [Alpha]
|
587
|
+
).first
|
588
|
+
@client.sort_by.should == 'created_at ASC, name_sort DESC'
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
describe 'max matches' do
|
593
|
+
it "should use the global setting by default" do
|
594
|
+
ThinkingSphinx::Search.new.first
|
595
|
+
@client.max_matches.should == 1000
|
596
|
+
end
|
597
|
+
|
598
|
+
it "should use explicit setting" do
|
599
|
+
ThinkingSphinx::Search.new(:max_matches => 2000).first
|
600
|
+
@client.max_matches.should == 2000
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
describe 'field weights' do
|
605
|
+
it "should set field weights as provided" do
|
606
|
+
ThinkingSphinx::Search.new(
|
607
|
+
:field_weights => {'foo' => 10, 'bar' => 5}
|
608
|
+
).first
|
609
|
+
|
610
|
+
@client.field_weights.should == {
|
611
|
+
'foo' => 10, 'bar' => 5
|
612
|
+
}
|
613
|
+
end
|
614
|
+
|
615
|
+
it "should use field weights set in the index" do
|
616
|
+
ThinkingSphinx::Search.new(:classes => [Alpha]).first
|
617
|
+
|
618
|
+
@client.field_weights.should == {'name' => 10}
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
describe 'index weights' do
|
623
|
+
it "should send index weights through to the client" do
|
624
|
+
ThinkingSphinx::Search.new(:index_weights => {'foo' => 100}).first
|
625
|
+
@client.index_weights.should == {'foo' => 100}
|
626
|
+
end
|
627
|
+
|
628
|
+
it "should convert classes to their core and delta index names" do
|
629
|
+
ThinkingSphinx::Search.new(:index_weights => {Alpha => 100}).first
|
630
|
+
@client.index_weights.should == {
|
631
|
+
'alpha_core' => 100,
|
632
|
+
'alpha_delta' => 100
|
633
|
+
}
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
describe 'grouping' do
|
638
|
+
it "should convert group into group_by and group_function" do
|
639
|
+
ThinkingSphinx::Search.new(:group => :edition).first
|
640
|
+
|
641
|
+
@client.group_function.should == :attr
|
642
|
+
@client.group_by.should == "edition"
|
643
|
+
end
|
644
|
+
|
645
|
+
it "should pass on explicit grouping arguments" do
|
646
|
+
ThinkingSphinx::Search.new(
|
647
|
+
:group_by => 'created_at',
|
648
|
+
:group_function => :attr,
|
649
|
+
:group_clause => 'clause',
|
650
|
+
:group_distinct => 'distinct'
|
651
|
+
).first
|
652
|
+
|
653
|
+
@client.group_by.should == 'created_at'
|
654
|
+
@client.group_function.should == :attr
|
655
|
+
@client.group_clause.should == 'clause'
|
656
|
+
@client.group_distinct.should == 'distinct'
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
describe 'anchor' do
|
661
|
+
it "should detect lat and lng attributes on the given model" do
|
662
|
+
ThinkingSphinx::Search.new(
|
663
|
+
:geo => [1.0, -1.0],
|
664
|
+
:classes => [Alpha]
|
665
|
+
).first
|
666
|
+
|
667
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
668
|
+
@client.anchor[:longitude_attribute].should == 'lng'
|
669
|
+
end
|
670
|
+
|
671
|
+
it "should detect lat and lon attributes on the given model" do
|
672
|
+
ThinkingSphinx::Search.new(
|
673
|
+
:geo => [1.0, -1.0],
|
674
|
+
:classes => [Beta]
|
675
|
+
).first
|
676
|
+
|
677
|
+
@client.anchor[:latitude_attribute].should == 'lat'
|
678
|
+
@client.anchor[:longitude_attribute].should == 'lon'
|
679
|
+
end
|
680
|
+
|
681
|
+
it "should detect latitude and longitude attributes on the given model" do
|
682
|
+
ThinkingSphinx::Search.new(
|
683
|
+
:geo => [1.0, -1.0],
|
684
|
+
:classes => [Person]
|
685
|
+
).first
|
686
|
+
|
687
|
+
@client.anchor[:latitude_attribute].should == 'latitude'
|
688
|
+
@client.anchor[:longitude_attribute].should == 'longitude'
|
689
|
+
end
|
690
|
+
|
691
|
+
it "should accept manually defined latitude and longitude attributes" do
|
692
|
+
ThinkingSphinx::Search.new(
|
693
|
+
:geo => [1.0, -1.0],
|
694
|
+
:classes => [Alpha],
|
695
|
+
:latitude_attr => :updown,
|
696
|
+
:longitude_attr => :leftright
|
697
|
+
).first
|
698
|
+
|
699
|
+
@client.anchor[:latitude_attribute].should == 'updown'
|
700
|
+
@client.anchor[:longitude_attribute].should == 'leftright'
|
701
|
+
end
|
702
|
+
|
703
|
+
it "should accept manually defined latitude and longitude attributes in the given model" do
|
704
|
+
ThinkingSphinx::Search.new(
|
705
|
+
:geo => [1.0, -1.0],
|
706
|
+
:classes => [Friendship]
|
707
|
+
).first
|
708
|
+
|
709
|
+
@client.anchor[:latitude_attribute].should == 'person_id'
|
710
|
+
@client.anchor[:longitude_attribute].should == 'person_id'
|
711
|
+
end
|
712
|
+
|
713
|
+
it "should accept geo array for geo-position values" do
|
714
|
+
ThinkingSphinx::Search.new(
|
715
|
+
:geo => [1.0, -1.0],
|
716
|
+
:classes => [Alpha]
|
717
|
+
).first
|
718
|
+
|
719
|
+
@client.anchor[:latitude].should == 1.0
|
720
|
+
@client.anchor[:longitude].should == -1.0
|
721
|
+
end
|
722
|
+
|
723
|
+
it "should accept lat and lng options for geo-position values" do
|
724
|
+
ThinkingSphinx::Search.new(
|
725
|
+
:lat => 1.0,
|
726
|
+
:lng => -1.0,
|
727
|
+
:classes => [Alpha]
|
728
|
+
).first
|
729
|
+
|
730
|
+
@client.anchor[:latitude].should == 1.0
|
731
|
+
@client.anchor[:longitude].should == -1.0
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
describe 'sql ordering' do
|
736
|
+
before :each do
|
737
|
+
@client.stub! :query => {
|
738
|
+
:matches => minimal_result_hashes(@alpha_b, @alpha_a)
|
739
|
+
}
|
740
|
+
Alpha.stub! :find => [@alpha_a, @alpha_b]
|
741
|
+
end
|
742
|
+
|
743
|
+
it "shouldn't re-sort SQL results based on Sphinx information" do
|
744
|
+
search = ThinkingSphinx::Search.new(
|
745
|
+
:classes => [Alpha],
|
746
|
+
:sql_order => 'id'
|
747
|
+
)
|
748
|
+
search.first.should == @alpha_a
|
749
|
+
search.last.should == @alpha_b
|
750
|
+
end
|
751
|
+
|
752
|
+
it "should use the option for the ActiveRecord::Base#find calls" do
|
753
|
+
Alpha.should_receive(:find) do |mode, options|
|
754
|
+
options[:order].should == 'id'
|
755
|
+
end
|
756
|
+
|
757
|
+
ThinkingSphinx::Search.new(
|
758
|
+
:classes => [Alpha],
|
759
|
+
:sql_order => 'id'
|
760
|
+
).first
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
context 'result objects' do
|
765
|
+
describe '#excerpts' do
|
766
|
+
before :each do
|
767
|
+
@search = ThinkingSphinx::Search.new
|
768
|
+
end
|
769
|
+
|
770
|
+
it "should add excerpts method if objects don't already have one" do
|
771
|
+
@search.first.should respond_to(:excerpts)
|
772
|
+
end
|
773
|
+
|
774
|
+
it "should return an instance of ThinkingSphinx::Excerpter" do
|
775
|
+
@search.first.excerpts.should be_a(ThinkingSphinx::Excerpter)
|
776
|
+
end
|
777
|
+
|
778
|
+
it "should not add excerpts method if objects already have one" do
|
779
|
+
@search.last.excerpts.should_not be_a(ThinkingSphinx::Excerpter)
|
780
|
+
end
|
781
|
+
|
782
|
+
it "should set up the excerpter with the instances and search" do
|
783
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_a)
|
784
|
+
ThinkingSphinx::Excerpter.should_receive(:new).with(@search, @alpha_b)
|
785
|
+
|
786
|
+
@search.first
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
describe '#sphinx_attributes' do
|
791
|
+
before :each do
|
792
|
+
@search = ThinkingSphinx::Search.new
|
793
|
+
end
|
794
|
+
|
795
|
+
it "should add sphinx_attributes method if objects don't already have one" do
|
796
|
+
@search.last.should respond_to(:sphinx_attributes)
|
797
|
+
end
|
798
|
+
|
799
|
+
it "should return a hash" do
|
800
|
+
@search.last.sphinx_attributes.should be_a(Hash)
|
801
|
+
end
|
802
|
+
|
803
|
+
it "should not add sphinx_attributes if objects have a method of that name already" do
|
804
|
+
@search.first.sphinx_attributes.should_not be_a(Hash)
|
805
|
+
end
|
806
|
+
|
807
|
+
it "should pair sphinx_attributes with the correct hash" do
|
808
|
+
hash = @search.last.sphinx_attributes
|
809
|
+
hash['sphinx_internal_id'].should == @search.last.id
|
810
|
+
hash['class_crc'].should == @search.last.class.to_crc32
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
describe '#matching_fields' do
|
815
|
+
it "should add matching_fields method if using fieldmask ranking mode" do
|
816
|
+
search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
|
817
|
+
search.first.should respond_to(:matching_fields)
|
818
|
+
end
|
819
|
+
|
820
|
+
it "should not add matching_fields method if using a different ranking mode" do
|
821
|
+
search = ThinkingSphinx::Search.new :rank_mode => :bm25
|
822
|
+
search.first.should_not respond_to(:matching_fields)
|
823
|
+
end
|
824
|
+
|
825
|
+
it "should not add matching_fields method if object already have one" do
|
826
|
+
search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
|
827
|
+
search.last.matching_fields.should_not be_an(Array)
|
828
|
+
end
|
829
|
+
|
830
|
+
it "should return an array" do
|
831
|
+
search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
|
832
|
+
search.first.matching_fields.should be_an(Array)
|
833
|
+
end
|
834
|
+
|
835
|
+
it "should return the fields that the bitmask match" do
|
836
|
+
search = ThinkingSphinx::Search.new :rank_mode => :fieldmask
|
837
|
+
search.first.matching_fields.should == ['one', 'three', 'five']
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
describe '#current_page' do
|
844
|
+
it "should return 1 by default" do
|
845
|
+
ThinkingSphinx::Search.new.current_page.should == 1
|
846
|
+
end
|
847
|
+
|
848
|
+
it "should handle string page values" do
|
849
|
+
ThinkingSphinx::Search.new(:page => '2').current_page.should == 2
|
850
|
+
end
|
851
|
+
|
852
|
+
it "should handle empty string page values" do
|
853
|
+
ThinkingSphinx::Search.new(:page => '').current_page.should == 1
|
854
|
+
end
|
855
|
+
|
856
|
+
it "should return the requested page" do
|
857
|
+
ThinkingSphinx::Search.new(:page => 10).current_page.should == 10
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
describe '#per_page' do
|
862
|
+
it "should return 20 by default" do
|
863
|
+
ThinkingSphinx::Search.new.per_page.should == 20
|
864
|
+
end
|
865
|
+
|
866
|
+
it "should allow for custom values" do
|
867
|
+
ThinkingSphinx::Search.new(:per_page => 30).per_page.should == 30
|
868
|
+
end
|
869
|
+
|
870
|
+
it "should prioritise :limit over :per_page if given" do
|
871
|
+
ThinkingSphinx::Search.new(
|
872
|
+
:per_page => 30, :limit => 40
|
873
|
+
).per_page.should == 40
|
874
|
+
end
|
875
|
+
|
876
|
+
it "should allow for string arguments" do
|
877
|
+
ThinkingSphinx::Search.new(:per_page => '10').per_page.should == 10
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
describe '#total_pages' do
|
882
|
+
it "should calculate the total pages depending on per_page and total_entries" do
|
883
|
+
ThinkingSphinx::Search.new.total_pages.should == 3
|
884
|
+
end
|
885
|
+
|
886
|
+
it "should allow for custom per_page values" do
|
887
|
+
ThinkingSphinx::Search.new(:per_page => 30).total_pages.should == 2
|
888
|
+
end
|
889
|
+
|
890
|
+
it "should not overstep the max_matches implied limit" do
|
891
|
+
@client.stub!(:query => {
|
892
|
+
:matches => [], :total_found => 41, :total => 40
|
893
|
+
})
|
894
|
+
|
895
|
+
ThinkingSphinx::Search.new.total_pages.should == 2
|
896
|
+
end
|
897
|
+
|
898
|
+
it "should return 0 if there is no index and therefore no results" do
|
899
|
+
@client.stub!(:query => {
|
900
|
+
:matches => [], :total_found => nil, :total => nil
|
901
|
+
})
|
902
|
+
|
903
|
+
ThinkingSphinx::Search.new.total_pages.should == 0
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
describe '#next_page' do
|
908
|
+
it "should return one more than the current page" do
|
909
|
+
ThinkingSphinx::Search.new.next_page.should == 2
|
910
|
+
end
|
911
|
+
|
912
|
+
it "should return nil if on the last page" do
|
913
|
+
ThinkingSphinx::Search.new(:page => 3).next_page.should be_nil
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
describe '#previous_page' do
|
918
|
+
it "should return one less than the current page" do
|
919
|
+
ThinkingSphinx::Search.new(:page => 2).previous_page.should == 1
|
920
|
+
end
|
921
|
+
|
922
|
+
it "should return nil if on the first page" do
|
923
|
+
ThinkingSphinx::Search.new.previous_page.should be_nil
|
924
|
+
end
|
925
|
+
end
|
926
|
+
|
927
|
+
describe '#total_entries' do
|
928
|
+
it "should return the total number of results, not just the amount on the page" do
|
929
|
+
ThinkingSphinx::Search.new.total_entries.should == 41
|
930
|
+
end
|
931
|
+
|
932
|
+
it "should return 0 if there is no index and therefore no results" do
|
933
|
+
@client.stub!(:query => {
|
934
|
+
:matches => [], :total_found => nil
|
935
|
+
})
|
936
|
+
|
937
|
+
ThinkingSphinx::Search.new.total_entries.should == 0
|
938
|
+
end
|
939
|
+
end
|
940
|
+
|
941
|
+
describe '#offset' do
|
942
|
+
it "should default to 0" do
|
943
|
+
ThinkingSphinx::Search.new.offset.should == 0
|
944
|
+
end
|
945
|
+
|
946
|
+
it "should increase by the per_page value for each page in" do
|
947
|
+
ThinkingSphinx::Search.new(:per_page => 25, :page => 2).offset.should == 25
|
948
|
+
end
|
949
|
+
|
950
|
+
it "should prioritise explicit :offset over calculated if given" do
|
951
|
+
ThinkingSphinx::Search.new(:offset => 5).offset.should == 5
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
describe '#indexes' do
|
956
|
+
it "should default to '*'" do
|
957
|
+
ThinkingSphinx::Search.new.indexes.should == '*'
|
958
|
+
end
|
959
|
+
|
960
|
+
it "should use given class to determine index name" do
|
961
|
+
ThinkingSphinx::Search.new(:classes => [Alpha]).indexes.
|
962
|
+
should == 'alpha_core'
|
963
|
+
end
|
964
|
+
|
965
|
+
it "should add both core and delta indexes for given classes" do
|
966
|
+
ThinkingSphinx::Search.new(:classes => [Alpha, Beta]).indexes.
|
967
|
+
should == 'alpha_core,beta_core,beta_delta'
|
968
|
+
end
|
969
|
+
|
970
|
+
it "should respect the :index option" do
|
971
|
+
ThinkingSphinx::Search.new(:classes => [Alpha], :index => '*').indexes.
|
972
|
+
should == '*'
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
describe '.each_with_groupby_and_count' do
|
977
|
+
before :each do
|
978
|
+
@alpha = Alpha.new
|
979
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
980
|
+
|
981
|
+
@client.stub! :query => {
|
982
|
+
:matches => [{
|
983
|
+
:attributes => {
|
984
|
+
'sphinx_internal_id' => @alpha.id,
|
985
|
+
'class_crc' => Alpha.to_crc32,
|
986
|
+
'@groupby' => 101,
|
987
|
+
'@count' => 5
|
988
|
+
}
|
989
|
+
}]
|
990
|
+
}
|
991
|
+
Alpha.stub!(:find => [@alpha])
|
992
|
+
end
|
993
|
+
|
994
|
+
it "should yield the match, group and count" do
|
995
|
+
search = ThinkingSphinx::Search.new
|
996
|
+
search.each_with_groupby_and_count do |obj, group, count|
|
997
|
+
obj.should == @alpha
|
998
|
+
group.should == 101
|
999
|
+
count.should == 5
|
1000
|
+
end
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
it "should be aliased to each_with_group_and_count" do
|
1004
|
+
search = ThinkingSphinx::Search.new
|
1005
|
+
search.each_with_group_and_count do |obj, group, count|
|
1006
|
+
obj.should == @alpha
|
1007
|
+
group.should == 101
|
1008
|
+
count.should == 5
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
describe '.each_with_weighting' do
|
1014
|
+
before :each do
|
1015
|
+
@alpha = Alpha.new
|
1016
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
1017
|
+
|
1018
|
+
@client.stub! :query => {
|
1019
|
+
:matches => [{
|
1020
|
+
:attributes => {
|
1021
|
+
'sphinx_internal_id' => @alpha.id,
|
1022
|
+
'class_crc' => Alpha.to_crc32
|
1023
|
+
}, :weight => 12
|
1024
|
+
}]
|
1025
|
+
}
|
1026
|
+
Alpha.stub!(:find => [@alpha])
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
it "should yield the match and weight" do
|
1030
|
+
search = ThinkingSphinx::Search.new
|
1031
|
+
search.each_with_weighting do |obj, weight|
|
1032
|
+
obj.should == @alpha
|
1033
|
+
weight.should == 12
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
describe '.each_with_*' do
|
1039
|
+
before :each do
|
1040
|
+
@alpha = Alpha.new
|
1041
|
+
@alpha.stub!(:id => 1, :read_attribute => 1)
|
1042
|
+
|
1043
|
+
@client.stub! :query => {
|
1044
|
+
:matches => [{
|
1045
|
+
:attributes => {
|
1046
|
+
'sphinx_internal_id' => @alpha.id,
|
1047
|
+
'class_crc' => Alpha.to_crc32,
|
1048
|
+
'@geodist' => 101,
|
1049
|
+
'@groupby' => 102,
|
1050
|
+
'@count' => 103
|
1051
|
+
}, :weight => 12
|
1052
|
+
}]
|
1053
|
+
}
|
1054
|
+
Alpha.stub!(:find => [@alpha])
|
1055
|
+
|
1056
|
+
@search = ThinkingSphinx::Search.new
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
it "should yield geodist if requested" do
|
1060
|
+
@search.each_with_geodist do |obj, distance|
|
1061
|
+
obj.should == @alpha
|
1062
|
+
distance.should == 101
|
1063
|
+
end
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
it "should yield count if requested" do
|
1067
|
+
@search.each_with_count do |obj, count|
|
1068
|
+
obj.should == @alpha
|
1069
|
+
count.should == 103
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
it "should yield groupby if requested" do
|
1074
|
+
@search.each_with_groupby do |obj, group|
|
1075
|
+
obj.should == @alpha
|
1076
|
+
group.should == 102
|
1077
|
+
end
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
it "should still use the array's each_with_index" do
|
1081
|
+
@search.each_with_index do |obj, index|
|
1082
|
+
obj.should == @alpha
|
1083
|
+
index.should == 0
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
describe '#excerpt_for' do
|
1089
|
+
before :each do
|
1090
|
+
@client.stub!(:excerpts => ['excerpted string'])
|
1091
|
+
@client.stub!(:query => {
|
1092
|
+
:matches => [],
|
1093
|
+
:words => {'one' => {}, 'two' => {}}
|
1094
|
+
})
|
1095
|
+
@search = ThinkingSphinx::Search.new(:classes => [Alpha])
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
it "should return the Sphinx excerpt value" do
|
1099
|
+
@search.excerpt_for('string').should == 'excerpted string'
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
it "should use the given model's core index" do
|
1103
|
+
@client.should_receive(:excerpts) do |options|
|
1104
|
+
options[:index].should == 'alpha_core'
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
@search.excerpt_for('string')
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
it "should optionally take a second argument to allow for multi-model searches" do
|
1111
|
+
@client.should_receive(:excerpts) do |options|
|
1112
|
+
options[:index].should == 'beta_core'
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
@search.excerpt_for('string', Beta)
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
it "should join the words together" do
|
1119
|
+
@client.should_receive(:excerpts) do |options|
|
1120
|
+
options[:words].should == @search.results[:words].keys.join(' ')
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
@search.excerpt_for('string', Beta)
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
it "should use the correct index in STI situations" do
|
1127
|
+
@client.should_receive(:excerpts) do |options|
|
1128
|
+
options[:index].should == 'person_core'
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
@search.excerpt_for('string', Parent)
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
describe '#search' do
|
1136
|
+
before :each do
|
1137
|
+
@search = ThinkingSphinx::Search.new('word',
|
1138
|
+
:conditions => {:field => 'field'},
|
1139
|
+
:with => {:int => 5}
|
1140
|
+
)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
it "should return itself" do
|
1144
|
+
@search.search.object_id.should == @search.object_id
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
it "should merge in arguments" do
|
1148
|
+
@client.should_receive(:query) do |query, index, comments|
|
1149
|
+
query.should == 'word more @field field'
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
@search.search('more').first
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
it "should merge conditions" do
|
1156
|
+
@client.should_receive(:query) do |query, index, comments|
|
1157
|
+
query.should match(/@name plato/)
|
1158
|
+
query.should match(/@field field/)
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
@search.search(:conditions => {:name => 'plato'}).first
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
it "should merge filters" do
|
1165
|
+
@search.search(:with => {:float => 1.5}).first
|
1166
|
+
|
1167
|
+
@client.filters.detect { |filter|
|
1168
|
+
filter.attribute == 'float'
|
1169
|
+
}.should_not be_nil
|
1170
|
+
@client.filters.detect { |filter|
|
1171
|
+
filter.attribute == 'int'
|
1172
|
+
}.should_not be_nil
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
describe ThinkingSphinx::Search, "playing nice with Search model" do
|
1178
|
+
it "should not conflict with models called Search" do
|
1179
|
+
lambda { Search.find(:all) }.should_not raise_error
|
1180
|
+
end
|
1181
|
+
end
|