📄 client.rb
字号:
# read matches
result['matches'] = []
while count > 0
count -= 1
if id64 != 0
doc = response.get_int64
weight = response.get_int
else
doc, weight = response.get_ints(2)
end
r = {} # This is a single result put in the result['matches'] array
r['id'] = doc
r['weight'] = weight
attrs_names_in_order.each do |a|
r['attrs'] ||= {}
# handle floats
if attrs[a] == SPH_ATTR_FLOAT
r['attrs'][a] = response.get_float
else
# handle everything else as unsigned ints
val = response.get_int
if (attrs[a] & SPH_ATTR_MULTI) != 0
r['attrs'][a] = []
1.upto(val) do
r['attrs'][a] << response.get_int
end
else
r['attrs'][a] = val
end
end
end
result['matches'] << r
end
result['total'], result['total_found'], msecs, words = response.get_ints(4)
result['time'] = '%.3f' % (msecs / 1000.0)
result['words'] = {}
while words > 0
words -= 1
word = response.get_string
docs, hits = response.get_ints(2)
result['words'][word] = { 'docs' => docs, 'hits' => hits }
end
results << result
end
#rescue EOFError
# @error = 'incomplete reply'
# raise SphinxResponseError, @error
end
return results
end
# Connect to searchd server and generate exceprts from given documents.
#
# * <tt>docs</tt> -- an array of strings which represent the documents' contents
# * <tt>index</tt> -- a string specifiying the index which settings will be used
# for stemming, lexing and case folding
# * <tt>words</tt> -- a string which contains the words to highlight
# * <tt>opts</tt> is a hash which contains additional optional highlighting parameters.
#
# You can use following parameters:
# * <tt>'before_match'</tt> -- a string to insert before a set of matching words, default is "<b>"
# * <tt>'after_match'</tt> -- a string to insert after a set of matching words, default is "<b>"
# * <tt>'chunk_separator'</tt> -- a string to insert between excerpts chunks, default is " ... "
# * <tt>'limit'</tt> -- max excerpt size in symbols (codepoints), default is 256
# * <tt>'around'</tt> -- how much words to highlight around each match, default is 5
# * <tt>'exact_phrase'</tt> -- whether to highlight exact phrase matches only, default is <tt>false</tt>
# * <tt>'single_passage'</tt> -- whether to extract single best passage only, default is false
# * <tt>'use_boundaries'</tt> -- whether to extract passages by phrase boundaries setup in tokenizer
# * <tt>'weight_order'</tt> -- whether to order best passages in document (default) or weight order
#
# Returns false on failure.
# Returns an array of string excerpts on success.
def BuildExcerpts(docs, index, words, opts = {})
assert { docs.instance_of? Array }
assert { index.instance_of? String }
assert { words.instance_of? String }
assert { opts.instance_of? Hash }
# fixup options
opts['before_match'] ||= '<b>';
opts['after_match'] ||= '</b>';
opts['chunk_separator'] ||= ' ... ';
opts['limit'] ||= 256;
opts['around'] ||= 5;
opts['exact_phrase'] ||= false
opts['single_passage'] ||= false
opts['use_boundaries'] ||= false
opts['weight_order'] ||= false
# build request
# v.1.0 req
flags = 1
flags |= 2 if opts['exact_phrase']
flags |= 4 if opts['single_passage']
flags |= 8 if opts['use_boundaries']
flags |= 16 if opts['weight_order']
request = Request.new
request.put_int 0, flags # mode=0, flags=1 (remove spaces)
# req index
request.put_string index
# req words
request.put_string words
# options
request.put_string opts['before_match']
request.put_string opts['after_match']
request.put_string opts['chunk_separator']
request.put_int opts['limit'].to_i, opts['around'].to_i
# documents
request.put_int docs.size
docs.each do |doc|
assert { doc.instance_of? String }
request.put_string doc
end
response = PerformRequest(:excerpt, request)
# parse response
begin
res = []
docs.each do |doc|
res << response.get_string
end
rescue EOFError
@error = 'incomplete reply'
raise SphinxResponseError, @error
end
return res
end
# Connect to searchd server, and generate keyword list for a given query.
#
# Returns an array of words on success.
def BuildKeywords(query, index, hits)
assert { query.instance_of? String }
assert { index.instance_of? String }
assert { hits.instance_of? Boolean }
# build request
request = Request.new
# v.1.0 req
request.put_string query # req query
request.put_string index # req index
request.put_int hits ? 1 : 0
response = PerformRequest(:keywords, request)
# parse response
begin
res = []
nwords = response.get_int
0.upto(nwords - 1) do |i|
tokenized = response.get_string
normalized = response.get_string
entry = { 'tokenized' => tokenized, 'normalized' => normalized }
entry['docs'], entry['hits'] = response.get_ints(2) if hits
res << entry
end
rescue EOFError
@error = 'incomplete reply'
raise SphinxResponseError, @error
end
return res
end
# Update specified attributes on specified documents.
#
# * <tt>index</tt> is a name of the index to be updated
# * <tt>attrs</tt> is an array of attribute name strings.
# * <tt>values</tt> is a hash where key is document id, and value is an array of
# new attribute values
#
# Returns number of actually updated documents (0 or more) on success.
# Returns -1 on failure.
#
# Usage example:
# sphinx.UpdateAttributes('test1', ['group_id'], { 1 => [456] })
def UpdateAttributes(index, attrs, values)
# verify everything
assert { index.instance_of? String }
assert { attrs.instance_of? Array }
attrs.each do |attr|
assert { attr.instance_of? String }
end
assert { values.instance_of? Hash }
values.each do |id, entry|
assert { id.instance_of? Fixnum }
assert { entry.instance_of? Array }
assert { entry.length == attrs.length }
entry.each do |v|
assert { v.instance_of? Fixnum }
end
end
# build request
request = Request.new
request.put_string index
request.put_int attrs.length
request.put_string(*attrs)
request.put_int values.length
values.each do |id, entry|
request.put_int64 id
request.put_int(*entry)
end
response = PerformRequest(:update, request)
# parse response
begin
return response.get_int
rescue EOFError
@error = 'incomplete reply'
raise SphinxResponseError, @error
end
end
protected
# Connect to searchd server.
def Connect
begin
sock = TCPSocket.new(@host, @port)
rescue
@error = "connection to #{@host}:#{@port} failed"
raise SphinxConnectError, @error
end
v = sock.recv(4).unpack('N*').first
if v < 1
sock.close
@error = "expected searchd protocol version 1+, got version '#{v}'"
raise SphinxConnectError, @error
end
sock.send([1].pack('N'), 0)
sock
end
# Get and check response packet from searchd server.
def GetResponse(sock, client_version)
response = ''
len = 0
header = sock.recv(8)
if header.length == 8
status, ver, len = header.unpack('n2N')
left = len.to_i
while left > 0 do
begin
chunk = sock.recv(left)
if chunk
response << chunk
left -= chunk.length
end
rescue EOFError
break
end
end
end
sock.close
# check response
read = response.length
if response.empty? or read != len.to_i
@error = len \
? "failed to read searchd response (status=#{status}, ver=#{ver}, len=#{len}, read=#{read})" \
: 'received zero-sized searchd response'
raise SphinxResponseError, @error
end
# check status
if (status == SEARCHD_WARNING)
wlen = response[0, 4].unpack('N*').first
@warning = response[4, wlen]
return response[4 + wlen, response.length - 4 - wlen]
end
if status == SEARCHD_ERROR
@error = 'searchd error: ' + response[4, response.length - 4]
raise SphinxInternalError, @error
end
if status == SEARCHD_RETRY
@error = 'temporary searchd error: ' + response[4, response.length - 4]
raise SphinxTemporaryError, @error
end
unless status == SEARCHD_OK
@error = "unknown status code: '#{status}'"
raise SphinxUnknownError, @error
end
# check version
if ver < client_version
@warning = "searchd command v.#{ver >> 8}.#{ver & 0xff} older than client's " +
"v.#{client_version >> 8}.#{client_version & 0xff}, some options might not work"
end
return response
end
# Connect, send query, get response.
def PerformRequest(command, request, additional = nil)
cmd = command.to_s.upcase
command_id = Sphinx::Client.const_get('SEARCHD_COMMAND_' + cmd)
command_ver = Sphinx::Client.const_get('VER_COMMAND_' + cmd)
sock = self.Connect
len = request.to_s.length + (additional != nil ? 4 : 0)
header = [command_id, command_ver, len].pack('nnN')
header << [additional].pack('N') if additional != nil
sock.send(header + request.to_s, 0)
response = self.GetResponse(sock, command_ver)
return Response.new(response)
end
# :stopdoc:
def assert
raise 'Assertion failed!' unless yield if $DEBUG
end
# :startdoc:
end
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -