sphinx 0.9.9.2117 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -3
- data/README.rdoc +18 -213
- data/Rakefile +15 -24
- data/VERSION.yml +1 -2
- data/install.rb +5 -0
- data/lib/sphinx/client.rb +893 -2089
- data/lib/sphinx/request.rb +11 -82
- data/lib/sphinx/response.rb +0 -2
- data/lib/sphinx.rb +5 -50
- data/spec/client_response_spec.rb +40 -33
- data/spec/client_spec.rb +471 -669
- data/spec/fixtures/ranking_sph04.php +9 -0
- data/spec/fixtures/sphinxapi.php +69 -54
- data/spec/sphinx/sphinx-id64.conf +1 -1
- data/spec/sphinx/sphinx.conf +1 -1
- data/spec/sphinx/sphinx_test.sql +2 -4
- data/sphinx.gemspec +8 -13
- data/sphinx.yml.tpl +3 -0
- data/tasks/sphinx.rake +75 -0
- metadata +6 -11
- data/lib/sphinx/buffered_io.rb +0 -26
- data/lib/sphinx/constants.rb +0 -179
- data/lib/sphinx/indifferent_access.rb +0 -152
- data/lib/sphinx/server.rb +0 -170
- data/lib/sphinx/timeout.rb +0 -31
- data/spec/client_validations_spec.rb +0 -859
- data/spec/spec_helper.rb +0 -24
data/spec/client_spec.rb
CHANGED
@@ -1,669 +1,471 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
@
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
end
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
|
1
|
+
require File.dirname(__FILE__) + '/../init'
|
2
|
+
|
3
|
+
class SphinxSpecError < StandardError; end
|
4
|
+
|
5
|
+
module SphinxFixtureHelper
|
6
|
+
def sphinx_fixture(name)
|
7
|
+
`php #{File.dirname(__FILE__)}/fixtures/#{name}.php`
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module SphinxApiCall
|
12
|
+
def create_sphinx
|
13
|
+
@sphinx = Sphinx::Client.new
|
14
|
+
@sock = mock('TCPSocket')
|
15
|
+
@sphinx.stub!(:Connect).and_return(@sock)
|
16
|
+
@sphinx.stub!(:GetResponse).and_raise(SphinxSpecError)
|
17
|
+
return @sphinx
|
18
|
+
end
|
19
|
+
|
20
|
+
def safe_call
|
21
|
+
yield
|
22
|
+
rescue SphinxSpecError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'The Connect method of Sphinx::Client' do
|
27
|
+
before(:each) do
|
28
|
+
@sphinx = Sphinx::Client.new
|
29
|
+
@sock = mock('TCPSocket')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should establish TCP connection to the server and initialize session' do
|
33
|
+
TCPSocket.should_receive(:new).with('localhost', 3312).and_return(@sock)
|
34
|
+
@sock.should_receive(:recv).with(4).and_return([1].pack('N'))
|
35
|
+
@sock.should_receive(:send).with([1].pack('N'), 0)
|
36
|
+
@sphinx.send(:Connect).should be(@sock)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should raise exception when searchd protocol is not 1+' do
|
40
|
+
TCPSocket.should_receive(:new).with('localhost', 3312).and_return(@sock)
|
41
|
+
@sock.should_receive(:send).with([1].pack('N'), 0)
|
42
|
+
@sock.should_receive(:recv).with(4).and_return([0].pack('N'))
|
43
|
+
@sock.should_receive(:close)
|
44
|
+
lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
|
45
|
+
@sphinx.GetLastError.should == 'expected searchd protocol version 1+, got version \'0\''
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should raise exception on connection error' do
|
49
|
+
TCPSocket.should_receive(:new).with('localhost', 3312).and_raise(Errno::EBADF)
|
50
|
+
lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
|
51
|
+
@sphinx.GetLastError.should == 'connection to localhost:3312 failed (errno=9, msg=Bad file descriptor)'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should use custom host and port' do
|
55
|
+
@sphinx.SetServer('anotherhost', 55555)
|
56
|
+
TCPSocket.should_receive(:new).with('anotherhost', 55555).and_raise(Errno::EBADF)
|
57
|
+
lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'The GetResponse method of Sphinx::Client' do
|
62
|
+
before(:each) do
|
63
|
+
@sphinx = Sphinx::Client.new
|
64
|
+
@sock = mock('TCPSocket')
|
65
|
+
@sock.should_receive(:close)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should receive response' do
|
69
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
|
70
|
+
@sock.should_receive(:recv).with(4).and_return([0].pack('N'))
|
71
|
+
@sphinx.send(:GetResponse, @sock, 1)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should raise exception on zero-sized response' do
|
75
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 0].pack('n2N'))
|
76
|
+
lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxResponseError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should raise exception when response is incomplete' do
|
80
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
|
81
|
+
@sock.should_receive(:recv).with(4).and_raise(EOFError)
|
82
|
+
lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxResponseError)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should set warning message when SEARCHD_WARNING received' do
|
86
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_WARNING, 1, 14].pack('n2N'))
|
87
|
+
@sock.should_receive(:recv).with(14).and_return([5].pack('N') + 'helloworld')
|
88
|
+
@sphinx.send(:GetResponse, @sock, 1).should == 'world'
|
89
|
+
@sphinx.GetLastWarning.should == 'hello'
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should raise exception when SEARCHD_ERROR received' do
|
93
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_ERROR, 1, 9].pack('n2N'))
|
94
|
+
@sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
|
95
|
+
lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxInternalError)
|
96
|
+
@sphinx.GetLastError.should == 'searchd error: hello'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should raise exception when SEARCHD_RETRY received' do
|
100
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_RETRY, 1, 9].pack('n2N'))
|
101
|
+
@sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
|
102
|
+
lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxTemporaryError)
|
103
|
+
@sphinx.GetLastError.should == 'temporary searchd error: hello'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should raise exception when unknown status received' do
|
107
|
+
@sock.should_receive(:recv).with(8).and_return([65535, 1, 9].pack('n2N'))
|
108
|
+
@sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
|
109
|
+
lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxUnknownError)
|
110
|
+
@sphinx.GetLastError.should == 'unknown status code: \'65535\''
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should set warning when server is older than client' do
|
114
|
+
@sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 9].pack('n2N'))
|
115
|
+
@sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
|
116
|
+
@sphinx.send(:GetResponse, @sock, 5)
|
117
|
+
@sphinx.GetLastWarning.should == 'searchd command v.0.1 older than client\'s v.0.5, some options might not work'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'The Query method of Sphinx::Client' do
|
122
|
+
include SphinxFixtureHelper
|
123
|
+
include SphinxApiCall
|
124
|
+
|
125
|
+
before(:each) do
|
126
|
+
@sphinx = create_sphinx
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should generate valid request with default parameters' do
|
130
|
+
expected = sphinx_fixture('default_search')
|
131
|
+
@sock.should_receive(:send).with(expected, 0)
|
132
|
+
@sphinx.Query('query') rescue nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should generate valid request with default parameters and index' do
|
136
|
+
expected = sphinx_fixture('default_search_index')
|
137
|
+
@sock.should_receive(:send).with(expected, 0)
|
138
|
+
@sphinx.Query('query', 'index') rescue nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should generate valid request with limits' do
|
142
|
+
expected = sphinx_fixture('limits')
|
143
|
+
@sock.should_receive(:send).with(expected, 0)
|
144
|
+
@sphinx.SetLimits(10, 20)
|
145
|
+
@sphinx.Query('query') rescue nil?
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should generate valid request with limits and max number to retrieve' do
|
149
|
+
expected = sphinx_fixture('limits_max')
|
150
|
+
@sock.should_receive(:send).with(expected, 0)
|
151
|
+
@sphinx.SetLimits(10, 20, 30)
|
152
|
+
@sphinx.Query('query') rescue nil?
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should generate valid request with limits and cutoff to retrieve' do
|
156
|
+
expected = sphinx_fixture('limits_cutoff')
|
157
|
+
@sock.should_receive(:send).with(expected, 0)
|
158
|
+
@sphinx.SetLimits(10, 20, 30, 40)
|
159
|
+
@sphinx.Query('query') rescue nil?
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should generate valid request with max query time specified' do
|
163
|
+
expected = sphinx_fixture('max_query_time')
|
164
|
+
@sock.should_receive(:send).with(expected, 0)
|
165
|
+
@sphinx.SetMaxQueryTime(1000)
|
166
|
+
@sphinx.Query('query') rescue nil?
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'with match' do
|
170
|
+
[ :all, :any, :phrase, :boolean, :extended, :fullscan, :extended2 ].each do |match|
|
171
|
+
it "should generate valid request for SPH_MATCH_#{match.to_s.upcase}" do
|
172
|
+
expected = sphinx_fixture("match_#{match}")
|
173
|
+
@sock.should_receive(:send).with(expected, 0)
|
174
|
+
@sphinx.SetMatchMode(Sphinx::Client::const_get("SPH_MATCH_#{match.to_s.upcase}"))
|
175
|
+
@sphinx.Query('query') rescue nil?
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe 'with rank' do
|
181
|
+
[ :proximity_bm25, :bm25, :none, :wordcount, :proximity, :matchany, :fieldmask, :sph04 ].each do |rank|
|
182
|
+
it "should generate valid request for SPH_RANK_#{rank.to_s.upcase}" do
|
183
|
+
expected = sphinx_fixture("ranking_#{rank}")
|
184
|
+
@sock.should_receive(:send).with(expected, 0)
|
185
|
+
@sphinx.SetRankingMode(Sphinx::Client.const_get("SPH_RANK_#{rank.to_s.upcase}"))
|
186
|
+
@sphinx.Query('query') rescue nil?
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe 'with sorting' do
|
192
|
+
[ :attr_desc, :relevance, :attr_asc, :time_segments, :extended, :expr ].each do |mode|
|
193
|
+
it "should generate valid request for SPH_SORT_#{mode.to_s.upcase}" do
|
194
|
+
expected = sphinx_fixture("sort_#{mode}")
|
195
|
+
@sock.should_receive(:send).with(expected, 0)
|
196
|
+
@sphinx.SetSortMode(Sphinx::Client.const_get("SPH_SORT_#{mode.to_s.upcase}"), mode == :relevance ? '' : 'sortby')
|
197
|
+
@sphinx.Query('query') rescue nil?
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'should generate valid request with weights' do
|
203
|
+
expected = sphinx_fixture('weights')
|
204
|
+
@sock.should_receive(:send).with(expected, 0)
|
205
|
+
@sphinx.SetWeights([10, 20, 30, 40])
|
206
|
+
@sphinx.Query('query') rescue nil?
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should generate valid request with field weights' do
|
210
|
+
expected = sphinx_fixture('field_weights')
|
211
|
+
@sock.should_receive(:send).with(expected, 0)
|
212
|
+
@sphinx.SetFieldWeights({'field1' => 10, 'field2' => 20})
|
213
|
+
@sphinx.Query('query') rescue nil?
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should generate valid request with index weights' do
|
217
|
+
expected = sphinx_fixture('index_weights')
|
218
|
+
@sock.should_receive(:send).with(expected, 0)
|
219
|
+
@sphinx.SetIndexWeights({'index1' => 10, 'index2' => 20})
|
220
|
+
@sphinx.Query('query') rescue nil?
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should generate valid request with ID range' do
|
224
|
+
expected = sphinx_fixture('id_range')
|
225
|
+
@sock.should_receive(:send).with(expected, 0)
|
226
|
+
@sphinx.SetIDRange(10, 20)
|
227
|
+
@sphinx.Query('query') rescue nil?
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should generate valid request with ID range and 64-bit ints' do
|
231
|
+
expected = sphinx_fixture('id_range64')
|
232
|
+
@sock.should_receive(:send).with(expected, 0)
|
233
|
+
@sphinx.SetIDRange(8589934591, 17179869183)
|
234
|
+
@sphinx.Query('query') rescue nil?
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should generate valid request with values filter' do
|
238
|
+
expected = sphinx_fixture('filter')
|
239
|
+
@sock.should_receive(:send).with(expected, 0)
|
240
|
+
@sphinx.SetFilter('attr', [10, 20, 30])
|
241
|
+
@sphinx.Query('query') rescue nil?
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'should generate valid request with two values filters' do
|
245
|
+
expected = sphinx_fixture('filters')
|
246
|
+
@sock.should_receive(:send).with(expected, 0)
|
247
|
+
@sphinx.SetFilter('attr2', [40, 50])
|
248
|
+
@sphinx.SetFilter('attr1', [10, 20, 30])
|
249
|
+
@sphinx.Query('query') rescue nil?
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should generate valid request with values filter excluded' do
|
253
|
+
expected = sphinx_fixture('filter_exclude')
|
254
|
+
@sock.should_receive(:send).with(expected, 0)
|
255
|
+
@sphinx.SetFilter('attr', [10, 20, 30], true)
|
256
|
+
@sphinx.Query('query') rescue nil?
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should generate valid request with values filter range' do
|
260
|
+
expected = sphinx_fixture('filter_range')
|
261
|
+
@sock.should_receive(:send).with(expected, 0)
|
262
|
+
@sphinx.SetFilterRange('attr', 10, 20)
|
263
|
+
@sphinx.Query('query') rescue nil?
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should generate valid request with two filter ranges' do
|
267
|
+
expected = sphinx_fixture('filter_ranges')
|
268
|
+
@sock.should_receive(:send).with(expected, 0)
|
269
|
+
@sphinx.SetFilterRange('attr2', 30, 40)
|
270
|
+
@sphinx.SetFilterRange('attr1', 10, 20)
|
271
|
+
@sphinx.Query('query') rescue nil?
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'should generate valid request with filter range excluded' do
|
275
|
+
expected = sphinx_fixture('filter_range_exclude')
|
276
|
+
@sock.should_receive(:send).with(expected, 0)
|
277
|
+
@sphinx.SetFilterRange('attr', 10, 20, true)
|
278
|
+
@sphinx.Query('query') rescue nil?
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'should generate valid request with signed int64-based filter range' do
|
282
|
+
expected = sphinx_fixture('filter_range_int64')
|
283
|
+
@sock.should_receive(:send).with(expected, 0)
|
284
|
+
@sphinx.SetFilterRange('attr1', -10, 20)
|
285
|
+
@sphinx.SetFilterRange('attr2', -1099511627770, 1099511627780)
|
286
|
+
safe_call { @sphinx.Query('query') }
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should generate valid request with float filter range' do
|
290
|
+
expected = sphinx_fixture('filter_float_range')
|
291
|
+
@sock.should_receive(:send).with(expected, 0)
|
292
|
+
@sphinx.SetFilterFloatRange('attr', 10.5, 20.3)
|
293
|
+
@sphinx.Query('query') rescue nil?
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should generate valid request with float filter excluded' do
|
297
|
+
expected = sphinx_fixture('filter_float_range_exclude')
|
298
|
+
@sock.should_receive(:send).with(expected, 0)
|
299
|
+
@sphinx.SetFilterFloatRange('attr', 10.5, 20.3, true)
|
300
|
+
@sphinx.Query('query') rescue nil?
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'should generate valid request with different filters' do
|
304
|
+
expected = sphinx_fixture('filters_different')
|
305
|
+
@sock.should_receive(:send).with(expected, 0)
|
306
|
+
@sphinx.SetFilterRange('attr1', 10, 20, true)
|
307
|
+
@sphinx.SetFilter('attr3', [30, 40, 50])
|
308
|
+
@sphinx.SetFilterRange('attr1', 60, 70)
|
309
|
+
@sphinx.SetFilter('attr2', [80, 90, 100], true)
|
310
|
+
@sphinx.SetFilterFloatRange('attr1', 60.8, 70.5)
|
311
|
+
@sphinx.Query('query') rescue nil?
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'should generate valid request with geographical anchor point' do
|
315
|
+
expected = sphinx_fixture('geo_anchor')
|
316
|
+
@sock.should_receive(:send).with(expected, 0)
|
317
|
+
@sphinx.SetGeoAnchor('attrlat', 'attrlong', 20.3, 40.7)
|
318
|
+
@sphinx.Query('query') rescue nil?
|
319
|
+
end
|
320
|
+
|
321
|
+
describe 'with group by' do
|
322
|
+
[ :day, :week, :month, :year, :attr, :attrpair ].each do |groupby|
|
323
|
+
it "should generate valid request for SPH_GROUPBY_#{groupby.to_s.upcase}" do
|
324
|
+
expected = sphinx_fixture("group_by_#{groupby}")
|
325
|
+
@sock.should_receive(:send).with(expected, 0)
|
326
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::const_get("SPH_GROUPBY_#{groupby.to_s.upcase}"))
|
327
|
+
@sphinx.Query('query') rescue nil?
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'should generate valid request for SPH_GROUPBY_DAY with sort' do
|
332
|
+
expected = sphinx_fixture('group_by_day_sort')
|
333
|
+
@sock.should_receive(:send).with(expected, 0)
|
334
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY, 'somesort')
|
335
|
+
@sphinx.Query('query') rescue nil?
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'should generate valid request with count-distinct attribute' do
|
339
|
+
expected = sphinx_fixture('group_distinct')
|
340
|
+
@sock.should_receive(:send).with(expected, 0)
|
341
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
|
342
|
+
@sphinx.SetGroupDistinct('attr')
|
343
|
+
@sphinx.Query('query') rescue nil?
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'should generate valid request with retries count specified' do
|
348
|
+
expected = sphinx_fixture('retries')
|
349
|
+
@sock.should_receive(:send).with(expected, 0)
|
350
|
+
@sphinx.SetRetries(10)
|
351
|
+
@sphinx.Query('query') rescue nil?
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'should generate valid request with retries count and delay specified' do
|
355
|
+
expected = sphinx_fixture('retries_delay')
|
356
|
+
@sock.should_receive(:send).with(expected, 0)
|
357
|
+
@sphinx.SetRetries(10, 20)
|
358
|
+
@sphinx.Query('query') rescue nil?
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'should generate valid request for SetOverride' do
|
362
|
+
expected = sphinx_fixture('set_override')
|
363
|
+
@sock.should_receive(:send).with(expected, 0)
|
364
|
+
@sphinx.SetOverride('attr1', Sphinx::Client::SPH_ATTR_INTEGER, { 10 => 20 })
|
365
|
+
@sphinx.SetOverride('attr2', Sphinx::Client::SPH_ATTR_FLOAT, { 11 => 30.3 })
|
366
|
+
@sphinx.SetOverride('attr3', Sphinx::Client::SPH_ATTR_BIGINT, { 12 => 1099511627780 })
|
367
|
+
@sphinx.Query('query') rescue nil?
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should generate valid request for SetSelect' do
|
371
|
+
expected = sphinx_fixture('select')
|
372
|
+
@sock.should_receive(:send).with(expected, 0)
|
373
|
+
@sphinx.SetSelect('attr1, attr2')
|
374
|
+
@sphinx.Query('query') rescue nil?
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
describe 'The RunQueries method of Sphinx::Client' do
|
379
|
+
include SphinxFixtureHelper
|
380
|
+
|
381
|
+
before(:each) do
|
382
|
+
@sphinx = Sphinx::Client.new
|
383
|
+
@sock = mock('TCPSocket')
|
384
|
+
@sphinx.stub!(:Connect).and_return(@sock)
|
385
|
+
@sphinx.stub!(:GetResponse).and_raise(Sphinx::SphinxError)
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'should generate valid request for multiple queries' do
|
389
|
+
expected = sphinx_fixture('miltiple_queries')
|
390
|
+
@sock.should_receive(:send).with(expected, 0)
|
391
|
+
|
392
|
+
@sphinx.SetRetries(10, 20)
|
393
|
+
@sphinx.AddQuery('test1')
|
394
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
|
395
|
+
@sphinx.AddQuery('test2') rescue nil?
|
396
|
+
|
397
|
+
@sphinx.RunQueries rescue nil?
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
describe 'The BuildExcerpts method of Sphinx::Client' do
|
402
|
+
include SphinxFixtureHelper
|
403
|
+
|
404
|
+
before(:each) do
|
405
|
+
@sphinx = Sphinx::Client.new
|
406
|
+
@sock = mock('TCPSocket')
|
407
|
+
@sphinx.stub!(:Connect).and_return(@sock)
|
408
|
+
@sphinx.stub!(:GetResponse).and_raise(Sphinx::SphinxError)
|
409
|
+
end
|
410
|
+
|
411
|
+
it 'should generate valid request with default parameters' do
|
412
|
+
expected = sphinx_fixture('excerpt_default')
|
413
|
+
@sock.should_receive(:send).with(expected, 0)
|
414
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2') rescue nil?
|
415
|
+
end
|
416
|
+
|
417
|
+
it 'should generate valid request with custom parameters' do
|
418
|
+
expected = sphinx_fixture('excerpt_custom')
|
419
|
+
@sock.should_receive(:send).with(expected, 0)
|
420
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'before_match' => 'before',
|
421
|
+
'after_match' => 'after',
|
422
|
+
'chunk_separator' => 'separator',
|
423
|
+
'limit' => 10 }) rescue nil?
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'should generate valid request with flags' do
|
427
|
+
expected = sphinx_fixture('excerpt_flags')
|
428
|
+
@sock.should_receive(:send).with(expected, 0)
|
429
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'exact_phrase' => true,
|
430
|
+
'single_passage' => true,
|
431
|
+
'use_boundaries' => true,
|
432
|
+
'weight_order' => true,
|
433
|
+
'query_mode' => true }) rescue nil?
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe 'The BuildKeywords method of Sphinx::Client' do
|
438
|
+
include SphinxFixtureHelper
|
439
|
+
include SphinxApiCall
|
440
|
+
|
441
|
+
before(:each) do
|
442
|
+
@sphinx = create_sphinx
|
443
|
+
end
|
444
|
+
|
445
|
+
it 'should generate valid request' do
|
446
|
+
expected = sphinx_fixture('keywords')
|
447
|
+
@sock.should_receive(:send).with(expected, 0)
|
448
|
+
safe_call { @sphinx.BuildKeywords('test', 'index', true) }
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
describe 'The UpdateAttributes method of Sphinx::Client' do
|
453
|
+
include SphinxFixtureHelper
|
454
|
+
include SphinxApiCall
|
455
|
+
|
456
|
+
before(:each) do
|
457
|
+
@sphinx = create_sphinx
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'should generate valid request' do
|
461
|
+
expected = sphinx_fixture('update_attributes')
|
462
|
+
@sock.should_receive(:send).with(expected, 0)
|
463
|
+
safe_call { @sphinx.UpdateAttributes('index', ['group'], { 123 => [456] }) }
|
464
|
+
end
|
465
|
+
|
466
|
+
it 'should generate valid request for MVA' do
|
467
|
+
expected = sphinx_fixture('update_attributes_mva')
|
468
|
+
@sock.should_receive(:send).with(expected, 0)
|
469
|
+
safe_call { @sphinx.UpdateAttributes('index', ['group', 'category'], { 123 => [ [456, 789], [1, 2, 3] ] }, true) }
|
470
|
+
end
|
471
|
+
end
|