sphinx 0.9.10 → 0.9.10.2043
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 +2 -2
- data/Rakefile +1 -1
- data/VERSION.yml +1 -0
- data/lib/sphinx.rb +9 -3
- data/lib/sphinx/buffered_io.rb +22 -0
- data/lib/sphinx/client.rb +661 -259
- data/lib/sphinx/server.rb +167 -0
- data/lib/sphinx/timeout.rb +28 -0
- data/spec/client_response_spec.rb +29 -28
- data/spec/client_spec.rb +650 -471
- data/spec/client_validations_spec.rb +850 -0
- data/spec/spec_helper.rb +24 -0
- data/sphinx.gemspec +11 -7
- metadata +9 -5
- data/install.rb +0 -5
- data/sphinx.yml.tpl +0 -3
- data/tasks/sphinx.rake +0 -75
@@ -0,0 +1,167 @@
|
|
1
|
+
class Sphinx::Server
|
2
|
+
# The host the Sphinx server is running on
|
3
|
+
attr_reader :host
|
4
|
+
|
5
|
+
# The port the Sphinx server is listening on
|
6
|
+
attr_reader :port
|
7
|
+
|
8
|
+
# The path to UNIX socket where Sphinx server is running on
|
9
|
+
attr_reader :path
|
10
|
+
|
11
|
+
# Creates a new instance of +Server+.
|
12
|
+
#
|
13
|
+
# Parameters:
|
14
|
+
# * +sphinx+ -- an instance of <tt>Sphinx::Client</tt>.
|
15
|
+
# * +host+ -- name of host where search is running (if +path+ is not specified).
|
16
|
+
# * +port+ -- searchd port (if +path+ is not specified).
|
17
|
+
# * +path+ -- an absolute path to the UNIX socket.
|
18
|
+
#
|
19
|
+
def initialize(sphinx, host, port, path)
|
20
|
+
@sphinx = sphinx
|
21
|
+
@host = host
|
22
|
+
@port = port
|
23
|
+
@path = path
|
24
|
+
|
25
|
+
@timeout = @sphinx.timeout
|
26
|
+
@retries = @sphinx.retries
|
27
|
+
|
28
|
+
@reqtimeout = @sphinx.reqtimeout
|
29
|
+
@reqretries = @sphinx.reqretries
|
30
|
+
|
31
|
+
@socket = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# Gets the opened socket to the server.
|
35
|
+
#
|
36
|
+
# You can pass a block to make any connection establishing related things,
|
37
|
+
# like protocol version interchange. They will be treated as a part of
|
38
|
+
# connection routine, so connection timeout will include them.
|
39
|
+
#
|
40
|
+
# In case of connection error, +SphinxConnectError+ exception will be raised.
|
41
|
+
#
|
42
|
+
# Method returns opened socket, so do not forget to close it using +free_socket+
|
43
|
+
# method. Make sure you will close socket in case of any emergency.
|
44
|
+
#
|
45
|
+
def get_socket(&block)
|
46
|
+
if persistent?
|
47
|
+
yield @socket
|
48
|
+
@socket
|
49
|
+
else
|
50
|
+
socket = nil
|
51
|
+
Sphinx::safe_execute(@timeout) do
|
52
|
+
socket = establish_connection
|
53
|
+
|
54
|
+
# Do custom initialization
|
55
|
+
yield socket if block_given?
|
56
|
+
end
|
57
|
+
socket
|
58
|
+
end
|
59
|
+
rescue SocketError, SystemCallError, IOError, EOFError, ::Timeout::Error, ::Errno::EPIPE => e
|
60
|
+
# Close previously opened socket (in case of it has been really opened)
|
61
|
+
free_socket(socket, true)
|
62
|
+
|
63
|
+
location = @path || "#{@host}:#{@port}"
|
64
|
+
error = "connection to #{location} failed ("
|
65
|
+
if e.kind_of?(SystemCallError)
|
66
|
+
error << "errno=#{e.class::Errno}, "
|
67
|
+
end
|
68
|
+
error << "msg=#{e.message})"
|
69
|
+
raise SphinxConnectError, error
|
70
|
+
end
|
71
|
+
|
72
|
+
# Closes previously opened socket.
|
73
|
+
#
|
74
|
+
# Pass socket retrieved with +get_socket+ method when finished work. It does
|
75
|
+
# not close persistent sockets, but if really you need to do it, pass +true+
|
76
|
+
# as +force+ parameter value.
|
77
|
+
#
|
78
|
+
def free_socket(socket, force = false)
|
79
|
+
# Socket has not been open
|
80
|
+
return false if socket.nil?
|
81
|
+
|
82
|
+
# Do we try to close persistent socket?
|
83
|
+
if socket == @socket
|
84
|
+
# do not close it if not forced
|
85
|
+
if force
|
86
|
+
@socket.close unless @socket.closed?
|
87
|
+
@socket = nil
|
88
|
+
true
|
89
|
+
else
|
90
|
+
false
|
91
|
+
end
|
92
|
+
else
|
93
|
+
# Just close this socket
|
94
|
+
socket.close unless socket.closed?
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Makes specified socket persistent.
|
100
|
+
#
|
101
|
+
# Previous persistent socket will be closed as well.
|
102
|
+
def make_persistent!(socket)
|
103
|
+
free_socket(@socket)
|
104
|
+
@socket = socket
|
105
|
+
end
|
106
|
+
|
107
|
+
# Closes persistent socket.
|
108
|
+
def close_persistent!
|
109
|
+
if @socket
|
110
|
+
@socket.close unless @socket.closed?
|
111
|
+
@socket = nil
|
112
|
+
true
|
113
|
+
else
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Gets a value indicating whether server has persistent socket associated.
|
119
|
+
def persistent?
|
120
|
+
!@socket.nil?
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# This is internal method which establishes a connection to a configured server.
|
126
|
+
#
|
127
|
+
# Method configures various socket options (like TCP_NODELAY), and
|
128
|
+
# sets socket timeouts.
|
129
|
+
#
|
130
|
+
# It does not close socket on any failure, please do it from calling code!
|
131
|
+
#
|
132
|
+
def establish_connection
|
133
|
+
if @path
|
134
|
+
sock = UNIXSocket.new(@path)
|
135
|
+
else
|
136
|
+
sock = TCPSocket.new(@host, @port)
|
137
|
+
end
|
138
|
+
|
139
|
+
io = Sphinx::BufferedIO.new(sock)
|
140
|
+
io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
141
|
+
if @reqtimeout > 0
|
142
|
+
io.read_timeout = @reqtimeout
|
143
|
+
|
144
|
+
# This is a part of memcache-client library.
|
145
|
+
#
|
146
|
+
# Getting reports from several customers, including 37signals,
|
147
|
+
# that the non-blocking timeouts in 1.7.5 don't seem to be reliable.
|
148
|
+
# It can't hurt to set the underlying socket timeout also, if possible.
|
149
|
+
if timeout
|
150
|
+
secs = Integer(timeout)
|
151
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
152
|
+
optval = [secs, usecs].pack("l_2")
|
153
|
+
begin
|
154
|
+
io.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
155
|
+
io.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
156
|
+
rescue Exception => ex
|
157
|
+
# Solaris, for one, does not like/support socket timeouts.
|
158
|
+
@warning = "Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
else
|
162
|
+
io.read_timeout = false
|
163
|
+
end
|
164
|
+
|
165
|
+
io
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sphinx
|
2
|
+
begin
|
3
|
+
# Try to use the SystemTimer gem instead of Ruby's timeout library
|
4
|
+
# when running on something that looks like Ruby 1.8.x. See:
|
5
|
+
# http://ph7spot.com/articles/system_timer
|
6
|
+
# We don't want to bother trying to load SystemTimer on jruby and
|
7
|
+
# ruby 1.9+
|
8
|
+
if defined?(JRUBY_VERSION) || (RUBY_VERSION >= '1.9')
|
9
|
+
require 'timeout'
|
10
|
+
Timeout = ::Timeout
|
11
|
+
else
|
12
|
+
require 'system_timer'
|
13
|
+
Timeout = ::SystemTimer
|
14
|
+
end
|
15
|
+
rescue LoadError => e
|
16
|
+
puts "[sphinx] Could not load SystemTimer gem, falling back to Ruby's slower/unsafe timeout library: #{e.message}"
|
17
|
+
require 'timeout'
|
18
|
+
Timeout = ::Timeout
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.safe_execute(timeout = 5, &block)
|
22
|
+
if timeout > 0
|
23
|
+
Sphinx::Timeout.timeout(timeout, &block)
|
24
|
+
else
|
25
|
+
yield
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
2
|
|
3
3
|
# To execute these tests you need to execute sphinx_test.sql and configure sphinx using sphinx.conf
|
4
4
|
# (both files are placed under sphinx directory)
|
@@ -24,7 +24,7 @@ describe Sphinx::Client, 'connected' do
|
|
24
24
|
|
25
25
|
it 'should process errors in Query method' do
|
26
26
|
@sphinx.Query('wifi', 'fakeindex').should be_false
|
27
|
-
@sphinx.GetLastError.
|
27
|
+
@sphinx.GetLastError.should_not be_empty
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -34,14 +34,14 @@ describe Sphinx::Client, 'connected' do
|
|
34
34
|
@sphinx.AddQuery('gprs', 'test1')
|
35
35
|
results = @sphinx.RunQueries
|
36
36
|
results.should be_an_instance_of(Array)
|
37
|
-
results.
|
37
|
+
results.should have(2).items
|
38
38
|
validate_results_wifi(results[0])
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'should process errors in RunQueries method' do
|
42
42
|
@sphinx.AddQuery('wifi', 'fakeindex')
|
43
43
|
r = @sphinx.RunQueries
|
44
|
-
r[0]['error'].
|
44
|
+
r[0]['error'].should_not be_empty
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -62,25 +62,6 @@ describe Sphinx::Client, 'connected' do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
context 'in Open method' do
|
66
|
-
it 'should open socket' do
|
67
|
-
@sphinx.Open.should be_true
|
68
|
-
socket = @sphinx.instance_variable_get(:@socket)
|
69
|
-
socket.should be_kind_of(TCPSocket)
|
70
|
-
socket.close
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'should produce an error when opened twice' do
|
74
|
-
@sphinx.Open.should be_true
|
75
|
-
@sphinx.Open.should be_false
|
76
|
-
@sphinx.GetLastError.should == 'already connected'
|
77
|
-
|
78
|
-
socket = @sphinx.instance_variable_get(:@socket)
|
79
|
-
socket.should be_kind_of(TCPSocket)
|
80
|
-
socket.close
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
65
|
context 'in UpdateAttributes method' do
|
85
66
|
it 'should parse response' do
|
86
67
|
@sphinx.UpdateAttributes('test1', ['group_id'], { 2 => [1] }).should == 1
|
@@ -101,23 +82,43 @@ describe Sphinx::Client, 'connected' do
|
|
101
82
|
end
|
102
83
|
end
|
103
84
|
|
85
|
+
context 'in Open method' do
|
86
|
+
it 'should open socket' do
|
87
|
+
@sphinx.Open.should be_true
|
88
|
+
socket = @sphinx.servers.first.instance_variable_get(:@socket)
|
89
|
+
socket.should_not be_nil
|
90
|
+
socket.should be_kind_of(Sphinx::BufferedIO)
|
91
|
+
socket.close
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should produce an error when opened twice' do
|
95
|
+
@sphinx.Open.should be_true
|
96
|
+
@sphinx.Open.should be_false
|
97
|
+
@sphinx.GetLastError.should == 'already connected'
|
98
|
+
|
99
|
+
socket = @sphinx.servers.first.instance_variable_get(:@socket)
|
100
|
+
socket.should be_kind_of(Sphinx::BufferedIO)
|
101
|
+
socket.close
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
104
105
|
context 'in Close method' do
|
105
106
|
it 'should open socket' do
|
106
107
|
@sphinx.Open.should be_true
|
107
108
|
@sphinx.Close.should be_true
|
108
|
-
@sphinx.instance_variable_get(:@socket).should
|
109
|
+
@sphinx.servers.first.instance_variable_get(:@socket).should be_nil
|
109
110
|
end
|
110
|
-
|
111
|
+
|
111
112
|
it 'should produce socket is closed' do
|
112
113
|
@sphinx.Close.should be_false
|
113
114
|
@sphinx.GetLastError.should == 'not connected'
|
114
|
-
@sphinx.instance_variable_get(:@socket).should
|
115
|
-
|
115
|
+
@sphinx.servers.first.instance_variable_get(:@socket).should be_nil
|
116
|
+
|
116
117
|
@sphinx.Open.should be_true
|
117
118
|
@sphinx.Close.should be_true
|
118
119
|
@sphinx.Close.should be_false
|
119
120
|
@sphinx.GetLastError.should == 'not connected'
|
120
|
-
@sphinx.instance_variable_get(:@socket).should
|
121
|
+
@sphinx.servers.first.instance_variable_get(:@socket).should be_nil
|
121
122
|
end
|
122
123
|
end
|
123
124
|
|
data/spec/client_spec.rb
CHANGED
@@ -1,471 +1,650 @@
|
|
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
|
-
end
|
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
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
@
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
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
|
-
|
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 round-robin servers on each call' do
|
30
|
+
@sphinx.SetServers(@servers)
|
31
|
+
cnt = 0
|
32
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0] }
|
33
|
+
cnt.should == 1
|
34
|
+
cnt = 0
|
35
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[1] }
|
36
|
+
cnt.should == 1
|
37
|
+
cnt = 0
|
38
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0] }
|
39
|
+
cnt.should == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should round-robin servers and raise an exception on error' do
|
43
|
+
@sphinx.SetServers(@servers)
|
44
|
+
cnt = 0
|
45
|
+
expect {
|
46
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0]; raise Sphinx::SphinxConnectError }
|
47
|
+
}.to raise_error(Sphinx::SphinxConnectError)
|
48
|
+
cnt.should == 1
|
49
|
+
cnt = 0
|
50
|
+
expect {
|
51
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[1]; raise Sphinx::SphinxConnectError }
|
52
|
+
}.to raise_error(Sphinx::SphinxConnectError)
|
53
|
+
cnt.should == 1
|
54
|
+
cnt = 0
|
55
|
+
expect {
|
56
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0]; raise Sphinx::SphinxConnectError }
|
57
|
+
}.to raise_error(Sphinx::SphinxConnectError)
|
58
|
+
cnt.should == 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with retries' do
|
63
|
+
before :each do
|
64
|
+
@sphinx.SetConnectTimeout(0, 3)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should raise an exception on error' do
|
68
|
+
cnt = 0
|
69
|
+
expect {
|
70
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[0]; raise Sphinx::SphinxConnectError }
|
71
|
+
}.to raise_error(Sphinx::SphinxConnectError)
|
72
|
+
cnt.should == 3
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should round-robin servers and raise an exception on error' do
|
76
|
+
@sphinx.SetServers(@servers)
|
77
|
+
cnt = 0
|
78
|
+
expect {
|
79
|
+
@sphinx.send(:with_server) { |server| cnt += 1; server.should == @sphinx.servers[(cnt - 1) % 2]; raise Sphinx::SphinxConnectError }
|
80
|
+
}.to raise_error(Sphinx::SphinxConnectError)
|
81
|
+
cnt.should == 3
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'in with_socket method' do
|
87
|
+
before :each do
|
88
|
+
@sphinx = Sphinx::Client.new
|
89
|
+
@socket = mock('TCPSocket')
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'without retries' do
|
93
|
+
before :each do
|
94
|
+
@server = mock('Server')
|
95
|
+
@server.should_receive(:get_socket).and_yield(@socket).and_return(@socket)
|
96
|
+
@server.should_receive(:free_socket).with(@socket).at_least(1)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should initialize session' do
|
100
|
+
@socket.should_receive(:write).with([1].pack('N'))
|
101
|
+
@socket.should_receive(:read).with(4).and_return([1].pack('N'))
|
102
|
+
cnt = 0
|
103
|
+
@sphinx.send(:with_socket, @server) { |socket| cnt += 1; socket.should == @socket }
|
104
|
+
cnt.should == 1
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should raise exception when searchd protocol is not 1+' do
|
108
|
+
@socket.should_receive(:write).with([1].pack('N'))
|
109
|
+
@socket.should_receive(:read).with(4).and_return([0].pack('N'))
|
110
|
+
cnt = 0
|
111
|
+
expect {
|
112
|
+
@sphinx.send(:with_socket, @server) { cnt += 1; }
|
113
|
+
}.to raise_error(Sphinx::SphinxConnectError, 'expected searchd protocol version 1+, got version \'0\'')
|
114
|
+
cnt.should == 0
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should handle request timeouts' do
|
118
|
+
@socket.should_receive(:write).with([1].pack('N'))
|
119
|
+
@socket.should_receive(:read).with(4).and_return([1].pack('N'))
|
120
|
+
@sphinx.SetRequestTimeout(1)
|
121
|
+
cnt = 0
|
122
|
+
expect {
|
123
|
+
@sphinx.send(:with_socket, @server) { cnt += 1; sleep 2 }
|
124
|
+
}.to raise_error(Sphinx::SphinxResponseError, 'failed to read searchd response (msg=time\'s up!)')
|
125
|
+
cnt.should == 1
|
126
|
+
|
127
|
+
@sphinx.GetLastError.should == 'failed to read searchd response (msg=time\'s up!)'
|
128
|
+
@sphinx.IsConnectError.should be_false
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should re-reaise Sphinx errors' do
|
132
|
+
@socket.should_receive(:write).with([1].pack('N'))
|
133
|
+
@socket.should_receive(:read).with(4).and_return([1].pack('N'))
|
134
|
+
cnt = 0
|
135
|
+
expect {
|
136
|
+
@sphinx.send(:with_socket, @server) { cnt += 1; raise Sphinx::SphinxInternalError, 'hello' }
|
137
|
+
}.to raise_error(Sphinx::SphinxInternalError, 'hello')
|
138
|
+
cnt.should == 1
|
139
|
+
|
140
|
+
@sphinx.GetLastError.should == 'hello'
|
141
|
+
@sphinx.IsConnectError.should be_false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'with retries' do
|
146
|
+
before :each do
|
147
|
+
@sphinx.SetRequestTimeout(0, 3)
|
148
|
+
# two more times yielding - retries
|
149
|
+
@server = mock('Server')
|
150
|
+
@server.should_receive(:get_socket).at_least(1).times.and_yield(@socket).and_return(@socket)
|
151
|
+
@server.should_receive(:free_socket).with(@socket).at_least(1)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should raise an exception on error' do
|
155
|
+
@socket.should_receive(:write).exactly(3).times.with([1].pack('N'))
|
156
|
+
@socket.should_receive(:read).exactly(3).times.with(4).and_return([1].pack('N'))
|
157
|
+
cnt = 0
|
158
|
+
expect {
|
159
|
+
@sphinx.send(:with_socket, @server) { cnt += 1; raise Sphinx::SphinxInternalError, 'hello' }
|
160
|
+
}.to raise_error(Sphinx::SphinxInternalError, 'hello')
|
161
|
+
cnt.should == 3
|
162
|
+
|
163
|
+
@sphinx.GetLastError.should == 'hello'
|
164
|
+
@sphinx.IsConnectError.should be_false
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'in parse_response method' do
|
170
|
+
before :each do
|
171
|
+
@sphinx = Sphinx::Client.new
|
172
|
+
@socket = mock('TCPSocket')
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should receive response' do
|
176
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
|
177
|
+
@socket.should_receive(:read).with(4).and_return([0].pack('N'))
|
178
|
+
@sphinx.send(:parse_response, @socket, 1)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should raise exception on zero-sized response' do
|
182
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 0].pack('n2N'))
|
183
|
+
expect {
|
184
|
+
@sphinx.send(:parse_response, @socket, 1)
|
185
|
+
}.to raise_error(Sphinx::SphinxResponseError, 'received zero-sized searchd response')
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should raise exception when response is incomplete' do
|
189
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 4].pack('n2N'))
|
190
|
+
@socket.should_receive(:read).with(4).and_return('')
|
191
|
+
expect {
|
192
|
+
@sphinx.send(:parse_response, @socket, 1)
|
193
|
+
}.to raise_error(Sphinx::SphinxResponseError)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should set warning message when SEARCHD_WARNING received' do
|
197
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_WARNING, 1, 14].pack('n2N'))
|
198
|
+
@socket.should_receive(:read).with(14).and_return([5].pack('N') + 'helloworld')
|
199
|
+
@sphinx.send(:parse_response, @socket, 1).should == 'world'
|
200
|
+
@sphinx.GetLastWarning.should == 'hello'
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'should raise exception when SEARCHD_ERROR received' do
|
204
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_ERROR, 1, 9].pack('n2N'))
|
205
|
+
@socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
|
206
|
+
expect {
|
207
|
+
@sphinx.send(:parse_response, @socket, 1)
|
208
|
+
}.to raise_error(Sphinx::SphinxInternalError, 'searchd error: hello')
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should raise exception when SEARCHD_RETRY received' do
|
212
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_RETRY, 1, 9].pack('n2N'))
|
213
|
+
@socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
|
214
|
+
expect {
|
215
|
+
@sphinx.send(:parse_response, @socket, 1)
|
216
|
+
}.to raise_error(Sphinx::SphinxTemporaryError, 'temporary searchd error: hello')
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should raise exception when unknown status received' do
|
220
|
+
@socket.should_receive(:read).with(8).and_return([65535, 1, 9].pack('n2N'))
|
221
|
+
@socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
|
222
|
+
expect {
|
223
|
+
@sphinx.send(:parse_response, @socket, 1)
|
224
|
+
}.to raise_error(Sphinx::SphinxUnknownError, 'unknown status code: \'65535\'')
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should set warning when server is older than client' do
|
228
|
+
@socket.should_receive(:read).with(8).and_return([Sphinx::Client::SEARCHD_OK, 1, 9].pack('n2N'))
|
229
|
+
@socket.should_receive(:read).with(9).and_return([1].pack('N') + 'hello')
|
230
|
+
@sphinx.send(:parse_response, @socket, 5)
|
231
|
+
@sphinx.GetLastWarning.should == 'searchd command v.0.1 older than client\'s v.0.5, some options might not work'
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'in Query method' do
|
236
|
+
before :each do
|
237
|
+
@sphinx = sphinx_create_client
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'should generate valid request with default parameters' do
|
241
|
+
expected = sphinx_fixture('default_search')
|
242
|
+
@sock.should_receive(:write).with(expected)
|
243
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should generate valid request with default parameters and index' do
|
247
|
+
expected = sphinx_fixture('default_search_index')
|
248
|
+
@sock.should_receive(:write).with(expected)
|
249
|
+
sphinx_safe_call { @sphinx.Query('query', 'index') }
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should generate valid request with limits' do
|
253
|
+
expected = sphinx_fixture('limits')
|
254
|
+
@sock.should_receive(:write).with(expected)
|
255
|
+
@sphinx.SetLimits(10, 20)
|
256
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should generate valid request with limits and max number to retrieve' do
|
260
|
+
expected = sphinx_fixture('limits_max')
|
261
|
+
@sock.should_receive(:write).with(expected)
|
262
|
+
@sphinx.SetLimits(10, 20, 30)
|
263
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should generate valid request with limits and cutoff to retrieve' do
|
267
|
+
expected = sphinx_fixture('limits_cutoff')
|
268
|
+
@sock.should_receive(:write).with(expected)
|
269
|
+
@sphinx.SetLimits(10, 20, 30, 40)
|
270
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should generate valid request with max query time specified' do
|
274
|
+
expected = sphinx_fixture('max_query_time')
|
275
|
+
@sock.should_receive(:write).with(expected)
|
276
|
+
@sphinx.SetMaxQueryTime(1000)
|
277
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
278
|
+
end
|
279
|
+
|
280
|
+
describe 'with match' do
|
281
|
+
[ :all, :any, :phrase, :boolean, :extended, :fullscan, :extended2 ].each do |match|
|
282
|
+
it "should generate valid request for SPH_MATCH_#{match.to_s.upcase}" do
|
283
|
+
expected = sphinx_fixture("match_#{match}")
|
284
|
+
@sock.should_receive(:write).with(expected)
|
285
|
+
@sphinx.SetMatchMode(Sphinx::Client::const_get("SPH_MATCH_#{match.to_s.upcase}"))
|
286
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should generate valid request for \"#{match}\"" do
|
290
|
+
expected = sphinx_fixture("match_#{match}")
|
291
|
+
@sock.should_receive(:write).with(expected)
|
292
|
+
@sphinx.SetMatchMode(match.to_s)
|
293
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should generate valid request for :#{match}" do
|
297
|
+
expected = sphinx_fixture("match_#{match}")
|
298
|
+
@sock.should_receive(:write).with(expected)
|
299
|
+
@sphinx.SetMatchMode(match)
|
300
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe 'with rank' do
|
306
|
+
[ :proximity_bm25, :bm25, :none, :wordcount, :proximity, :matchany, :fieldmask, :sph04 ].each do |rank|
|
307
|
+
it "should generate valid request for SPH_RANK_#{rank.to_s.upcase}" do
|
308
|
+
expected = sphinx_fixture("ranking_#{rank}")
|
309
|
+
@sock.should_receive(:write).with(expected)
|
310
|
+
@sphinx.SetRankingMode(Sphinx::Client.const_get("SPH_RANK_#{rank.to_s.upcase}"))
|
311
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should generate valid request for \"#{rank}\"" do
|
315
|
+
expected = sphinx_fixture("ranking_#{rank}")
|
316
|
+
@sock.should_receive(:write).with(expected)
|
317
|
+
@sphinx.SetRankingMode(rank.to_s)
|
318
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should generate valid request for :#{rank}" do
|
322
|
+
expected = sphinx_fixture("ranking_#{rank}")
|
323
|
+
@sock.should_receive(:write).with(expected)
|
324
|
+
@sphinx.SetRankingMode(rank)
|
325
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe 'with sorting' do
|
331
|
+
[ :attr_desc, :relevance, :attr_asc, :time_segments, :extended, :expr ].each do |mode|
|
332
|
+
it "should generate valid request for SPH_SORT_#{mode.to_s.upcase}" do
|
333
|
+
expected = sphinx_fixture("sort_#{mode}")
|
334
|
+
@sock.should_receive(:write).with(expected)
|
335
|
+
@sphinx.SetSortMode(Sphinx::Client.const_get("SPH_SORT_#{mode.to_s.upcase}"), mode == :relevance ? '' : 'sortby')
|
336
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
337
|
+
end
|
338
|
+
|
339
|
+
it "should generate valid request for \"#{mode}\"" do
|
340
|
+
expected = sphinx_fixture("sort_#{mode}")
|
341
|
+
@sock.should_receive(:write).with(expected)
|
342
|
+
@sphinx.SetSortMode(mode.to_s, mode == :relevance ? '' : 'sortby')
|
343
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should generate valid request for :#{mode}" do
|
347
|
+
expected = sphinx_fixture("sort_#{mode}")
|
348
|
+
@sock.should_receive(:write).with(expected)
|
349
|
+
@sphinx.SetSortMode(mode, mode == :relevance ? '' : 'sortby')
|
350
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
it 'should generate valid request with weights' do
|
356
|
+
expected = sphinx_fixture('weights')
|
357
|
+
@sock.should_receive(:write).with(expected)
|
358
|
+
@sphinx.SetWeights([10, 20, 30, 40])
|
359
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should generate valid request with field weights' do
|
363
|
+
expected = sphinx_fixture('field_weights')
|
364
|
+
@sock.should_receive(:write).with(expected)
|
365
|
+
@sphinx.SetFieldWeights({'field1' => 10, 'field2' => 20})
|
366
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'should generate valid request with index weights' do
|
370
|
+
expected = sphinx_fixture('index_weights')
|
371
|
+
@sock.should_receive(:write).with(expected)
|
372
|
+
@sphinx.SetIndexWeights({'index1' => 10, 'index2' => 20})
|
373
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'should generate valid request with ID range' do
|
377
|
+
expected = sphinx_fixture('id_range')
|
378
|
+
@sock.should_receive(:write).with(expected)
|
379
|
+
@sphinx.SetIDRange(10, 20)
|
380
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should generate valid request with ID range and 64-bit ints' do
|
384
|
+
expected = sphinx_fixture('id_range64')
|
385
|
+
@sock.should_receive(:write).with(expected)
|
386
|
+
@sphinx.SetIDRange(8589934591, 17179869183)
|
387
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'should generate valid request with values filter' do
|
391
|
+
expected = sphinx_fixture('filter')
|
392
|
+
@sock.should_receive(:write).with(expected)
|
393
|
+
@sphinx.SetFilter('attr', [10, 20, 30])
|
394
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'should generate valid request with two values filters' do
|
398
|
+
expected = sphinx_fixture('filters')
|
399
|
+
@sock.should_receive(:write).with(expected)
|
400
|
+
@sphinx.SetFilter('attr2', [40, 50])
|
401
|
+
@sphinx.SetFilter('attr1', [10, 20, 30])
|
402
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
403
|
+
end
|
404
|
+
|
405
|
+
it 'should generate valid request with values filter excluded' do
|
406
|
+
expected = sphinx_fixture('filter_exclude')
|
407
|
+
@sock.should_receive(:write).with(expected)
|
408
|
+
@sphinx.SetFilter('attr', [10, 20, 30], true)
|
409
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'should generate valid request with values filter range' do
|
413
|
+
expected = sphinx_fixture('filter_range')
|
414
|
+
@sock.should_receive(:write).with(expected)
|
415
|
+
@sphinx.SetFilterRange('attr', 10, 20)
|
416
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
417
|
+
end
|
418
|
+
|
419
|
+
it 'should generate valid request with two filter ranges' do
|
420
|
+
expected = sphinx_fixture('filter_ranges')
|
421
|
+
@sock.should_receive(:write).with(expected)
|
422
|
+
@sphinx.SetFilterRange('attr2', 30, 40)
|
423
|
+
@sphinx.SetFilterRange('attr1', 10, 20)
|
424
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
425
|
+
end
|
426
|
+
|
427
|
+
it 'should generate valid request with filter range excluded' do
|
428
|
+
expected = sphinx_fixture('filter_range_exclude')
|
429
|
+
@sock.should_receive(:write).with(expected)
|
430
|
+
@sphinx.SetFilterRange('attr', 10, 20, true)
|
431
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'should generate valid request with signed int64-based filter range' do
|
435
|
+
expected = sphinx_fixture('filter_range_int64')
|
436
|
+
@sock.should_receive(:write).with(expected)
|
437
|
+
@sphinx.SetFilterRange('attr1', -10, 20)
|
438
|
+
@sphinx.SetFilterRange('attr2', -1099511627770, 1099511627780)
|
439
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'should generate valid request with float filter range' do
|
443
|
+
expected = sphinx_fixture('filter_float_range')
|
444
|
+
@sock.should_receive(:write).with(expected)
|
445
|
+
@sphinx.SetFilterFloatRange('attr', 10.5, 20.3)
|
446
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'should generate valid request with float filter excluded' do
|
450
|
+
expected = sphinx_fixture('filter_float_range_exclude')
|
451
|
+
@sock.should_receive(:write).with(expected)
|
452
|
+
@sphinx.SetFilterFloatRange('attr', 10.5, 20.3, true)
|
453
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'should generate valid request with different filters' do
|
457
|
+
expected = sphinx_fixture('filters_different')
|
458
|
+
@sock.should_receive(:write).with(expected)
|
459
|
+
@sphinx.SetFilterRange('attr1', 10, 20, true)
|
460
|
+
@sphinx.SetFilter('attr3', [30, 40, 50])
|
461
|
+
@sphinx.SetFilterRange('attr1', 60, 70)
|
462
|
+
@sphinx.SetFilter('attr2', [80, 90, 100], true)
|
463
|
+
@sphinx.SetFilterFloatRange('attr1', 60.8, 70.5)
|
464
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'should generate valid request with geographical anchor point' do
|
468
|
+
expected = sphinx_fixture('geo_anchor')
|
469
|
+
@sock.should_receive(:write).with(expected)
|
470
|
+
@sphinx.SetGeoAnchor('attrlat', 'attrlong', 20.3, 40.7)
|
471
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
472
|
+
end
|
473
|
+
|
474
|
+
describe 'with group by' do
|
475
|
+
[ :day, :week, :month, :year, :attr, :attrpair ].each do |groupby|
|
476
|
+
it "should generate valid request for SPH_GROUPBY_#{groupby.to_s.upcase}" do
|
477
|
+
expected = sphinx_fixture("group_by_#{groupby}")
|
478
|
+
@sock.should_receive(:write).with(expected)
|
479
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::const_get("SPH_GROUPBY_#{groupby.to_s.upcase}"))
|
480
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should generate valid request for \"#{groupby}\"" do
|
484
|
+
expected = sphinx_fixture("group_by_#{groupby}")
|
485
|
+
@sock.should_receive(:write).with(expected)
|
486
|
+
@sphinx.SetGroupBy('attr', groupby.to_s)
|
487
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should generate valid request for :#{groupby}" do
|
491
|
+
expected = sphinx_fixture("group_by_#{groupby}")
|
492
|
+
@sock.should_receive(:write).with(expected)
|
493
|
+
@sphinx.SetGroupBy('attr', groupby)
|
494
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
it 'should generate valid request for SPH_GROUPBY_DAY with sort' do
|
499
|
+
expected = sphinx_fixture('group_by_day_sort')
|
500
|
+
@sock.should_receive(:write).with(expected)
|
501
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY, 'somesort')
|
502
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'should generate valid request with count-distinct attribute' do
|
506
|
+
expected = sphinx_fixture('group_distinct')
|
507
|
+
@sock.should_receive(:write).with(expected)
|
508
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
|
509
|
+
@sphinx.SetGroupDistinct('attr')
|
510
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'should generate valid request with retries count specified' do
|
515
|
+
expected = sphinx_fixture('retries')
|
516
|
+
@sock.should_receive(:write).with(expected)
|
517
|
+
@sphinx.SetRetries(10)
|
518
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'should generate valid request with retries count and delay specified' do
|
522
|
+
expected = sphinx_fixture('retries_delay')
|
523
|
+
@sock.should_receive(:write).with(expected)
|
524
|
+
@sphinx.SetRetries(10, 20)
|
525
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'should generate valid request for SetOverride' do
|
529
|
+
expected = sphinx_fixture('set_override')
|
530
|
+
@sock.should_receive(:write).with(expected)
|
531
|
+
@sphinx.SetOverride('attr1', Sphinx::Client::SPH_ATTR_INTEGER, { 10 => 20 })
|
532
|
+
@sphinx.SetOverride('attr2', Sphinx::Client::SPH_ATTR_FLOAT, { 11 => 30.3 })
|
533
|
+
@sphinx.SetOverride('attr3', Sphinx::Client::SPH_ATTR_BIGINT, { 12 => 1099511627780 })
|
534
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
535
|
+
end
|
536
|
+
|
537
|
+
it 'should generate valid request for SetSelect' do
|
538
|
+
expected = sphinx_fixture('select')
|
539
|
+
@sock.should_receive(:write).with(expected)
|
540
|
+
@sphinx.SetSelect('attr1, attr2')
|
541
|
+
sphinx_safe_call { @sphinx.Query('query') }
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
context 'in RunQueries method' do
|
546
|
+
before(:each) do
|
547
|
+
@sphinx = sphinx_create_client
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'should generate valid request for multiple queries' do
|
551
|
+
expected = sphinx_fixture('miltiple_queries')
|
552
|
+
@sock.should_receive(:write).with(expected)
|
553
|
+
|
554
|
+
@sphinx.SetRetries(10, 20)
|
555
|
+
@sphinx.AddQuery('test1')
|
556
|
+
@sphinx.SetGroupBy('attr', Sphinx::Client::SPH_GROUPBY_DAY)
|
557
|
+
@sphinx.AddQuery('test2')
|
558
|
+
|
559
|
+
sphinx_safe_call { @sphinx.RunQueries }
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context 'in BuildExcerpts method' do
|
564
|
+
before :each do
|
565
|
+
@sphinx = sphinx_create_client
|
566
|
+
end
|
567
|
+
|
568
|
+
it 'should generate valid request with default parameters' do
|
569
|
+
expected = sphinx_fixture('excerpt_default')
|
570
|
+
@sock.should_receive(:write).with(expected)
|
571
|
+
sphinx_safe_call { @sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2') }
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'should generate valid request with custom parameters' do
|
575
|
+
expected = sphinx_fixture('excerpt_custom')
|
576
|
+
@sock.should_receive(:write).with(expected)
|
577
|
+
sphinx_safe_call do
|
578
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'before_match' => 'before',
|
579
|
+
'after_match' => 'after',
|
580
|
+
'chunk_separator' => 'separator',
|
581
|
+
'limit' => 10 })
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
it 'should generate valid request with custom parameters as symbols' do
|
586
|
+
expected = sphinx_fixture('excerpt_custom')
|
587
|
+
@sock.should_receive(:write).with(expected)
|
588
|
+
sphinx_safe_call do
|
589
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { :before_match => 'before',
|
590
|
+
:after_match => 'after',
|
591
|
+
:chunk_separator => 'separator',
|
592
|
+
:limit => 10 })
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
it 'should generate valid request with flags' do
|
597
|
+
expected = sphinx_fixture('excerpt_flags')
|
598
|
+
@sock.should_receive(:write).with(expected)
|
599
|
+
sphinx_safe_call do
|
600
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { 'exact_phrase' => true,
|
601
|
+
'single_passage' => true,
|
602
|
+
'use_boundaries' => true,
|
603
|
+
'weight_order' => true,
|
604
|
+
'query_mode' => true })
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
it 'should generate valid request with flags as symbols' do
|
609
|
+
expected = sphinx_fixture('excerpt_flags')
|
610
|
+
@sock.should_receive(:write).with(expected)
|
611
|
+
sphinx_safe_call do
|
612
|
+
@sphinx.BuildExcerpts(['10', '20'], 'index', 'word1 word2', { :exact_phrase => true,
|
613
|
+
:single_passage => true,
|
614
|
+
:use_boundaries => true,
|
615
|
+
:weight_order => true,
|
616
|
+
:query_mode => true })
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context 'in BuildKeywords method' do
|
622
|
+
before :each do
|
623
|
+
@sphinx = sphinx_create_client
|
624
|
+
end
|
625
|
+
|
626
|
+
it 'should generate valid request' do
|
627
|
+
expected = sphinx_fixture('keywords')
|
628
|
+
@sock.should_receive(:write).with(expected)
|
629
|
+
sphinx_safe_call { @sphinx.BuildKeywords('test', 'index', true) }
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
context 'in UpdateAttributes method' do
|
634
|
+
before :each do
|
635
|
+
@sphinx = sphinx_create_client
|
636
|
+
end
|
637
|
+
|
638
|
+
it 'should generate valid request' do
|
639
|
+
expected = sphinx_fixture('update_attributes')
|
640
|
+
@sock.should_receive(:write).with(expected)
|
641
|
+
sphinx_safe_call { @sphinx.UpdateAttributes('index', ['group'], { 123 => [456] }) }
|
642
|
+
end
|
643
|
+
|
644
|
+
it 'should generate valid request for MVA' do
|
645
|
+
expected = sphinx_fixture('update_attributes_mva')
|
646
|
+
@sock.should_receive(:write).with(expected)
|
647
|
+
sphinx_safe_call { @sphinx.UpdateAttributes('index', ['group', 'category'], { 123 => [ [456, 789], [1, 2, 3] ] }, true) }
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|