Class Sphinx::Client
In: lib/client.rb
Parent: Object

Methods

Constants

SPH_MATCH_ALL = 0   match all query words
SPH_MATCH_ANY = 1   match any query word
SPH_MATCH_PHRASE = 2   match this exact phrase
SPH_MATCH_BOOLEAN = 3   match this boolean query
SPH_MATCH_EXTENDED = 4   match this extended query
SPH_MATCH_FULLSCAN = 5   match all document IDs w/o fulltext query, apply filters
SPH_MATCH_EXTENDED2 = 6   extended engine V2 (TEMPORARY, WILL BE REMOVED IN 0.9.8-RELEASE)
SPH_SORT_RELEVANCE = 0   sort by document relevance desc, then by date
SPH_SORT_ATTR_DESC = 1   sort by document date desc, then by relevance desc
SPH_SORT_ATTR_ASC = 2   sort by document date asc, then by relevance desc
SPH_SORT_TIME_SEGMENTS = 3   sort by time segments (hour/day/week/etc) desc, then by relevance desc
SPH_SORT_EXTENDED = 4   sort by SQL-like expression (eg. "@relevance DESC, price ASC, @id DESC")
SPH_FILTER_VALUES = 0   filter by integer values set
SPH_FILTER_RANGE = 1   filter by integer range
SPH_FILTER_FLOATRANGE = 2   filter by float range
SPH_ATTR_INTEGER = 1   this attr is just an integer
SPH_ATTR_TIMESTAMP = 2   this attr is a timestamp
SPH_ATTR_ORDINAL = 3   this attr is an ordinal string number (integer at search time, specially handled at indexing time)
SPH_ATTR_BOOL = 4   this attr is a boolean bit field
SPH_ATTR_FLOAT = 5   this attr is a float
SPH_ATTR_MULTI = 0x40000000   this attr has multiple values (0 or more)
SPH_GROUPBY_DAY = 0   group by day
SPH_GROUPBY_WEEK = 1   group by week
SPH_GROUPBY_MONTH = 2   group by month
SPH_GROUPBY_YEAR = 3   group by year
SPH_GROUPBY_ATTR = 4   group by attribute value
SPH_GROUPBY_ATTRPAIR = 5   group by sequential attrs pair

Public Class methods

Constructs the Sphinx::Client object and sets options to their default values.

[Source]

     # File lib/client.rb, line 148
148:     def initialize
149:       # per-client-object settings
150:       @host          = 'localhost'         # searchd host (default is "localhost")
151:       @port          = 3312                # searchd port (default is 3312)
152:       
153:       # per-query settings
154:       @offset        = 0                   # how many records to seek from result-set start (default is 0)
155:       @limit         = 20                  # how many records to return from result-set starting at offset (default is 20)
156:       @mode          = SPH_MATCH_ALL       # query matching mode (default is SPH_MATCH_ALL)
157:       @weights       = []                  # per-field weights (default is 1 for all fields)
158:       @sort          = SPH_SORT_RELEVANCE  # match sorting mode (default is SPH_SORT_RELEVANCE)
159:       @sortby        = ''                  # attribute to sort by (defualt is "")
160:       @min_id        = 0                   # min ID to match (default is 0, which means no limit)
161:       @max_id        = 0                   # max ID to match (default is 0, which means no limit)
162:       @filters       = []                  # search filters
163:       @groupby       = ''                  # group-by attribute name
164:       @groupfunc     = SPH_GROUPBY_DAY     # function to pre-process group-by attribute value with
165:       @groupsort     = '@group desc'       # group-by sorting clause (to sort groups in result set with)
166:       @groupdistinct = ''                  # group-by count-distinct attribute
167:       @maxmatches    = 1000                # max matches to retrieve
168:       @cutoff        = 0                   # cutoff to stop searching at (default is 0)
169:       @retrycount    = 0                   # distributed retries count
170:       @retrydelay    = 0                   # distributed retries delay
171:       @anchor        = []                  # geographical anchor point
172:       @indexweights  = []                  # per-index weights
173:     
174:       # per-reply fields (for single-query case)
175:       @error         = ''                  # last error message
176:       @warning       = ''                  # last warning message
177:       
178:       # requests storage (for multi-query case)
179:       @reqs          = []                  # requests array for multi-query
180:     end

Public Instance methods

Add query to batch.

Batch queries enable searchd to perform internal optimizations, if possible; and reduce network connection overheads in all cases.

For instance, running exactly the same query with different groupby settings will enable searched to perform expensive full-text search and ranking operation only once, but compute multiple groupby results from its output.

Parameters are exactly the same as in Query call. Returns index to results array returned by RunQueries call.

[Source]

     # File lib/client.rb, line 474
474:     def AddQuery(query, index = '*')
475:       # build request
476:   
477:       # mode and limits
478:       req = [@offset, @limit, @mode, @sort].pack('NNNN')
479:       req << [@sortby.length].pack('N') + @sortby
480:       # query itself
481:       req << [query.length].pack('N') + query
482:       # weights
483:       req << [@weights.length].pack('N')
484:       req << @weights.pack('N' * @weights.length)
485:       # indexes
486:       req << [index.length].pack('N') + index
487:       # id32 range
488:       req << [0, @min_id.to_i, @max_id.to_i].pack('NNN')
489:       
490:       # filters
491:       req << [@filters.length].pack('N')
492:       @filters.each do |filter|
493:         req << [filter['attr'].length].pack('N') + filter['attr']
494:         req << [filter['type']].pack('N')
495: 
496:         case filter['type']
497:           when SPH_FILTER_VALUES
498:             req << [filter['values'].length].pack('N')
499:             req << filter['values'].pack('N' * filter['values'].length)
500:           when SPH_FILTER_RANGE
501:             req << [filter['min'], filter['max']].pack('NN')
502:           when SPH_FILTER_FLOATRANGE
503:             req << self.PackFloat(filter['min']) + self.PackFloat(filter['max'])
504:           else
505:             raise SphinxInternalError, 'Internal error: unhandled filter type'
506:         end
507:         req << [filter['exclude'] ? 1 : 0].pack('N')
508:       end
509:       
510:       # group-by clause, max-matches count, group-sort clause, cutoff count
511:       req << [@groupfunc, @groupby.length].pack('NN') + @groupby
512:       req << [@maxmatches].pack('N')
513:       req << [@groupsort.length].pack('N') + @groupsort
514:       req << [@cutoff, @retrycount, @retrydelay].pack('NNN')
515:       req << [@groupdistinct.length].pack('N') + @groupdistinct
516:       
517:       # anchor point
518:       if @anchor.empty?
519:         req << [0].pack('N')
520:       else
521:         req << [1].pack('N')
522:         req << [@anchor['attrlat'].length].pack('N') + @anchor['attrlat']
523:         req << [@anchor['attrlong'].length].pack('N') + @anchor['attrlong']
524:         req << self.PackFloat(@anchor['lat']) + self.PackFloat(@anchor['long'])
525:       end
526:       
527:       # per-index weights
528:       req << [@indexweights.length].pack('N')
529:       @indexweights.each do |idx, weight|
530:         req << [idx.length].pack('N') + idx + [weight].pack('N')
531:       end
532:       
533:       # store request to requests array
534:       @reqs << req;
535:       return @reqs.length - 1
536:     end

Connect to searchd server and generate exceprts from given documents.

  • docs — an array of strings which represent the documents’ contents
  • index — a string specifiying the index which settings will be used

for stemming, lexing and case folding

  • words — a string which contains the words to highlight
  • opts is a hash which contains additional optional highlighting parameters.

You can use following parameters:

  • ‘before_match‘ — a string to insert before a set of matching words, default is "<b>"
  • ‘after_match‘ — a string to insert after a set of matching words, default is "<b>"
  • ‘chunk_separator‘ — a string to insert between excerpts chunks, default is " … "
  • ‘limit‘ — max excerpt size in symbols (codepoints), default is 256
  • ‘around‘ — how much words to highlight around each match, default is 5
  • ‘exact_phrase‘ — whether to highlight exact phrase matches only, default is false
  • ‘single_passage‘ — whether to extract single best passage only, default is false
  • ‘use_boundaries‘ — whether to extract passages by phrase boundaries setup in tokenizer
  • ‘weight_order‘ — whether to order best passages in document (default) or weight order

Returns false on failure. Returns an array of string excerpts on success.

[Source]

     # File lib/client.rb, line 706
706:     def BuildExcerpts(docs, index, words, opts = {})
707:       assert { docs.instance_of? Array }
708:       assert { index.instance_of? String }
709:       assert { words.instance_of? String }
710:       assert { opts.instance_of? Hash }
711: 
712:       sock = self.Connect
713:   
714:       # fixup options
715:       opts['before_match'] ||= '<b>';
716:       opts['after_match'] ||= '</b>';
717:       opts['chunk_separator'] ||= ' ... ';
718:       opts['limit'] ||= 256;
719:       opts['around'] ||= 5;
720:       opts['exact_phrase'] ||= false
721:       opts['single_passage'] ||= false
722:       opts['use_boundaries'] ||= false
723:       opts['weight_order'] ||= false
724:       
725:       # build request
726:       
727:       # v.1.0 req
728:       flags = 1
729:       flags |= 2  if opts['exact_phrase']
730:       flags |= 4  if opts['single_passage']
731:       flags |= 8  if opts['use_boundaries']
732:       flags |= 16 if opts['weight_order']
733:       
734:       req = [0, flags].pack('NN'); # mode=0, flags=1 (remove spaces)
735:       # req index
736:       req << [index.length].pack('N') + index
737:       # req words
738:       req << [words.length].pack('N') + words
739:   
740:       # options
741:       req << [opts['before_match'].length].pack('N') + opts['before_match']
742:       req << [opts['after_match'].length].pack('N') + opts['after_match']
743:       req << [opts['chunk_separator'].length].pack('N') + opts['chunk_separator']
744:       req << [opts['limit'].to_i, opts['around'].to_i].pack('NN')
745:       
746:       # documents
747:       req << [docs.size].pack('N');
748:       docs.each do |doc|
749:         assert { doc.instance_of? String }
750: 
751:         req << [doc.length].pack('N') + doc
752:       end
753:       
754:       # send query, get response
755:       len = req.length
756:       # add header
757:       req = [SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, len].pack('nnN') + req
758:       sock.send(req, 0)
759:       
760:       response = GetResponse(sock, VER_COMMAND_EXCERPT)
761:       
762:       # parse response
763:       p = 0
764:       res = []
765:       rlen = response.length
766:       docs.each do |doc|
767:         len = response[p, 4].unpack('N*').first; p += 4
768:         if p + len > rlen
769:           @error = 'incomplete reply'
770:           raise SphinxResponseError, @error
771:         end
772:         res << response[p, len] if len > 0
773:         p += len
774:       end
775:       return res
776:     end

Get last error message.

[Source]

     # File lib/client.rb, line 183
183:     def GetLastError
184:       @error
185:     end

Get last warning message.

[Source]

     # File lib/client.rb, line 188
188:     def GetLastWarning
189:       @warning
190:     end

index is index name (or names) to query. default value is "*" which means to query all indexes. Accepted characters for index names are letters, numbers, dash, and underscore; everything else is considered a separator. Therefore, all the following calls are valid and will search two indexes:

  sphinx.Query('test query', 'main delta')
  sphinx.Query('test query', 'main;delta')
  sphinx.Query('test query', 'main, delta')

Index order matters. If identical IDs are found in two or more indexes, weight and attribute values from the very last matching index will be used for sorting and returning to client. Therefore, in the example above, matches from "delta" index will always "win" over matches from "main".

Returns false on failure. Returns hash which has the following keys on success:

  • ‘matches‘ — array of hashes {‘weight’, ‘group’, ‘id’}, where ‘id’ is document_id.
  • ‘total‘ — total amount of matches retrieved (upto SPH_MAX_MATCHES, see sphinx.h)
  • ‘total_found‘ — total amount of matching documents in index
  • ‘time‘ — search time
  • ‘words‘ — hash which maps query terms (stemmed!) to (‘docs’, ‘hits’) hash

[Source]

     # File lib/client.rb, line 446
446:     def Query(query, index = '*')
447:       assert { @reqs.empty? }
448:       
449:       self.AddQuery(query, index)
450:       results = self.RunQueries
451:       
452:       # probably network error; error message should be already filled
453:       return false unless results.instance_of?(Array)
454:       
455:       @error = results[0]['error']
456:       @warning = results[0]['warning']
457:       
458:       return false if results[0]['status'] == SEARCHD_ERROR
459:       return results[0]
460:     end

Clear all filters (for multi-queries).

[Source]

     # File lib/client.rb, line 407
407:     def ResetFilters
408:       @filters = []
409:       @anchor = []
410:     end

Clear groupby settings.

[Source]

     # File lib/client.rb, line 413
413:     def ResetGroupBy
414:       @groupby       = ''
415:       @groupfunc     = SPH_GROUPBY_DAY
416:       @groupsort     = '@group desc'
417:       @groupdistinct = ''
418:     end

Run queries batch.

Returns an array of result sets on success. Returns false on network IO failure.

Each result set in returned array is a hash which containts the same keys as the hash returned by Query, plus:

  • ‘error‘ — search error for this query
  • ‘words‘ — hash which maps query terms (stemmed!) to ( "docs", "hits" ) hash

[Source]

     # File lib/client.rb, line 548
548:     def RunQueries
549:       if @reqs.empty?
550:         @error = 'No queries defined, issue AddQuery() first'
551:         return false
552:       end
553: 
554:       sock = self.Connect
555: 
556:       # send query, get response
557:       nreqs = @reqs.length
558:       req = @reqs.join('')
559:       len = 4 + req.length
560:       
561:       # add header
562:       req = [SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, len, nreqs].pack('nnNN') + req
563:       sock.send(req, 0)
564:       
565:       response = GetResponse(sock, VER_COMMAND_SEARCH)
566:       
567:       @reqs = []
568:       
569:       # parse response
570:       p = 0 # current position
571:       max = response.length # max position for checks, to protect against broken responses
572:       
573:       results = []
574:       
575:       ires = 0
576:       while ires < nreqs and p < max
577:         result = {}
578:         
579:         result['error'] = ''
580:         result['warning'] = ''
581:         
582:         # extract status
583:         
584:         status = result['status'] = response[p, 4].unpack('N*').first; p += 4
585:         if status != SEARCHD_OK
586:           len = response[p, 4].unpack('N*').first; p += 4
587:           message = response[p, len].unpack('N*').first; p += len
588:           
589:           if status == SEARCHD_WARNING
590:             result['warning'] = message
591:           else
592:             result['error'] = message
593:             next
594:           end
595:         end
596:     
597:         # read schema
598:         fields = []
599:         attrs = {}
600:         attrs_names_in_order = []
601:         
602:         nfields = response[p, 4].unpack('N*').first; p += 4
603:         while nfields > 0 and p < max
604:           nfields -= 1
605:           len = response[p, 4].unpack('N*').first; p += 4
606:           fields << response[p, len]; p += len
607:         end
608:         result['fields'] = fields
609:     
610:         nattrs = response[p, 4].unpack('N*').first; p += 4
611:         while nattrs > 0 && p < max
612:           nattrs -= 1
613:           len = response[p, 4].unpack('N*').first; p += 4
614:           attr = response[p, len]; p += len
615:           type = response[p, 4].unpack('N*').first; p += 4
616:           attrs[attr] = type
617:           attrs_names_in_order << attr
618:         end
619:         result['attrs'] = attrs
620:         
621:         # read match count
622:         count = response[p, 4].unpack('N*').first; p += 4
623:         id64 = response[p, 4].unpack('N*').first; p += 4
624:         
625:         # read matches
626:         result['matches'] = []
627:         while count > 0 and p < max
628:           count -= 1
629:           
630:           if id64 != 0
631:             dochi, doclo, weight = response[p, 12].unpack('N*N*N*'); p += 12
632:             doc = dochi << 32 + doclo
633:           else
634:             doc, weight = response[p, 8].unpack('N*N*'); p += 8
635:           end
636:     
637:           r = {} # This is a single result put in the result['matches'] array
638:           r['id'] = doc
639:           r['weight'] = weight
640:           attrs_names_in_order.each do |attr|
641:             r['attrs'] ||= {}
642: 
643:             # handle floats
644:             if attrs[attr] == SPH_ATTR_FLOAT
645:               uval = response[p, 4].unpack('N*').first; p += 4
646:               fval = ([uval].pack('L')).unpack.first
647:               r['attrs'][attr] = fval
648:             else
649:               # handle everything else as unsigned ints
650:               val = response[p, 4].unpack('N*').first; p += 4
651:               if (attrs[attr] & SPH_ATTR_MULTI) != 0
652:                 r['attrs'][attr] = []
653:                 nvalues = val
654:                 while nvalues > 0 and p < max
655:                   nvalues -= 1
656:                   val = response[p, 4].unpack('N*').first; p += 4
657:                   r['attrs'][attr] << val
658:                 end
659:               else
660:                 r['attrs'][attr] = val
661:               end
662:             end
663:           end
664:           result['matches'] << r
665:         end
666:         result['total'], result['total_found'], msecs, words = response[p, 16].unpack('N*N*N*N*'); p += 16
667:         result['time'] = '%.3f' % (msecs / 1000.0)
668: 
669:         result['words'] = {}
670:         while words > 0 and p < max
671:           words -= 1
672:           len = response[p, 4].unpack('N*').first; p += 4
673:           word = response[p, len]; p += len
674:           docs, hits = response[p, 8].unpack('N*N*'); p += 8
675:           result['words'][word] = { 'docs' => docs, 'hits' => hits }
676:         end
677:         
678:         results << result
679:         ires += 1
680:       end
681:       
682:       return results
683:     end

Set values filter.

Only match those records where attribute column values are in specified set.

[Source]

     # File lib/client.rb, line 282
282:     def SetFilter(attribute, values, exclude = false)
283:       assert { attribute.instance_of? String }
284:       assert { values.instance_of? Array }
285:       assert { !values.empty? }
286: 
287:       if values.instance_of?(Array) && values.size > 0
288:         values.each do |value|
289:           assert { value.instance_of? Fixnum }
290:         end
291:       
292:         @filters << { 'type' => SPH_FILTER_VALUES, 'attr' => attribute, 'exclude' => exclude, 'values' => values }
293:       end
294:     end

Set float range filter.

Only match those records where attribute column value is beetwen min and max (including min and max).

[Source]

     # File lib/client.rb, line 313
313:     def SetFilterFloatRange(attribute, min, max, exclude = false)
314:       assert { attribute.instance_of? String }
315:       assert { min.instance_of? Float }
316:       assert { max.instance_of? Float }
317:       assert { min <= max }
318:     
319:       @filters << { 'type' => SPH_FILTER_FLOATRANGE, 'attr' => attribute, 'exclude' => exclude, 'min' => min, 'max' => max }
320:     end

Set range filter.

Only match those records where attribute column value is beetwen min and max (including min and max).

[Source]

     # File lib/client.rb, line 300
300:     def SetFilterRange(attribute, min, max, exclude = false)
301:       assert { attribute.instance_of? String }
302:       assert { min.instance_of? Fixnum }
303:       assert { max.instance_of? Fixnum }
304:       assert { min <= max }
305:     
306:       @filters << { 'type' => SPH_FILTER_RANGE, 'attr' => attribute, 'exclude' => exclude, 'min' => min, 'max' => max }
307:     end

Setup geographical anchor point.

Required to use @geodist in filters and sorting distance will be computed to this point.

  • attrlat — is the name of latitude attribute
  • attrlong — is the name of longitude attribute
  • lat — is anchor point latitude, in radians
  • long — is anchor point longitude, in radians

[Source]

     # File lib/client.rb, line 331
331:     def SetGeoAnchor(attrlat, attrlong, lat, long)
332:       assert { attrlat.instance_of? String }
333:       assert { attrlong.instance_of? String }
334:       assert { lat.instance_of? Float }
335:       assert { long.instance_of? Float }
336: 
337:       @anchor = { 'attrlat' => attrlat, 'attrlong' => attrlong, 'lat' => lat, 'long' => long }
338:     end

Set grouping attribute and function.

In grouping mode, all matches are assigned to different groups based on grouping function value.

Each group keeps track of the total match count, and the best match (in this group) according to current sorting function.

The final result set contains one best match per group, with grouping function value and matches count attached.

Groups in result set could be sorted by any sorting clause, including both document attributes and the following special internal Sphinx attributes:

  • @id - match document ID;
  • @weight, @rank, @relevance - match weight;
  • @group - groupby function value;
  • @count - amount of matches in group.

the default mode is to sort by groupby value in descending order, ie. by ’@group desc’.

‘total_found’ would contain total amount of matching groups over the whole index.

WARNING: grouping is done in fixed memory and thus its results are only approximate; so there might be more groups reported in total_found than actually present. @count might also be underestimated.

For example, if sorting by relevance and grouping by "published" attribute with SPH_GROUPBY_DAY function, then the result set will contain one most relevant match per each day when there were any matches published, with day number and per-day match count attached, and sorted by day number in descending order (ie. recent days first).

[Source]

     # File lib/client.rb, line 376
376:     def SetGroupBy(attribute, func, groupsort = '@group desc')
377:       assert { attribute.instance_of? String }
378:       assert { groupsort.instance_of? String }
379:       assert { func == SPH_GROUPBY_DAY \
380:             || func == SPH_GROUPBY_WEEK \
381:             || func == SPH_GROUPBY_MONTH \
382:             || func == SPH_GROUPBY_YEAR \
383:             || func == SPH_GROUPBY_ATTR \
384:             || func == SPH_GROUPBY_ATTRPAIR }
385: 
386:       @groupby = attribute
387:       @groupfunc = func
388:       @groupsort = groupsort
389:     end

Set count-distinct attribute for group-by queries.

[Source]

     # File lib/client.rb, line 392
392:     def SetGroupDistinct(attribute)
393:       assert { attribute.instance_of? String }
394:       @groupdistinct = attribute
395:     end

Set IDs range to match.

Only match those records where document ID is beetwen min_id and max_id (including min_id and max_id).

[Source]

     # File lib/client.rb, line 269
269:     def SetIDRange(min, max)
270:       assert { min.instance_of? Fixnum }
271:       assert { max.instance_of? Fixnum }
272:       assert { min <= max }
273: 
274:       @min_id = min
275:       @max_id = max
276:     end

Set per-index weights.

[Source]

     # File lib/client.rb, line 255
255:     def SetIndexWeights(weights)
256:       assert { weights.instance_of? Hash }
257:       weights.each do |index, weight|
258:         assert { index.instance_of? String }
259:         assert { weight.instance_of? Fixnum }
260:       end
261:       
262:       @indexweights = weights
263:     end

Set offset and count into result set, and max-matches and cutoff to use while searching.

[Source]

     # File lib/client.rb, line 203
203:     def SetLimits(offset, limit, max = 0, cutoff = 0)
204:       assert { offset.instance_of? Fixnum }
205:       assert { limit.instance_of? Fixnum }
206:       assert { max.instance_of? Fixnum }
207:       assert { offset >= 0 }
208:       assert { limit > 0 }
209:       assert { max >= 0 }
210: 
211:       @offset = offset
212:       @limit = limit
213:       @maxmatches = max if max > 0
214:       @cutoff = cutoff if cutoff > 0
215:     end

Set match mode.

[Source]

     # File lib/client.rb, line 218
218:     def SetMatchMode(mode)
219:       assert { mode == SPH_MATCH_ALL \
220:             || mode == SPH_MATCH_ANY \
221:             || mode == SPH_MATCH_PHRASE \
222:             || mode == SPH_MATCH_BOOLEAN \
223:             || mode == SPH_MATCH_EXTENDED \
224:             || mode == SPH_MATCH_FULLSCAN \
225:             || mode == SPH_MATCH_EXTENDED2 }
226: 
227:       @mode = mode
228:     end

Set distributed retries count and delay.

[Source]

     # File lib/client.rb, line 398
398:     def SetRetries(count, delay = 0)
399:       assert { count.instance_of? Fixnum }
400:       assert { delay.instance_of? Fixnum }
401:       
402:       @retrycount = count
403:       @retrydelay = delay
404:     end

Set searchd server.

[Source]

     # File lib/client.rb, line 193
193:     def SetServer(host, port)
194:       assert { host.instance_of? String }
195:       assert { port.instance_of? Fixnum }
196: 
197:       @host = host
198:       @port = port
199:     end

Set matches sorting mode.

[Source]

     # File lib/client.rb, line 231
231:     def SetSortMode(mode, sortby = '')
232:       assert { mode == SPH_SORT_RELEVANCE \
233:             || mode == SPH_SORT_ATTR_DESC \
234:             || mode == SPH_SORT_ATTR_ASC \
235:             || mode == SPH_SORT_TIME_SEGMENTS \
236:             || mode == SPH_SORT_EXTENDED }
237:       assert { sortby.instance_of? String }
238:       assert { mode == SPH_SORT_RELEVANCE || !sortby.empty? }
239: 
240:       @sort = mode
241:       @sortby = sortby
242:     end

Set per-field weights.

[Source]

     # File lib/client.rb, line 245
245:     def SetWeights(weights)
246:       assert { weights.instance_of? Array }
247:       weights.each do |weight|
248:         assert { weight.instance_of? Fixnum }
249:       end
250: 
251:       @weights = weights
252:     end

Attribute updates

Update specified attributes on specified documents.

  • index is a name of the index to be updated
  • attrs is an array of attribute name strings.
  • values 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] })

[Source]

     # File lib/client.rb, line 792
792:     def UpdateAttributes(index, attrs, values)
793:       # verify everything
794:       assert { index.instance_of? String }
795:       
796:       assert { attrs.instance_of? Array }
797:       attrs.each do |attr|
798:         assert { attr.instance_of? String }
799:       end
800:       
801:       assert { values.instance_of? Hash }
802:       values.each do |id, entry|
803:         assert { id.instance_of? Fixnum }
804:         assert { entry.instance_of? Array }
805:         assert { entry.length == attrs.length }
806:         entry.each do |v|
807:           assert { v.instance_of? Fixnum }
808:         end
809:       end
810:       
811:       # build request
812:       req = [index.length].pack('N') + index
813:       
814:       req << [attrs.length].pack('N')
815:       attrs.each do |attr|
816:         req << [attr.length].pack('N') + attr
817:       end
818:       
819:       req << [values.length].pack('N')
820:       values.each do |id, entry|
821:         req << [id].pack('N')
822:         req << entry.pack('N' * entry.length)
823:       end
824:       
825:       # connect, send query, get response
826:       sock = self.Connect
827:       len = req.length
828:       req = [SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, len].pack('nnN') + req # add header
829:       sock.send(req, 0)
830:       
831:       response = self.GetResponse(sock, VER_COMMAND_UPDATE)
832:       
833:       # parse response
834:       response[0, 4].unpack('N*').first
835:     end

Protected Instance methods

Connect to searchd server.

[Source]

     # File lib/client.rb, line 840
840:       def Connect
841:         begin
842:           sock = TCPSocket.new(@host, @port)
843:         rescue
844:           @error = "connection to #{@host}:#{@port} failed"
845:           raise SphinxConnectError, @error
846:         end
847:         
848:         v = sock.recv(4).unpack('N*').first
849:         if v < 1
850:           sock.close
851:           @error = "expected searchd protocol version 1+, got version '#{v}'"
852:           raise SphinxConnectError, @error
853:         end
854:         
855:         sock.send([1].pack('N'), 0)
856:         sock
857:       end

Get and check response packet from searchd server.

[Source]

     # File lib/client.rb, line 860
860:       def GetResponse(sock, client_version)
861:         response = ''
862:         len = 0
863:         
864:         header = sock.recv(8)
865:         if header.length == 8
866:           status, ver, len = header.unpack('n2N')
867:           left = len.to_i
868:           while left > 0 do
869:             begin
870:               chunk = sock.recv(left)
871:               if chunk
872:                 response << chunk
873:                 left -= chunk.length
874:               end
875:             rescue EOFError
876:               break
877:             end
878:           end
879:         end
880:         sock.close
881:     
882:         # check response
883:         read = response.length
884:         if response.empty? or read != len.to_i
885:           @error = len \
886:             ? "failed to read searchd response (status=#{status}, ver=#{ver}, len=#{len}, read=#{read})" \
887:             : 'received zero-sized searchd response'
888:           raise SphinxResponseError, @error
889:         end
890:         
891:         # check status
892:         if (status == SEARCHD_WARNING)
893:           wlen = response[0, 4].unpack('N*').first
894:           @warning = response[4, wlen]
895:           return response[4 + wlen, response.length - 4 - wlen]
896:         end
897: 
898:         if status == SEARCHD_ERROR
899:           @error = 'searchd error: ' + response[4, response.length - 4]
900:           raise SphinxInternalError, @error
901:         end
902:     
903:         if status == SEARCHD_RETRY
904:           @error = 'temporary searchd error: ' + response[4, response.length - 4]
905:           raise SphinxTemporaryError, @error
906:         end
907:     
908:         unless status == SEARCHD_OK
909:           @error = "unknown status code: '#{status}'"
910:           raise SphinxUnknownError, @error
911:         end
912:         
913:         # check version
914:         if ver < client_version
915:           @warning = "searchd command v.#{ver >> 8}.#{ver & 0xff} older than client's " +
916:             "v.#{client_version >> 8}.#{client_version & 0xff}, some options might not work"
917:         end
918:         
919:         return response
920:       end

[Source]

     # File lib/client.rb, line 922
922:       def PackFloat(f)
923:         t1 = [f].pack('f') # machine order
924:         t2 = t1.unpack('L*').first # int in machine order
925:         [t2].pack('N')
926:       end

[Validate]