ninjudd-model_set 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +39 -0
  3. data/VERSION.yml +4 -0
  4. data/lib/model_set.rb +712 -0
  5. data/lib/model_set/conditioned.rb +33 -0
  6. data/lib/model_set/conditions.rb +103 -0
  7. data/lib/model_set/query.rb +128 -0
  8. data/lib/model_set/raw_query.rb +41 -0
  9. data/lib/model_set/raw_sql_query.rb +19 -0
  10. data/lib/model_set/set_query.rb +34 -0
  11. data/lib/model_set/solr_query.rb +70 -0
  12. data/lib/model_set/sphinx_query.rb +148 -0
  13. data/lib/model_set/sql_base_query.rb +52 -0
  14. data/lib/model_set/sql_query.rb +75 -0
  15. data/lib/multi_set.rb +67 -0
  16. data/test/model_set_test.rb +283 -0
  17. data/test/multi_set_test.rb +65 -0
  18. data/test/test_helper.rb +23 -0
  19. data/vendor/sphinx_client/README.rdoc +41 -0
  20. data/vendor/sphinx_client/Rakefile +21 -0
  21. data/vendor/sphinx_client/init.rb +1 -0
  22. data/vendor/sphinx_client/install.rb +5 -0
  23. data/vendor/sphinx_client/lib/sphinx.rb +6 -0
  24. data/vendor/sphinx_client/lib/sphinx/client.rb +1093 -0
  25. data/vendor/sphinx_client/lib/sphinx/request.rb +50 -0
  26. data/vendor/sphinx_client/lib/sphinx/response.rb +69 -0
  27. data/vendor/sphinx_client/spec/client_response_spec.rb +112 -0
  28. data/vendor/sphinx_client/spec/client_spec.rb +469 -0
  29. data/vendor/sphinx_client/spec/fixtures/default_search.php +8 -0
  30. data/vendor/sphinx_client/spec/fixtures/default_search_index.php +8 -0
  31. data/vendor/sphinx_client/spec/fixtures/excerpt_custom.php +11 -0
  32. data/vendor/sphinx_client/spec/fixtures/excerpt_default.php +8 -0
  33. data/vendor/sphinx_client/spec/fixtures/excerpt_flags.php +11 -0
  34. data/vendor/sphinx_client/spec/fixtures/field_weights.php +9 -0
  35. data/vendor/sphinx_client/spec/fixtures/filter.php +9 -0
  36. data/vendor/sphinx_client/spec/fixtures/filter_exclude.php +9 -0
  37. data/vendor/sphinx_client/spec/fixtures/filter_float_range.php +9 -0
  38. data/vendor/sphinx_client/spec/fixtures/filter_float_range_exclude.php +9 -0
  39. data/vendor/sphinx_client/spec/fixtures/filter_range.php +9 -0
  40. data/vendor/sphinx_client/spec/fixtures/filter_range_exclude.php +9 -0
  41. data/vendor/sphinx_client/spec/fixtures/filter_range_int64.php +10 -0
  42. data/vendor/sphinx_client/spec/fixtures/filter_ranges.php +10 -0
  43. data/vendor/sphinx_client/spec/fixtures/filters.php +10 -0
  44. data/vendor/sphinx_client/spec/fixtures/filters_different.php +13 -0
  45. data/vendor/sphinx_client/spec/fixtures/geo_anchor.php +9 -0
  46. data/vendor/sphinx_client/spec/fixtures/group_by_attr.php +9 -0
  47. data/vendor/sphinx_client/spec/fixtures/group_by_attrpair.php +9 -0
  48. data/vendor/sphinx_client/spec/fixtures/group_by_day.php +9 -0
  49. data/vendor/sphinx_client/spec/fixtures/group_by_day_sort.php +9 -0
  50. data/vendor/sphinx_client/spec/fixtures/group_by_month.php +9 -0
  51. data/vendor/sphinx_client/spec/fixtures/group_by_week.php +9 -0
  52. data/vendor/sphinx_client/spec/fixtures/group_by_year.php +9 -0
  53. data/vendor/sphinx_client/spec/fixtures/group_distinct.php +10 -0
  54. data/vendor/sphinx_client/spec/fixtures/id_range.php +9 -0
  55. data/vendor/sphinx_client/spec/fixtures/id_range64.php +9 -0
  56. data/vendor/sphinx_client/spec/fixtures/index_weights.php +9 -0
  57. data/vendor/sphinx_client/spec/fixtures/keywords.php +8 -0
  58. data/vendor/sphinx_client/spec/fixtures/limits.php +9 -0
  59. data/vendor/sphinx_client/spec/fixtures/limits_cutoff.php +9 -0
  60. data/vendor/sphinx_client/spec/fixtures/limits_max.php +9 -0
  61. data/vendor/sphinx_client/spec/fixtures/limits_max_cutoff.php +9 -0
  62. data/vendor/sphinx_client/spec/fixtures/match_all.php +9 -0
  63. data/vendor/sphinx_client/spec/fixtures/match_any.php +9 -0
  64. data/vendor/sphinx_client/spec/fixtures/match_boolean.php +9 -0
  65. data/vendor/sphinx_client/spec/fixtures/match_extended.php +9 -0
  66. data/vendor/sphinx_client/spec/fixtures/match_extended2.php +9 -0
  67. data/vendor/sphinx_client/spec/fixtures/match_fullscan.php +9 -0
  68. data/vendor/sphinx_client/spec/fixtures/match_phrase.php +9 -0
  69. data/vendor/sphinx_client/spec/fixtures/max_query_time.php +9 -0
  70. data/vendor/sphinx_client/spec/fixtures/miltiple_queries.php +12 -0
  71. data/vendor/sphinx_client/spec/fixtures/ranking_bm25.php +9 -0
  72. data/vendor/sphinx_client/spec/fixtures/ranking_none.php +9 -0
  73. data/vendor/sphinx_client/spec/fixtures/ranking_proximity.php +9 -0
  74. data/vendor/sphinx_client/spec/fixtures/ranking_proximity_bm25.php +9 -0
  75. data/vendor/sphinx_client/spec/fixtures/ranking_wordcount.php +9 -0
  76. data/vendor/sphinx_client/spec/fixtures/retries.php +9 -0
  77. data/vendor/sphinx_client/spec/fixtures/retries_delay.php +9 -0
  78. data/vendor/sphinx_client/spec/fixtures/select.php +9 -0
  79. data/vendor/sphinx_client/spec/fixtures/set_override.php +11 -0
  80. data/vendor/sphinx_client/spec/fixtures/sort_attr_asc.php +9 -0
  81. data/vendor/sphinx_client/spec/fixtures/sort_attr_desc.php +9 -0
  82. data/vendor/sphinx_client/spec/fixtures/sort_expr.php +9 -0
  83. data/vendor/sphinx_client/spec/fixtures/sort_extended.php +9 -0
  84. data/vendor/sphinx_client/spec/fixtures/sort_relevance.php +9 -0
  85. data/vendor/sphinx_client/spec/fixtures/sort_time_segments.php +9 -0
  86. data/vendor/sphinx_client/spec/fixtures/sphinxapi.php +1269 -0
  87. data/vendor/sphinx_client/spec/fixtures/update_attributes.php +8 -0
  88. data/vendor/sphinx_client/spec/fixtures/update_attributes_mva.php +8 -0
  89. data/vendor/sphinx_client/spec/fixtures/weights.php +9 -0
  90. data/vendor/sphinx_client/spec/sphinx/sphinx-id64.conf +67 -0
  91. data/vendor/sphinx_client/spec/sphinx/sphinx.conf +67 -0
  92. data/vendor/sphinx_client/spec/sphinx/sphinx_test.sql +86 -0
  93. data/vendor/sphinx_client/sphinx.yml.tpl +3 -0
  94. data/vendor/sphinx_client/tasks/sphinx.rake +75 -0
  95. metadata +154 -0
@@ -0,0 +1,50 @@
1
+ module Sphinx
2
+ # Pack ints, floats, strings, and arrays to internal representation
3
+ # needed by Sphinx search engine.
4
+ class Request
5
+ # Initialize new request.
6
+ def initialize
7
+ @request = ''
8
+ end
9
+
10
+ # Put int(s) to request.
11
+ def put_int(*ints)
12
+ ints.each { |i| @request << [i].pack('N') }
13
+ end
14
+
15
+ # Put 64-bit int(s) to request.
16
+ def put_int64(*ints)
17
+ ints.each { |i| @request << [i].pack('q').reverse }#[i >> 32, i & ((1 << 32) - 1)].pack('NN') }
18
+ end
19
+
20
+ # Put string(s) to request (first length, then the string itself).
21
+ def put_string(*strings)
22
+ strings.each { |s| @request << [s.length].pack('N') + s }
23
+ end
24
+
25
+ # Put float(s) to request.
26
+ def put_float(*floats)
27
+ floats.each do |f|
28
+ t1 = [f].pack('f') # machine order
29
+ t2 = t1.unpack('L*').first # int in machine order
30
+ @request << [t2].pack('N')
31
+ end
32
+ end
33
+
34
+ # Put array of ints to request (first length, then the array itself)
35
+ def put_int_array(arr)
36
+ put_int arr.length, *arr
37
+ end
38
+
39
+ # Put array of 64-bit ints to request (first length, then the array itself)
40
+ def put_int64_array(arr)
41
+ put_int arr.length
42
+ put_int64(*arr)
43
+ end
44
+
45
+ # Returns the entire message
46
+ def to_s
47
+ @request
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,69 @@
1
+ module Sphinx
2
+ # Unpack internal Sphinx representation of ints, floats, strings, and arrays.
3
+ # needed by Sphinx search engine.
4
+ class Response
5
+ # Initialize new request.
6
+ def initialize(response)
7
+ @response = response
8
+ @position = 0
9
+ @size = response.length
10
+ end
11
+
12
+ # Gets current stream position.
13
+ def position
14
+ @position
15
+ end
16
+
17
+ # Gets response size.
18
+ def size
19
+ @size
20
+ end
21
+
22
+ # Returns <tt>true</tt> when response stream is out.
23
+ def eof?
24
+ @position >= @size
25
+ end
26
+
27
+ # Get int from stream.
28
+ def get_int
29
+ raise EOFError if @position + 4 > @size
30
+ value = @response[@position, 4].unpack('N*').first
31
+ @position += 4
32
+ return value
33
+ end
34
+
35
+ # Get 64-bit int from stream.
36
+ def get_int64
37
+ raise EOFError if @position + 8 > @size
38
+ hi, lo = @response[@position, 8].unpack('N*N*')
39
+ @position += 8
40
+ return (hi << 32) + lo
41
+ end
42
+
43
+ # Get array of <tt>count</tt> ints from stream.
44
+ def get_ints(count)
45
+ length = 4 * count
46
+ raise EOFError if @position + length > @size
47
+ values = @response[@position, length].unpack('N*' * count)
48
+ @position += length
49
+ return values
50
+ end
51
+
52
+ # Get string from stream.
53
+ def get_string
54
+ length = get_int
55
+ raise EOFError if @position + length > @size
56
+ value = length > 0 ? @response[@position, length] : ''
57
+ @position += length
58
+ return value
59
+ end
60
+
61
+ # Get float from stream.
62
+ def get_float
63
+ raise EOFError if @position + 4 > @size
64
+ uval = @response[@position, 4].unpack('N*').first;
65
+ @position += 4
66
+ return ([uval].pack('L')).unpack('f*').first
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,112 @@
1
+ require File.dirname(__FILE__) + '/../init'
2
+
3
+ # To execute these tests you need to execute sphinx_test.sql and configure sphinx using sphinx.conf
4
+ # (both files are placed under sphinx directory)
5
+ context 'The SphinxApi connected to Sphinx' do
6
+ setup do
7
+ @sphinx = Sphinx::Client.new
8
+ end
9
+
10
+ specify 'should parse response in Query method' do
11
+ result = @sphinx.Query('wifi', 'test1')
12
+ validate_results_wifi(result)
13
+ end
14
+
15
+ specify 'should process 64-bit keys in Query method' do
16
+ result = @sphinx.Query('wifi', 'test2')
17
+ result['total_found'].should == 3
18
+ result['matches'].length.should == 3
19
+ result['matches'][0]['id'].should == 4294967298
20
+ result['matches'][1]['id'].should == 4294967299
21
+ result['matches'][2]['id'].should == 4294967297
22
+ end
23
+
24
+ specify 'should parse batch-query responce in RunQueries method' do
25
+ @sphinx.AddQuery('wifi', 'test1')
26
+ @sphinx.AddQuery('gprs', 'test1')
27
+ results = @sphinx.RunQueries
28
+ results.should be_an_instance_of(Array)
29
+ results.length.should == 2
30
+ validate_results_wifi(results[0])
31
+ end
32
+
33
+ specify 'should parse response in BuildExcerpts method' do
34
+ result = @sphinx.BuildExcerpts(['what the world', 'London is the capital of Great Britain'], 'test1', 'the')
35
+ result.should == ['what <b>the</b> world', 'London is <b>the</b> capital of Great Britain']
36
+ end
37
+
38
+ specify 'should parse response in BuildKeywords method' do
39
+ result = @sphinx.BuildKeywords('wifi gprs', 'test1', true)
40
+ result.should == [
41
+ { 'normalized' => 'wifi', 'tokenized' => 'wifi', 'hits' => 6, 'docs' => 3 },
42
+ { 'normalized' => 'gprs', 'tokenized' => 'gprs', 'hits' => 1, 'docs' => 1 }
43
+ ]
44
+ end
45
+
46
+ specify 'should parse response in UpdateAttributes method' do
47
+ @sphinx.UpdateAttributes('test1', ['group_id'], { 2 => [1] }).should == 1
48
+ result = @sphinx.Query('wifi', 'test1')
49
+ result['matches'][0]['attrs']['group_id'].should == 1
50
+ @sphinx.UpdateAttributes('test1', ['group_id'], { 2 => [2] }).should == 1
51
+ result = @sphinx.Query('wifi', 'test1')
52
+ result['matches'][0]['attrs']['group_id'].should == 2
53
+ end
54
+
55
+ specify 'should parse response in UpdateAttributes method with MVA' do
56
+ @sphinx.UpdateAttributes('test1', ['tags'], { 2 => [[1, 2, 3, 4, 5, 6, 7, 8, 9]] }, true).should == 1
57
+ result = @sphinx.Query('wifi', 'test1')
58
+ result['matches'][0]['attrs']['tags'].should == [1, 2, 3, 4, 5, 6, 7, 8, 9]
59
+ @sphinx.UpdateAttributes('test1', ['tags'], { 2 => [[5, 6, 7, 8]] }, true).should == 1
60
+ result = @sphinx.Query('wifi', 'test1')
61
+ result['matches'][0]['attrs']['tags'].should == [5, 6, 7, 8]
62
+ end
63
+
64
+ specify 'should process errors in Query method' do
65
+ @sphinx.Query('wifi', 'fakeindex').should be_false
66
+ @sphinx.GetLastError.length.should_not == 0
67
+ end
68
+
69
+ specify 'should process errors in RunQueries method' do
70
+ @sphinx.AddQuery('wifi', 'fakeindex')
71
+ r = @sphinx.RunQueries
72
+ r[0]['error'].length.should_not == 0
73
+ end
74
+
75
+ def validate_results_wifi(result)
76
+ result['total_found'].should == 3
77
+ result['matches'].length.should == 3
78
+ result['time'].should_not be_nil
79
+ result['attrs'].should == {
80
+ 'group_id' => Sphinx::Client::SPH_ATTR_INTEGER,
81
+ 'created_at' => Sphinx::Client::SPH_ATTR_TIMESTAMP,
82
+ 'rating' => Sphinx::Client::SPH_ATTR_FLOAT,
83
+ 'tags' => Sphinx::Client::SPH_ATTR_MULTI | Sphinx::Client::SPH_ATTR_INTEGER
84
+ }
85
+ result['fields'].should == [ 'name', 'description' ]
86
+ result['total'].should == 3
87
+ result['matches'].should be_an_instance_of(Array)
88
+
89
+ result['matches'][0]['id'].should == 2
90
+ result['matches'][0]['weight'].should == 2
91
+ result['matches'][0]['attrs']['group_id'].should == 2
92
+ result['matches'][0]['attrs']['created_at'].should == 1175658555
93
+ result['matches'][0]['attrs']['tags'].should == [5, 6, 7, 8]
94
+ ('%0.2f' % result['matches'][0]['attrs']['rating']).should == '54.85'
95
+
96
+ result['matches'][1]['id'].should == 3
97
+ result['matches'][1]['weight'].should == 2
98
+ result['matches'][1]['attrs']['group_id'].should == 1
99
+ result['matches'][1]['attrs']['created_at'].should == 1175658647
100
+ result['matches'][1]['attrs']['tags'].should == [1, 7, 9, 10]
101
+ ('%0.2f' % result['matches'][1]['attrs']['rating']).should == '16.25'
102
+
103
+ result['matches'][2]['id'].should == 1
104
+ result['matches'][2]['weight'].should == 1
105
+ result['matches'][2]['attrs']['group_id'].should == 1
106
+ result['matches'][2]['attrs']['created_at'].should == 1175658490
107
+ result['matches'][2]['attrs']['tags'].should == [1, 2, 3, 4]
108
+ ('%0.2f' % result['matches'][2]['attrs']['rating']).should == '13.32'
109
+
110
+ result['words'].should == { 'wifi' => { 'hits' => 6, 'docs' => 3 } }
111
+ end
112
+ end
@@ -0,0 +1,469 @@
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(:recv).with(4).and_return([0].pack('N'))
42
+ @sock.should_receive(:close)
43
+ lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
44
+ @sphinx.GetLastError.should == 'expected searchd protocol version 1+, got version \'0\''
45
+ end
46
+
47
+ it 'should raise exception on connection error' do
48
+ TCPSocket.should_receive(:new).with('localhost', 3312).and_raise(Errno::EBADF)
49
+ lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
50
+ @sphinx.GetLastError.should == 'connection to localhost:3312 failed'
51
+ end
52
+
53
+ it 'should use custom host and port' do
54
+ @sphinx.SetServer('anotherhost', 55555)
55
+ TCPSocket.should_receive(:new).with('anotherhost', 55555).and_raise(Errno::EBADF)
56
+ lambda { @sphinx.send(:Connect) }.should raise_error(Sphinx::SphinxConnectError)
57
+ end
58
+ end
59
+
60
+ describe 'The GetResponse method of Sphinx::Client' do
61
+ before(:each) do
62
+ @sphinx = Sphinx::Client.new
63
+ @sock = mock('TCPSocket')
64
+ @sock.should_receive(:close)
65
+ end
66
+
67
+ it 'should receive response' do
68
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
69
+ @sock.should_receive(:recv).with(4).and_return([0].pack('N'))
70
+ @sphinx.send(:GetResponse, @sock, 1)
71
+ end
72
+
73
+ it 'should raise exception on zero-sized response' do
74
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 0].pack('n2N'))
75
+ lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxResponseError)
76
+ end
77
+
78
+ it 'should raise exception when response is incomplete' do
79
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
80
+ @sock.should_receive(:recv).with(4).and_raise(EOFError)
81
+ lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxResponseError)
82
+ end
83
+
84
+ it 'should set warning message when SEARCHD_WARNING received' do
85
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_WARNING, 1, 14].pack('n2N'))
86
+ @sock.should_receive(:recv).with(14).and_return([5].pack('N') + 'helloworld')
87
+ @sphinx.send(:GetResponse, @sock, 1).should == 'world'
88
+ @sphinx.GetLastWarning.should == 'hello'
89
+ end
90
+
91
+ it 'should raise exception when SEARCHD_ERROR received' do
92
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_ERROR, 1, 9].pack('n2N'))
93
+ @sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
94
+ lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxInternalError)
95
+ @sphinx.GetLastError.should == 'searchd error: hello'
96
+ end
97
+
98
+ it 'should raise exception when SEARCHD_RETRY received' do
99
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_RETRY, 1, 9].pack('n2N'))
100
+ @sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
101
+ lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxTemporaryError)
102
+ @sphinx.GetLastError.should == 'temporary searchd error: hello'
103
+ end
104
+
105
+ it 'should raise exception when unknown status received' do
106
+ @sock.should_receive(:recv).with(8).and_return([65535, 1, 9].pack('n2N'))
107
+ @sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
108
+ lambda { @sphinx.send(:GetResponse, @sock, 1) }.should raise_error(Sphinx::SphinxUnknownError)
109
+ @sphinx.GetLastError.should == 'unknown status code: \'65535\''
110
+ end
111
+
112
+ it 'should set warning when server is older than client' do
113
+ @sock.should_receive(:recv).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 9].pack('n2N'))
114
+ @sock.should_receive(:recv).with(9).and_return([1].pack('N') + 'hello')
115
+ @sphinx.send(:GetResponse, @sock, 5)
116
+ @sphinx.GetLastWarning.should == 'searchd command v.0.1 older than client\'s v.0.5, some options might not work'
117
+ end
118
+ end
119
+
120
+ describe 'The Query method of Sphinx::Client' do
121
+ include SphinxFixtureHelper
122
+ include SphinxApiCall
123
+
124
+ before(:each) do
125
+ @sphinx = create_sphinx
126
+ end
127
+
128
+ it 'should generate valid request with default parameters' do
129
+ expected = sphinx_fixture('default_search')
130
+ @sock.should_receive(:send).with(expected, 0)
131
+ @sphinx.Query('query') rescue nil?
132
+ end
133
+
134
+ it 'should generate valid request with default parameters and index' do
135
+ expected = sphinx_fixture('default_search_index')
136
+ @sock.should_receive(:send).with(expected, 0)
137
+ @sphinx.Query('query', 'index') rescue nil?
138
+ end
139
+
140
+ it 'should generate valid request with limits' do
141
+ expected = sphinx_fixture('limits')
142
+ @sock.should_receive(:send).with(expected, 0)
143
+ @sphinx.SetLimits(10, 20)
144
+ @sphinx.Query('query') rescue nil?
145
+ end
146
+
147
+ it 'should generate valid request with limits and max number to retrieve' do
148
+ expected = sphinx_fixture('limits_max')
149
+ @sock.should_receive(:send).with(expected, 0)
150
+ @sphinx.SetLimits(10, 20, 30)
151
+ @sphinx.Query('query') rescue nil?
152
+ end
153
+
154
+ it 'should generate valid request with limits and cutoff to retrieve' do
155
+ expected = sphinx_fixture('limits_cutoff')
156
+ @sock.should_receive(:send).with(expected, 0)
157
+ @sphinx.SetLimits(10, 20, 30, 40)
158
+ @sphinx.Query('query') rescue nil?
159
+ end
160
+
161
+ it 'should generate valid request with max query time specified' do
162
+ expected = sphinx_fixture('max_query_time')
163
+ @sock.should_receive(:send).with(expected, 0)
164
+ @sphinx.SetMaxQueryTime(1000)
165
+ @sphinx.Query('query') rescue nil?
166
+ end
167
+
168
+ describe 'with match' do
169
+ [ :all, :any, :phrase, :boolean, :extended, :fullscan, :extended2 ].each do |match|
170
+ it "should generate valid request for SPH_MATCH_#{match.to_s.upcase}" do
171
+ expected = sphinx_fixture("match_#{match}")
172
+ @sock.should_receive(:send).with(expected, 0)
173
+ @sphinx.SetMatchMode(Sphinx::Client::const_get("SPH_MATCH_#{match.to_s.upcase}"))
174
+ @sphinx.Query('query') rescue nil?
175
+ end
176
+ end
177
+ end
178
+
179
+ describe 'with rank' do
180
+ [ :proximity_bm25, :bm25, :none, :wordcount, :proximity ].each do |rank|
181
+ it "should generate valid request for SPH_RANK_#{rank.to_s.upcase}" do
182
+ expected = sphinx_fixture("ranking_#{rank}")
183
+ @sock.should_receive(:send).with(expected, 0)
184
+ @sphinx.SetRankingMode(Sphinx::Client.const_get("SPH_RANK_#{rank.to_s.upcase}"))
185
+ @sphinx.Query('query') rescue nil?
186
+ end
187
+ end
188
+ end
189
+
190
+ describe 'with sorting' do
191
+ [ :attr_desc, :relevance, :attr_asc, :time_segments, :extended, :expr ].each do |mode|
192
+ it "should generate valid request for SPH_SORT_#{mode.to_s.upcase}" do
193
+ expected = sphinx_fixture("sort_#{mode}")
194
+ @sock.should_receive(:send).with(expected, 0)
195
+ @sphinx.SetSortMode(Sphinx::Client.const_get("SPH_SORT_#{mode.to_s.upcase}"), mode == :relevance ? '' : 'sortby')
196
+ @sphinx.Query('query') rescue nil?
197
+ end
198
+ end
199
+ end
200
+
201
+ it 'should generate valid request with weights' do
202
+ expected = sphinx_fixture('weights')
203
+ @sock.should_receive(:send).with(expected, 0)
204
+ @sphinx.SetWeights([10, 20, 30, 40])
205
+ @sphinx.Query('query') rescue nil?
206
+ end
207
+
208
+ it 'should generate valid request with field weights' do
209
+ expected = sphinx_fixture('field_weights')
210
+ @sock.should_receive(:send).with(expected, 0)
211
+ @sphinx.SetFieldWeights({'field1' => 10, 'field2' => 20})
212
+ @sphinx.Query('query') rescue nil?
213
+ end
214
+
215
+ it 'should generate valid request with index weights' do
216
+ expected = sphinx_fixture('index_weights')
217
+ @sock.should_receive(:send).with(expected, 0)
218
+ @sphinx.SetIndexWeights({'index1' => 10, 'index2' => 20})
219
+ @sphinx.Query('query') rescue nil?
220
+ end
221
+
222
+ it 'should generate valid request with ID range' do
223
+ expected = sphinx_fixture('id_range')
224
+ @sock.should_receive(:send).with(expected, 0)
225
+ @sphinx.SetIDRange(10, 20)
226
+ @sphinx.Query('query') rescue nil?
227
+ end
228
+
229
+ it 'should generate valid request with ID range and 64-bit ints' do
230
+ expected = sphinx_fixture('id_range64')
231
+ @sock.should_receive(:send).with(expected, 0)
232
+ @sphinx.SetIDRange(8589934591, 17179869183)
233
+ @sphinx.Query('query') rescue nil?
234
+ end
235
+
236
+ it 'should generate valid request with values filter' do
237
+ expected = sphinx_fixture('filter')
238
+ @sock.should_receive(:send).with(expected, 0)
239
+ @sphinx.SetFilter('attr', [10, 20, 30])
240
+ @sphinx.Query('query') rescue nil?
241
+ end
242
+
243
+ it 'should generate valid request with two values filters' do
244
+ expected = sphinx_fixture('filters')
245
+ @sock.should_receive(:send).with(expected, 0)
246
+ @sphinx.SetFilter('attr2', [40, 50])
247
+ @sphinx.SetFilter('attr1', [10, 20, 30])
248
+ @sphinx.Query('query') rescue nil?
249
+ end
250
+
251
+ it 'should generate valid request with values filter excluded' do
252
+ expected = sphinx_fixture('filter_exclude')
253
+ @sock.should_receive(:send).with(expected, 0)
254
+ @sphinx.SetFilter('attr', [10, 20, 30], true)
255
+ @sphinx.Query('query') rescue nil?
256
+ end
257
+
258
+ it 'should generate valid request with values filter range' do
259
+ expected = sphinx_fixture('filter_range')
260
+ @sock.should_receive(:send).with(expected, 0)
261
+ @sphinx.SetFilterRange('attr', 10, 20)
262
+ @sphinx.Query('query') rescue nil?
263
+ end
264
+
265
+ it 'should generate valid request with two filter ranges' do
266
+ expected = sphinx_fixture('filter_ranges')
267
+ @sock.should_receive(:send).with(expected, 0)
268
+ @sphinx.SetFilterRange('attr2', 30, 40)
269
+ @sphinx.SetFilterRange('attr1', 10, 20)
270
+ @sphinx.Query('query') rescue nil?
271
+ end
272
+
273
+ it 'should generate valid request with filter range excluded' do
274
+ expected = sphinx_fixture('filter_range_exclude')
275
+ @sock.should_receive(:send).with(expected, 0)
276
+ @sphinx.SetFilterRange('attr', 10, 20, true)
277
+ @sphinx.Query('query') rescue nil?
278
+ end
279
+
280
+ it 'should generate valid request with signed int64-based filter range' do
281
+ expected = sphinx_fixture('filter_range_int64')
282
+ @sock.should_receive(:send).with(expected, 0)
283
+ @sphinx.SetFilterRange('attr1', -10, 20)
284
+ @sphinx.SetFilterRange('attr2', -1099511627770, 1099511627780)
285
+ safe_call { @sphinx.Query('query') }
286
+ end
287
+
288
+ it 'should generate valid request with float filter range' do
289
+ expected = sphinx_fixture('filter_float_range')
290
+ @sock.should_receive(:send).with(expected, 0)
291
+ @sphinx.SetFilterFloatRange('attr', 10.5, 20.3)
292
+ @sphinx.Query('query') rescue nil?
293
+ end
294
+
295
+ it 'should generate valid request with float filter excluded' do
296
+ expected = sphinx_fixture('filter_float_range_exclude')
297
+ @sock.should_receive(:send).with(expected, 0)
298
+ @sphinx.SetFilterFloatRange('attr', 10.5, 20.3, true)
299
+ @sphinx.Query('query') rescue nil?
300
+ end
301
+
302
+ it 'should generate valid request with different filters' do
303
+ expected = sphinx_fixture('filters_different')
304
+ @sock.should_receive(:send).with(expected, 0)
305
+ @sphinx.SetFilterRange('attr1', 10, 20, true)
306
+ @sphinx.SetFilter('attr3', [30, 40, 50])
307
+ @sphinx.SetFilterRange('attr1', 60, 70)
308
+ @sphinx.SetFilter('attr2', [80, 90, 100], true)
309
+ @sphinx.SetFilterFloatRange('attr1', 60.8, 70.5)
310
+ @sphinx.Query('query') rescue nil?
311
+ end
312
+
313
+ it 'should generate valid request with geographical anchor point' do
314
+ expected = sphinx_fixture('geo_anchor')
315
+ @sock.should_receive(:send).with(expected, 0)
316
+ @sphinx.SetGeoAnchor('attrlat', 'attrlong', 20.3, 40.7)
317
+ @sphinx.Query('query') rescue nil?
318
+ end
319
+
320
+ describe 'with group by' do
321
+ [ :day, :week, :month, :year, :attr, :attrpair ].each do |groupby|
322
+ it "should generate valid request for SPH_GROUPBY_#{groupby.to_s.upcase}" do
323
+ expected = sphinx_fixture("group_by_#{groupby}")
324
+ @sock.should_receive(:send).with(expected, 0)
325
+ @sphinx.SetGroupBy('attr', Sphinx::Client::const_get("SPH_GROUPBY_#{groupby.to_s.upcase}"))
326
+ @sphinx.Query('query') rescue nil?
327
+ end
328
+ end
329
+
330
+ it 'should generate valid request for SPH_GROUPBY_DAY with sort' do
331
+ expected = sphinx_fixture('group_by_day_sort')
332
+ @sock.should_receive(:send).with(expected, 0)
333
+ @sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY, 'somesort')
334
+ @sphinx.Query('query') rescue nil?
335
+ end
336
+
337
+ it 'should generate valid request with count-distinct attribute' do
338
+ expected = sphinx_fixture('group_distinct')
339
+ @sock.should_receive(:send).with(expected, 0)
340
+ @sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
341
+ @sphinx.SetGroupDistinct('attr')
342
+ @sphinx.Query('query') rescue nil?
343
+ end
344
+ end
345
+
346
+ it 'should generate valid request with retries count specified' do
347
+ expected = sphinx_fixture('retries')
348
+ @sock.should_receive(:send).with(expected, 0)
349
+ @sphinx.SetRetries(10)
350
+ @sphinx.Query('query') rescue nil?
351
+ end
352
+
353
+ it 'should generate valid request with retries count and delay specified' do
354
+ expected = sphinx_fixture('retries_delay')
355
+ @sock.should_receive(:send).with(expected, 0)
356
+ @sphinx.SetRetries(10, 20)
357
+ @sphinx.Query('query') rescue nil?
358
+ end
359
+
360
+ it 'should generate valid request for SetOverride' do
361
+ expected = sphinx_fixture('set_override')
362
+ @sock.should_receive(:send).with(expected, 0)
363
+ @sphinx.SetOverride('attr1', Sphinx::Client::SPH_ATTR_INTEGER, { 10 => 20 })
364
+ @sphinx.SetOverride('attr2', Sphinx::Client::SPH_ATTR_FLOAT, { 11 => 30.3 })
365
+ @sphinx.SetOverride('attr3', Sphinx::Client::SPH_ATTR_BIGINT, { 12 => 1099511627780 })
366
+ @sphinx.Query('query') rescue nil?
367
+ end
368
+
369
+ it 'should generate valid request for SetSelect' do
370
+ expected = sphinx_fixture('select')
371
+ @sock.should_receive(:send).with(expected, 0)
372
+ @sphinx.SetSelect('attr1, attr2')
373
+ @sphinx.Query('query') rescue nil?
374
+ end
375
+ end
376
+
377
+ describe 'The RunQueries method of Sphinx::Client' do
378
+ include SphinxFixtureHelper
379
+
380
+ before(:each) do
381
+ @sphinx = Sphinx::Client.new
382
+ @sock = mock('TCPSocket')
383
+ @sphinx.stub!(:Connect).and_return(@sock)
384
+ @sphinx.stub!(:GetResponse).and_raise(Sphinx::SphinxError)
385
+ end
386
+
387
+ it 'should generate valid request for multiple queries' do
388
+ expected = sphinx_fixture('miltiple_queries')
389
+ @sock.should_receive(:send).with(expected, 0)
390
+
391
+ @sphinx.SetRetries(10, 20)
392
+ @sphinx.AddQuery('test1')
393
+ @sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
394
+ @sphinx.AddQuery('test2') rescue nil?
395
+
396
+ @sphinx.RunQueries rescue nil?
397
+ end
398
+ end
399
+
400
+ describe 'The BuildExcerpts method of Sphinx::Client' do
401
+ include SphinxFixtureHelper
402
+
403
+ before(:each) do
404
+ @sphinx = Sphinx::Client.new
405
+ @sock = mock('TCPSocket')
406
+ @sphinx.stub!(:Connect).and_return(@sock)
407
+ @sphinx.stub!(:GetResponse).and_raise(Sphinx::SphinxError)
408
+ end
409
+
410
+ it 'should generate valid request with default parameters' do
411
+ expected = sphinx_fixture('excerpt_default')
412
+ @sock.should_receive(:send).with(expected, 0)
413
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2') rescue nil?
414
+ end
415
+
416
+ it 'should generate valid request with custom parameters' do
417
+ expected = sphinx_fixture('excerpt_custom')
418
+ @sock.should_receive(:send).with(expected, 0)
419
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'before_match' => 'before',
420
+ 'after_match' => 'after',
421
+ 'chunk_separator' => 'separator',
422
+ 'limit' => 10 }) rescue nil?
423
+ end
424
+
425
+ it 'should generate valid request with flags' do
426
+ expected = sphinx_fixture('excerpt_flags')
427
+ @sock.should_receive(:send).with(expected, 0)
428
+ @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'exact_phrase' => true,
429
+ 'single_passage' => true,
430
+ 'use_boundaries' => true,
431
+ 'weight_order' => true }) rescue nil?
432
+ end
433
+ end
434
+
435
+ describe 'The BuildKeywords method of Sphinx::Client' do
436
+ include SphinxFixtureHelper
437
+ include SphinxApiCall
438
+
439
+ before(:each) do
440
+ @sphinx = create_sphinx
441
+ end
442
+
443
+ it 'should generate valid request' do
444
+ expected = sphinx_fixture('keywords')
445
+ @sock.should_receive(:send).with(expected, 0)
446
+ safe_call { @sphinx.BuildKeywords('test', 'index', true) }
447
+ end
448
+ end
449
+
450
+ describe 'The UpdateAttributes method of Sphinx::Client' do
451
+ include SphinxFixtureHelper
452
+ include SphinxApiCall
453
+
454
+ before(:each) do
455
+ @sphinx = create_sphinx
456
+ end
457
+
458
+ it 'should generate valid request' do
459
+ expected = sphinx_fixture('update_attributes')
460
+ @sock.should_receive(:send).with(expected, 0)
461
+ safe_call { @sphinx.UpdateAttributes('index', ['group'], { 123 => [456] }) }
462
+ end
463
+
464
+ it 'should generate valid request for MVA' do
465
+ expected = sphinx_fixture('update_attributes_mva')
466
+ @sock.should_receive(:send).with(expected, 0)
467
+ safe_call { @sphinx.UpdateAttributes('index', ['group', 'category'], { 123 => [ [456, 789], [1, 2, 3] ] }, true) }
468
+ end
469
+ end