sphinx 0.9.9.2117

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/.gitignore +4 -0
  2. data/README.rdoc +243 -0
  3. data/Rakefile +45 -0
  4. data/VERSION.yml +5 -0
  5. data/init.rb +1 -0
  6. data/lib/sphinx/buffered_io.rb +26 -0
  7. data/lib/sphinx/client.rb +2426 -0
  8. data/lib/sphinx/constants.rb +179 -0
  9. data/lib/sphinx/indifferent_access.rb +152 -0
  10. data/lib/sphinx/request.rb +121 -0
  11. data/lib/sphinx/response.rb +71 -0
  12. data/lib/sphinx/server.rb +170 -0
  13. data/lib/sphinx/timeout.rb +31 -0
  14. data/lib/sphinx.rb +51 -0
  15. data/spec/client_response_spec.rb +170 -0
  16. data/spec/client_spec.rb +669 -0
  17. data/spec/client_validations_spec.rb +859 -0
  18. data/spec/fixtures/default_search.php +8 -0
  19. data/spec/fixtures/default_search_index.php +8 -0
  20. data/spec/fixtures/excerpt_custom.php +11 -0
  21. data/spec/fixtures/excerpt_default.php +8 -0
  22. data/spec/fixtures/excerpt_flags.php +12 -0
  23. data/spec/fixtures/field_weights.php +9 -0
  24. data/spec/fixtures/filter.php +9 -0
  25. data/spec/fixtures/filter_exclude.php +9 -0
  26. data/spec/fixtures/filter_float_range.php +9 -0
  27. data/spec/fixtures/filter_float_range_exclude.php +9 -0
  28. data/spec/fixtures/filter_range.php +9 -0
  29. data/spec/fixtures/filter_range_exclude.php +9 -0
  30. data/spec/fixtures/filter_range_int64.php +10 -0
  31. data/spec/fixtures/filter_ranges.php +10 -0
  32. data/spec/fixtures/filters.php +10 -0
  33. data/spec/fixtures/filters_different.php +13 -0
  34. data/spec/fixtures/geo_anchor.php +9 -0
  35. data/spec/fixtures/group_by_attr.php +9 -0
  36. data/spec/fixtures/group_by_attrpair.php +9 -0
  37. data/spec/fixtures/group_by_day.php +9 -0
  38. data/spec/fixtures/group_by_day_sort.php +9 -0
  39. data/spec/fixtures/group_by_month.php +9 -0
  40. data/spec/fixtures/group_by_week.php +9 -0
  41. data/spec/fixtures/group_by_year.php +9 -0
  42. data/spec/fixtures/group_distinct.php +10 -0
  43. data/spec/fixtures/id_range.php +9 -0
  44. data/spec/fixtures/id_range64.php +9 -0
  45. data/spec/fixtures/index_weights.php +9 -0
  46. data/spec/fixtures/keywords.php +8 -0
  47. data/spec/fixtures/limits.php +9 -0
  48. data/spec/fixtures/limits_cutoff.php +9 -0
  49. data/spec/fixtures/limits_max.php +9 -0
  50. data/spec/fixtures/limits_max_cutoff.php +9 -0
  51. data/spec/fixtures/match_all.php +9 -0
  52. data/spec/fixtures/match_any.php +9 -0
  53. data/spec/fixtures/match_boolean.php +9 -0
  54. data/spec/fixtures/match_extended.php +9 -0
  55. data/spec/fixtures/match_extended2.php +9 -0
  56. data/spec/fixtures/match_fullscan.php +9 -0
  57. data/spec/fixtures/match_phrase.php +9 -0
  58. data/spec/fixtures/max_query_time.php +9 -0
  59. data/spec/fixtures/miltiple_queries.php +12 -0
  60. data/spec/fixtures/ranking_bm25.php +9 -0
  61. data/spec/fixtures/ranking_fieldmask.php +9 -0
  62. data/spec/fixtures/ranking_matchany.php +9 -0
  63. data/spec/fixtures/ranking_none.php +9 -0
  64. data/spec/fixtures/ranking_proximity.php +9 -0
  65. data/spec/fixtures/ranking_proximity_bm25.php +9 -0
  66. data/spec/fixtures/ranking_wordcount.php +9 -0
  67. data/spec/fixtures/retries.php +9 -0
  68. data/spec/fixtures/retries_delay.php +9 -0
  69. data/spec/fixtures/select.php +9 -0
  70. data/spec/fixtures/set_override.php +11 -0
  71. data/spec/fixtures/sort_attr_asc.php +9 -0
  72. data/spec/fixtures/sort_attr_desc.php +9 -0
  73. data/spec/fixtures/sort_expr.php +9 -0
  74. data/spec/fixtures/sort_extended.php +9 -0
  75. data/spec/fixtures/sort_relevance.php +9 -0
  76. data/spec/fixtures/sort_time_segments.php +9 -0
  77. data/spec/fixtures/sphinxapi.php +1633 -0
  78. data/spec/fixtures/update_attributes.php +8 -0
  79. data/spec/fixtures/update_attributes_mva.php +8 -0
  80. data/spec/fixtures/weights.php +9 -0
  81. data/spec/spec_helper.rb +24 -0
  82. data/spec/sphinx/sphinx-id64.conf +67 -0
  83. data/spec/sphinx/sphinx.conf +67 -0
  84. data/spec/sphinx/sphinx_test.sql +88 -0
  85. data/sphinx.gemspec +127 -0
  86. metadata +142 -0
@@ -0,0 +1,669 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Sphinx::Client, 'disconnected' do
4
+ context 'in with_server method' do
5
+ before :each do
6
+ @sphinx = Sphinx::Client.new
7
+ @servers = [{:host => 'localhost', :port => 1}, {:host => 'localhost', :port => 2}]
8
+ end
9
+
10
+ context 'without retries' do
11
+ it 'should use single Server instance' do
12
+ 2.times do
13
+ cnt = 0
14
+ @sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0] }
15
+ cnt.should == 1
16
+ end
17
+ end
18
+
19
+ it 'should raise an exception on error' do
20
+ 2.times do
21
+ cnt = 0
22
+ expect {
23
+ @sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0]; raise Sphinx::SphinxConnectError }
24
+ }.to raise_error(Sphinx::SphinxConnectError)
25
+ cnt.should == 1
26
+ end
27
+ end
28
+
29
+ it 'should select server based on index' do
30
+ @sphinx.SetServers(@servers)
31
+ cnt = 0
32
+ @sphinx.send(:with_server, 0) { |server| cnt += 1; server.should == @sphinx.servers[0] }
33
+ cnt.should == 1
34
+ cnt = 0
35
+ @sphinx.send(:with_server, 1) { |server| cnt += 1; server.should == @sphinx.servers[1] }
36
+ cnt.should == 1
37
+ cnt = 0
38
+ @sphinx.send(:with_server, 2) { |server| cnt += 1; server.should == @sphinx.servers[0] }
39
+ cnt.should == 1
40
+ end
41
+
42
+ it 'should select given server' do
43
+ @sphinx.SetServers(@servers)
44
+ cnt = 0
45
+ @sphinx.send(:with_server, @sphinx.servers[0]) { |server| cnt += 1; server.should == @sphinx.servers[0] }
46
+ cnt.should == 1
47
+ cnt = 0
48
+ @sphinx.send(:with_server, @sphinx.servers[1]) { |server| cnt += 1; server.should == @sphinx.servers[1] }
49
+ cnt.should == 1
50
+ end
51
+ end
52
+
53
+ context 'with retries' do
54
+ before :each do
55
+ @sphinx.SetConnectTimeout(0, 3)
56
+ end
57
+
58
+ it 'should raise an exception on error' do
59
+ cnt = 0
60
+ expect {
61
+ @sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0]; raise Sphinx::SphinxConnectError }
62
+ }.to raise_error(Sphinx::SphinxConnectError)
63
+ cnt.should == 3
64
+ end
65
+
66
+ it 'should round-robin servers and raise an exception on error' do
67
+ @sphinx.SetServers(@servers)
68
+ cnt = 0
69
+ expect {
70
+ @sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[(cnt - 1) % 2]; raise Sphinx::SphinxConnectError }
71
+ }.to raise_error(Sphinx::SphinxConnectError)
72
+ cnt.should == 3
73
+ end
74
+
75
+ it 'should round-robin servers with respect to passed index and raise an exception on error' do
76
+ @sphinx.SetServers(@servers)
77
+ cnt = 0
78
+ expect {
79
+ @sphinx.send(:with_server, 1) { |server| cnt += 1; server.should == @sphinx.servers[cnt % 2]; raise Sphinx::SphinxConnectError }
80
+ }.to raise_error(Sphinx::SphinxConnectError)
81
+ cnt.should == 3
82
+ end
83
+
84
+ it 'should round-robin with respect to attempts number passed' do
85
+ @sphinx.SetServers(@servers)
86
+ cnt = 0
87
+ expect {
88
+ @sphinx.send(:with_server, 0, 5) { |server| cnt += 1; server.should == @sphinx.servers[(cnt - 1) % 2]; raise Sphinx::SphinxConnectError }
89
+ }.to raise_error(Sphinx::SphinxConnectError)
90
+ cnt.should == 5
91
+ end
92
+ end
93
+ end
94
+
95
+ context 'in with_socket method' do
96
+ before :each do
97
+ @sphinx = Sphinx::Client.new
98
+ @socket = mock('TCPSocket')
99
+ end
100
+
101
+ context 'without retries' do
102
+ before :each do
103
+ @server = mock('Server')
104
+ @server.should_receive(:get_socket).and_yield(@socket).and_return(@socket)
105
+ @server.should_receive(:free_socket).with(@socket).at_least(1)
106
+ end
107
+
108
+ it 'should initialize session' do
109
+ @socket.should_receive(:write).with([1].pack('N'))
110
+ @socket.should_receive(:read).with(4).and_return([1].pack('N'))
111
+ cnt = 0
112
+ @sphinx.send(:with_socket, @server) { |socket| cnt += 1; socket.should == @socket }
113
+ cnt.should == 1
114
+ end
115
+
116
+ it 'should raise exception when searchd protocol is not 1+' do
117
+ @socket.should_receive(:write).with([1].pack('N'))
118
+ @socket.should_receive(:read).with(4).and_return([0].pack('N'))
119
+ cnt = 0
120
+ expect {
121
+ @sphinx.send(:with_socket, @server) { cnt += 1; }
122
+ }.to raise_error(Sphinx::SphinxConnectError, 'expected searchd protocol version 1+, got version \'0\'')
123
+ cnt.should == 0
124
+ end
125
+
126
+ it 'should handle request timeouts' do
127
+ @socket.should_receive(:write).with([1].pack('N'))
128
+ @socket.should_receive(:read).with(4).and_return([1].pack('N'))
129
+ @sphinx.SetRequestTimeout(1)
130
+ cnt = 0
131
+ expect {
132
+ @sphinx.send(:with_socket, @server) { cnt += 1; sleep 2 }
133
+ }.to raise_error(Sphinx::SphinxResponseError, 'failed to read searchd response (msg=time\'s up!)')
134
+ cnt.should == 1
135
+
136
+ @sphinx.GetLastError.should == 'failed to read searchd response (msg=time\'s up!)'
137
+ @sphinx.IsConnectError.should be_false
138
+ end
139
+
140
+ it 'should re-reaise Sphinx errors' do
141
+ @socket.should_receive(:write).with([1].pack('N'))
142
+ @socket.should_receive(:read).with(4).and_return([1].pack('N'))
143
+ cnt = 0
144
+ expect {
145
+ @sphinx.send(:with_socket, @server) { cnt += 1; raise Sphinx::SphinxInternalError, 'hello' }
146
+ }.to raise_error(Sphinx::SphinxInternalError, 'hello')
147
+ cnt.should == 1
148
+
149
+ @sphinx.GetLastError.should == 'hello'
150
+ @sphinx.IsConnectError.should be_false
151
+ end
152
+ end
153
+
154
+ context 'with retries' do
155
+ before :each do
156
+ @sphinx.SetRequestTimeout(0, 3)
157
+ # two more times yielding - retries
158
+ @server = mock('Server')
159
+ @server.should_receive(:get_socket).at_least(1).times.and_yield(@socket).and_return(@socket)
160
+ @server.should_receive(:free_socket).with(@socket).at_least(1)
161
+ end
162
+
163
+ it 'should raise an exception on error' do
164
+ @socket.should_receive(:write).exactly(3).times.with([1].pack('N'))
165
+ @socket.should_receive(:read).exactly(3).times.with(4).and_return([1].pack('N'))
166
+ cnt = 0
167
+ expect {
168
+ @sphinx.send(:with_socket, @server) { cnt += 1; raise Sphinx::SphinxInternalError, 'hello' }
169
+ }.to raise_error(Sphinx::SphinxInternalError, 'hello')
170
+ cnt.should == 3
171
+
172
+ @sphinx.GetLastError.should == 'hello'
173
+ @sphinx.IsConnectError.should be_false
174
+ end
175
+ end
176
+ end
177
+
178
+ context 'in parse_response method' do
179
+ before :each do
180
+ @sphinx = Sphinx::Client.new
181
+ @socket = mock('TCPSocket')
182
+ end
183
+
184
+ it 'should receive response' do
185
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_OK, 1, 4].pack('n2N'))
186
+ @socket.should_receive(:read).with(4).and_return([0].pack('N'))
187
+ @sphinx.send(:parse_response, @socket, 1)
188
+ end
189
+
190
+ it 'should raise exception on zero-sized response' do
191
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_OK, 1, 0].pack('n2N'))
192
+ expect {
193
+ @sphinx.send(:parse_response, @socket, 1)
194
+ }.to raise_error(Sphinx::SphinxResponseError, 'received zero-sized searchd response')
195
+ end
196
+
197
+ it 'should raise exception when response is incomplete' do
198
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_OK, 1, 4].pack('n2N'))
199
+ @socket.should_receive(:read).with(4).and_return('')
200
+ expect {
201
+ @sphinx.send(:parse_response, @socket, 1)
202
+ }.to raise_error(Sphinx::SphinxResponseError)
203
+ end
204
+
205
+ it 'should set warning message when SEARCHD_WARNING received' do
206
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_WARNING, 1, 14].pack('n2N'))
207
+ @socket.should_receive(:read).with(14).and_return([5].pack('N') + 'helloworld')
208
+ @sphinx.send(:parse_response, @socket, 1).should == 'world'
209
+ @sphinx.GetLastWarning.should == 'hello'
210
+ end
211
+
212
+ it 'should raise exception when SEARCHD_ERROR received' do
213
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_ERROR, 1, 9].pack('n2N'))
214
+ @socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
215
+ expect {
216
+ @sphinx.send(:parse_response, @socket, 1)
217
+ }.to raise_error(Sphinx::SphinxInternalError, 'searchd error: hello')
218
+ end
219
+
220
+ it 'should raise exception when SEARCHD_RETRY received' do
221
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_RETRY, 1, 9].pack('n2N'))
222
+ @socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
223
+ expect {
224
+ @sphinx.send(:parse_response, @socket, 1)
225
+ }.to raise_error(Sphinx::SphinxTemporaryError, 'temporary searchd error: hello')
226
+ end
227
+
228
+ it 'should raise exception when unknown status received' do
229
+ @socket.should_receive(:read).with(8).and_return([65535, 1, 9].pack('n2N'))
230
+ @socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
231
+ expect {
232
+ @sphinx.send(:parse_response, @socket, 1)
233
+ }.to raise_error(Sphinx::SphinxUnknownError, 'unknown status code: \'65535\'')
234
+ end
235
+
236
+ it 'should set warning when server is older than client' do
237
+ @socket.should_receive(:read).with(8).and_return([Sphinx::SEARCHD_OK, 1, 9].pack('n2N'))
238
+ @socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
239
+ @sphinx.send(:parse_response, @socket, 5)
240
+ @sphinx.GetLastWarning.should == 'searchd command v.0.1 older than client\'s v.0.5, some options might not work'
241
+ end
242
+ end
243
+
244
+ context 'in Query method' do
245
+ before :each do
246
+ @sphinx = sphinx_create_client
247
+ end
248
+
249
+ it 'should generate valid request with default parameters' do
250
+ expected = sphinx_fixture('default_search')
251
+ @sock.should_receive(:write).with(expected)
252
+ sphinx_safe_call { @sphinx.Query('query') }
253
+ end
254
+
255
+ it 'should generate valid request with default parameters and index' do
256
+ expected = sphinx_fixture('default_search_index')
257
+ @sock.should_receive(:write).with(expected)
258
+ sphinx_safe_call { @sphinx.Query('query', 'index') }
259
+ end
260
+
261
+ it 'should generate valid request with limits' do
262
+ expected = sphinx_fixture('limits')
263
+ @sock.should_receive(:write).with(expected)
264
+ @sphinx.SetLimits(10, 20)
265
+ sphinx_safe_call { @sphinx.Query('query') }
266
+ end
267
+
268
+ it 'should generate valid request with limits and max number to retrieve' do
269
+ expected = sphinx_fixture('limits_max')
270
+ @sock.should_receive(:write).with(expected)
271
+ @sphinx.SetLimits(10, 20, 30)
272
+ sphinx_safe_call { @sphinx.Query('query') }
273
+ end
274
+
275
+ it 'should generate valid request with limits and cutoff to retrieve' do
276
+ expected = sphinx_fixture('limits_cutoff')
277
+ @sock.should_receive(:write).with(expected)
278
+ @sphinx.SetLimits(10, 20, 30, 40)
279
+ sphinx_safe_call { @sphinx.Query('query') }
280
+ end
281
+
282
+ it 'should generate valid request with max query time specified' do
283
+ expected = sphinx_fixture('max_query_time')
284
+ @sock.should_receive(:write).with(expected)
285
+ @sphinx.SetMaxQueryTime(1000)
286
+ sphinx_safe_call { @sphinx.Query('query') }
287
+ end
288
+
289
+ describe 'with match' do
290
+ [ :all, :any, :phrase, :boolean, :extended, :fullscan, :extended2 ].each do |match|
291
+ it "should generate valid request for SPH_MATCH_#{match.to_s.upcase}" do
292
+ expected = sphinx_fixture("match_#{match}")
293
+ @sock.should_receive(:write).with(expected)
294
+ @sphinx.SetMatchMode(Sphinx::const_get("SPH_MATCH_#{match.to_s.upcase}"))
295
+ sphinx_safe_call { @sphinx.Query('query') }
296
+ end
297
+
298
+ it "should generate valid request for \"#{match}\"" do
299
+ expected = sphinx_fixture("match_#{match}")
300
+ @sock.should_receive(:write).with(expected)
301
+ @sphinx.SetMatchMode(match.to_s)
302
+ sphinx_safe_call { @sphinx.Query('query') }
303
+ end
304
+
305
+ it "should generate valid request for :#{match}" do
306
+ expected = sphinx_fixture("match_#{match}")
307
+ @sock.should_receive(:write).with(expected)
308
+ @sphinx.SetMatchMode(match)
309
+ sphinx_safe_call { @sphinx.Query('query') }
310
+ end
311
+ end
312
+ end
313
+
314
+ describe 'with rank' do
315
+ [ :proximity_bm25, :bm25, :none, :wordcount, :proximity, :matchany, :fieldmask ].each do |rank|
316
+ it "should generate valid request for SPH_RANK_#{rank.to_s.upcase}" do
317
+ expected = sphinx_fixture("ranking_#{rank}")
318
+ @sock.should_receive(:write).with(expected)
319
+ @sphinx.SetRankingMode(Sphinx::Client.const_get("SPH_RANK_#{rank.to_s.upcase}"))
320
+ sphinx_safe_call { @sphinx.Query('query') }
321
+ end
322
+
323
+ it "should generate valid request for \"#{rank}\"" do
324
+ expected = sphinx_fixture("ranking_#{rank}")
325
+ @sock.should_receive(:write).with(expected)
326
+ @sphinx.SetRankingMode(rank.to_s)
327
+ sphinx_safe_call { @sphinx.Query('query') }
328
+ end
329
+
330
+ it "should generate valid request for :#{rank}" do
331
+ expected = sphinx_fixture("ranking_#{rank}")
332
+ @sock.should_receive(:write).with(expected)
333
+ @sphinx.SetRankingMode(rank)
334
+ sphinx_safe_call { @sphinx.Query('query') }
335
+ end
336
+ end
337
+ end
338
+
339
+ describe 'with sorting' do
340
+ [ :attr_desc, :relevance, :attr_asc, :time_segments, :extended, :expr ].each do |mode|
341
+ it "should generate valid request for SPH_SORT_#{mode.to_s.upcase}" do
342
+ expected = sphinx_fixture("sort_#{mode}")
343
+ @sock.should_receive(:write).with(expected)
344
+ @sphinx.SetSortMode(Sphinx::Client.const_get("SPH_SORT_#{mode.to_s.upcase}"), mode == :relevance ? '' : 'sortby')
345
+ sphinx_safe_call { @sphinx.Query('query') }
346
+ end
347
+
348
+ it "should generate valid request for \"#{mode}\"" do
349
+ expected = sphinx_fixture("sort_#{mode}")
350
+ @sock.should_receive(:write).with(expected)
351
+ @sphinx.SetSortMode(mode.to_s, mode == :relevance ? '' : 'sortby')
352
+ sphinx_safe_call { @sphinx.Query('query') }
353
+ end
354
+
355
+ it "should generate valid request for :#{mode}" do
356
+ expected = sphinx_fixture("sort_#{mode}")
357
+ @sock.should_receive(:write).with(expected)
358
+ @sphinx.SetSortMode(mode, mode == :relevance ? '' : 'sortby')
359
+ sphinx_safe_call { @sphinx.Query('query') }
360
+ end
361
+ end
362
+ end
363
+
364
+ it 'should generate valid request with weights' do
365
+ expected = sphinx_fixture('weights')
366
+ @sock.should_receive(:write).with(expected)
367
+ @sphinx.SetWeights([10, 20, 30, 40])
368
+ sphinx_safe_call { @sphinx.Query('query') }
369
+ end
370
+
371
+ it 'should generate valid request with field weights' do
372
+ expected = sphinx_fixture('field_weights')
373
+ @sock.should_receive(:write).with(expected)
374
+ @sphinx.SetFieldWeights({'field1' => 10, 'field2' => 20})
375
+ sphinx_safe_call { @sphinx.Query('query') }
376
+ end
377
+
378
+ it 'should generate valid request with index weights' do
379
+ expected = sphinx_fixture('index_weights')
380
+ @sock.should_receive(:write).with(expected)
381
+ @sphinx.SetIndexWeights({'index1' => 10, 'index2' => 20})
382
+ sphinx_safe_call { @sphinx.Query('query') }
383
+ end
384
+
385
+ it 'should generate valid request with ID range' do
386
+ expected = sphinx_fixture('id_range')
387
+ @sock.should_receive(:write).with(expected)
388
+ @sphinx.SetIDRange(10, 20)
389
+ sphinx_safe_call { @sphinx.Query('query') }
390
+ end
391
+
392
+ it 'should generate valid request with ID range and 64-bit ints' do
393
+ expected = sphinx_fixture('id_range64')
394
+ @sock.should_receive(:write).with(expected)
395
+ @sphinx.SetIDRange(8589934591, 17179869183)
396
+ sphinx_safe_call { @sphinx.Query('query') }
397
+ end
398
+
399
+ it 'should generate valid request with values filter' do
400
+ expected = sphinx_fixture('filter')
401
+ @sock.should_receive(:write).with(expected)
402
+ @sphinx.SetFilter('attr', [10, 20, 30])
403
+ sphinx_safe_call { @sphinx.Query('query') }
404
+ end
405
+
406
+ it 'should generate valid request with two values filters' do
407
+ expected = sphinx_fixture('filters')
408
+ @sock.should_receive(:write).with(expected)
409
+ @sphinx.SetFilter('attr2', [40, 50])
410
+ @sphinx.SetFilter('attr1', [10, 20, 30])
411
+ sphinx_safe_call { @sphinx.Query('query') }
412
+ end
413
+
414
+ it 'should generate valid request with values filter excluded' do
415
+ expected = sphinx_fixture('filter_exclude')
416
+ @sock.should_receive(:write).with(expected)
417
+ @sphinx.SetFilter('attr', [10, 20, 30], true)
418
+ sphinx_safe_call { @sphinx.Query('query') }
419
+ end
420
+
421
+ it 'should generate valid request with values filter range' do
422
+ expected = sphinx_fixture('filter_range')
423
+ @sock.should_receive(:write).with(expected)
424
+ @sphinx.SetFilterRange('attr', 10, 20)
425
+ sphinx_safe_call { @sphinx.Query('query') }
426
+ end
427
+
428
+ it 'should generate valid request with two filter ranges' do
429
+ expected = sphinx_fixture('filter_ranges')
430
+ @sock.should_receive(:write).with(expected)
431
+ @sphinx.SetFilterRange('attr2', 30, 40)
432
+ @sphinx.SetFilterRange('attr1', 10, 20)
433
+ sphinx_safe_call { @sphinx.Query('query') }
434
+ end
435
+
436
+ it 'should generate valid request with filter range excluded' do
437
+ expected = sphinx_fixture('filter_range_exclude')
438
+ @sock.should_receive(:write).with(expected)
439
+ @sphinx.SetFilterRange('attr', 10, 20, true)
440
+ sphinx_safe_call { @sphinx.Query('query') }
441
+ end
442
+
443
+ it 'should generate valid request with signed int64-based filter range' do
444
+ expected = sphinx_fixture('filter_range_int64')
445
+ @sock.should_receive(:write).with(expected)
446
+ @sphinx.SetFilterRange('attr1', -10, 20)
447
+ @sphinx.SetFilterRange('attr2', -1099511627770, 1099511627780)
448
+ sphinx_safe_call { @sphinx.Query('query') }
449
+ end
450
+
451
+ it 'should generate valid request with float filter range' do
452
+ expected = sphinx_fixture('filter_float_range')
453
+ @sock.should_receive(:write).with(expected)
454
+ @sphinx.SetFilterFloatRange('attr', 10.5, 20.3)
455
+ sphinx_safe_call { @sphinx.Query('query') }
456
+ end
457
+
458
+ it 'should generate valid request with float filter excluded' do
459
+ expected = sphinx_fixture('filter_float_range_exclude')
460
+ @sock.should_receive(:write).with(expected)
461
+ @sphinx.SetFilterFloatRange('attr', 10.5, 20.3, true)
462
+ sphinx_safe_call { @sphinx.Query('query') }
463
+ end
464
+
465
+ it 'should generate valid request with different filters' do
466
+ expected = sphinx_fixture('filters_different')
467
+ @sock.should_receive(:write).with(expected)
468
+ @sphinx.SetFilterRange('attr1', 10, 20, true)
469
+ @sphinx.SetFilter('attr3', [30, 40, 50])
470
+ @sphinx.SetFilterRange('attr1', 60, 70)
471
+ @sphinx.SetFilter('attr2', [80, 90, 100], true)
472
+ @sphinx.SetFilterFloatRange('attr1', 60.8, 70.5)
473
+ sphinx_safe_call { @sphinx.Query('query') }
474
+ end
475
+
476
+ it 'should generate valid request with geographical anchor point' do
477
+ expected = sphinx_fixture('geo_anchor')
478
+ @sock.should_receive(:write).with(expected)
479
+ @sphinx.SetGeoAnchor('attrlat', 'attrlong', 20.3, 40.7)
480
+ sphinx_safe_call { @sphinx.Query('query') }
481
+ end
482
+
483
+ describe 'with group by' do
484
+ [ :day, :week, :month, :year, :attr, :attrpair ].each do |groupby|
485
+ it "should generate valid request for SPH_GROUPBY_#{groupby.to_s.upcase}" do
486
+ expected = sphinx_fixture("group_by_#{groupby}")
487
+ @sock.should_receive(:write).with(expected)
488
+ @sphinx.SetGroupBy('attr', Sphinx::const_get("SPH_GROUPBY_#{groupby.to_s.upcase}"))
489
+ sphinx_safe_call { @sphinx.Query('query') }
490
+ end
491
+
492
+ it "should generate valid request for \"#{groupby}\"" do
493
+ expected = sphinx_fixture("group_by_#{groupby}")
494
+ @sock.should_receive(:write).with(expected)
495
+ @sphinx.SetGroupBy('attr', groupby.to_s)
496
+ sphinx_safe_call { @sphinx.Query('query') }
497
+ end
498
+
499
+ it "should generate valid request for :#{groupby}" do
500
+ expected = sphinx_fixture("group_by_#{groupby}")
501
+ @sock.should_receive(:write).with(expected)
502
+ @sphinx.SetGroupBy('attr', groupby)
503
+ sphinx_safe_call { @sphinx.Query('query') }
504
+ end
505
+ end
506
+
507
+ it 'should generate valid request for SPH_GROUPBY_DAY with sort' do
508
+ expected = sphinx_fixture('group_by_day_sort')
509
+ @sock.should_receive(:write).with(expected)
510
+ @sphinx.SetGroupBy('attr', Sphinx::SPH_GROUPBY_DAY, 'somesort')
511
+ sphinx_safe_call { @sphinx.Query('query') }
512
+ end
513
+
514
+ it 'should generate valid request with count-distinct attribute' do
515
+ expected = sphinx_fixture('group_distinct')
516
+ @sock.should_receive(:write).with(expected)
517
+ @sphinx.SetGroupBy('attr', Sphinx::SPH_GROUPBY_DAY)
518
+ @sphinx.SetGroupDistinct('attr')
519
+ sphinx_safe_call { @sphinx.Query('query') }
520
+ end
521
+ end
522
+
523
+ it 'should generate valid request with retries count specified' do
524
+ expected = sphinx_fixture('retries')
525
+ @sock.should_receive(:write).with(expected)
526
+ @sphinx.SetRetries(10)
527
+ sphinx_safe_call { @sphinx.Query('query') }
528
+ end
529
+
530
+ it 'should generate valid request with retries count and delay specified' do
531
+ expected = sphinx_fixture('retries_delay')
532
+ @sock.should_receive(:write).with(expected)
533
+ @sphinx.SetRetries(10, 20)
534
+ sphinx_safe_call { @sphinx.Query('query') }
535
+ end
536
+
537
+ it 'should generate valid request for SetOverride' do
538
+ expected = sphinx_fixture('set_override')
539
+ @sock.should_receive(:write).with(expected)
540
+ @sphinx.SetOverride('attr1', Sphinx::SPH_ATTR_INTEGER, { 10 => 20 })
541
+ @sphinx.SetOverride('attr2', Sphinx::SPH_ATTR_FLOAT, { 11 => 30.3 })
542
+ @sphinx.SetOverride('attr3', Sphinx::SPH_ATTR_BIGINT, { 12 => 1099511627780 })
543
+ sphinx_safe_call { @sphinx.Query('query') }
544
+ end
545
+
546
+ it 'should generate valid request for SetSelect' do
547
+ expected = sphinx_fixture('select')
548
+ @sock.should_receive(:write).with(expected)
549
+ @sphinx.SetSelect('attr1, attr2')
550
+ sphinx_safe_call { @sphinx.Query('query') }
551
+ end
552
+ end
553
+
554
+ context 'in RunQueries method' do
555
+ before(:each) do
556
+ @sphinx = sphinx_create_client
557
+ end
558
+
559
+ it 'should generate valid request for multiple queries' do
560
+ expected = sphinx_fixture('miltiple_queries')
561
+ @sock.should_receive(:write).with(expected)
562
+
563
+ @sphinx.SetRetries(10, 20)
564
+ @sphinx.AddQuery('test1')
565
+ @sphinx.SetGroupBy('attr', Sphinx::SPH_GROUPBY_DAY)
566
+ @sphinx.AddQuery('test2')
567
+
568
+ sphinx_safe_call { @sphinx.RunQueries }
569
+ end
570
+ end
571
+
572
+ context 'in BuildExcerpts method' do
573
+ before :each do
574
+ @sphinx = sphinx_create_client
575
+ end
576
+
577
+ it 'should generate valid request with default parameters' do
578
+ expected = sphinx_fixture('excerpt_default')
579
+ @sock.should_receive(:write).with(expected)
580
+ sphinx_safe_call { @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2') }
581
+ end
582
+
583
+ it 'should generate valid request with custom parameters' do
584
+ expected = sphinx_fixture('excerpt_custom')
585
+ @sock.should_receive(:write).with(expected)
586
+ sphinx_safe_call do
587
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'before_match' => 'before',
588
+ 'after_match' => 'after',
589
+ 'chunk_separator' => 'separator',
590
+ 'limit' => 10 })
591
+ end
592
+ end
593
+
594
+ it 'should generate valid request with custom parameters as symbols' do
595
+ expected = sphinx_fixture('excerpt_custom')
596
+ @sock.should_receive(:write).with(expected)
597
+ sphinx_safe_call do
598
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { :before_match => 'before',
599
+ :after_match => 'after',
600
+ :chunk_separator => 'separator',
601
+ :limit => 10 })
602
+ end
603
+ end
604
+
605
+ it 'should generate valid request with flags' do
606
+ expected = sphinx_fixture('excerpt_flags')
607
+ @sock.should_receive(:write).with(expected)
608
+ sphinx_safe_call do
609
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'exact_phrase' => true,
610
+ 'single_passage' => true,
611
+ 'use_boundaries' => true,
612
+ 'weight_order' => true,
613
+ 'query_mode' => true })
614
+ end
615
+ end
616
+
617
+ it 'should generate valid request with flags as symbols' do
618
+ expected = sphinx_fixture('excerpt_flags')
619
+ @sock.should_receive(:write).with(expected)
620
+ sphinx_safe_call do
621
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { :exact_phrase => true,
622
+ :single_passage => true,
623
+ :use_boundaries => true,
624
+ :weight_order => true,
625
+ :query_mode => true })
626
+ end
627
+ end
628
+ end
629
+
630
+ context 'in BuildKeywords method' do
631
+ before :each do
632
+ @sphinx = sphinx_create_client
633
+ end
634
+
635
+ it 'should generate valid request' do
636
+ expected = sphinx_fixture('keywords')
637
+ @sock.should_receive(:write).with(expected)
638
+ sphinx_safe_call { @sphinx.BuildKeywords('test', 'index', true) }
639
+ end
640
+ end
641
+
642
+ context 'in UpdateAttributes method' do
643
+ before :each do
644
+ @sphinx = sphinx_create_client
645
+ end
646
+
647
+ it 'should generate valid request' do
648
+ expected = sphinx_fixture('update_attributes')
649
+ @sock.should_receive(:write).with(expected)
650
+ sphinx_safe_call { @sphinx.UpdateAttributes('index', ['group'], { 123 => [456] }) }
651
+ end
652
+
653
+ it 'should generate valid request for MVA' do
654
+ expected = sphinx_fixture('update_attributes_mva')
655
+ @sock.should_receive(:write).with(expected)
656
+ sphinx_safe_call { @sphinx.UpdateAttributes('index', ['group', 'category'], { 123 => [ [456, 789], [1, 2, 3] ] }, true) }
657
+ end
658
+ end
659
+
660
+ context 'in EscapeString method' do
661
+ before :each do
662
+ @sphinx = Sphinx::Client.new
663
+ end
664
+
665
+ it 'should escape special characters' do
666
+ @sphinx.escape_string("escaping-sample@query/string").should == "escaping\\-sample\\@query\\/string"
667
+ end
668
+ end
669
+ end