riddle 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README.textile +1 -0
  2. data/lib/riddle.rb +30 -4
  3. data/lib/riddle/0.9.8.rb +1 -0
  4. data/lib/riddle/0.9.9.rb +2 -0
  5. data/lib/riddle/auto_version.rb +11 -0
  6. data/lib/riddle/client.rb +2 -0
  7. data/lib/riddle/configuration.rb +3 -1
  8. data/lib/riddle/configuration/sql_source.rb +13 -0
  9. data/lib/riddle/controller.rb +29 -2
  10. data/spec/functional/excerpt_spec.rb +2 -2
  11. data/spec/functional/status_spec.rb +1 -1
  12. data/spec/riddle/auto_version_spec.rb +24 -0
  13. data/spec/riddle/client_spec.rb +11 -0
  14. data/spec/riddle/configuration_spec.rb +11 -0
  15. data/spec/riddle/controller_spec.rb +29 -0
  16. data/spec/riddle_spec.rb +27 -0
  17. data/spec/spec_helper.rb +1 -4
  18. data/spec/unit/client_spec.rb +1 -1
  19. data/spec/unit/configuration/searchd_spec.rb +2 -2
  20. data/spec/unit/configuration/sql_source_spec.rb +21 -0
  21. metadata +8 -48
  22. data/spec/fixtures/data/anchor.bin +0 -0
  23. data/spec/fixtures/data/any.bin +0 -0
  24. data/spec/fixtures/data/boolean.bin +0 -0
  25. data/spec/fixtures/data/comment.bin +0 -0
  26. data/spec/fixtures/data/distinct.bin +0 -0
  27. data/spec/fixtures/data/field_weights.bin +0 -0
  28. data/spec/fixtures/data/filter.bin +0 -0
  29. data/spec/fixtures/data/filter_array.bin +0 -0
  30. data/spec/fixtures/data/filter_array_exclude.bin +0 -0
  31. data/spec/fixtures/data/filter_boolean.bin +0 -0
  32. data/spec/fixtures/data/filter_floats.bin +0 -0
  33. data/spec/fixtures/data/filter_floats_exclude.bin +0 -0
  34. data/spec/fixtures/data/filter_range.bin +0 -0
  35. data/spec/fixtures/data/filter_range_exclude.bin +0 -0
  36. data/spec/fixtures/data/group.bin +0 -0
  37. data/spec/fixtures/data/index.bin +0 -0
  38. data/spec/fixtures/data/index_weights.bin +0 -0
  39. data/spec/fixtures/data/keywords_with_hits.bin +0 -0
  40. data/spec/fixtures/data/keywords_without_hits.bin +0 -0
  41. data/spec/fixtures/data/overrides.bin +0 -0
  42. data/spec/fixtures/data/phrase.bin +0 -0
  43. data/spec/fixtures/data/rank_mode.bin +0 -0
  44. data/spec/fixtures/data/select.bin +0 -0
  45. data/spec/fixtures/data/simple.bin +0 -0
  46. data/spec/fixtures/data/sort.bin +0 -0
  47. data/spec/fixtures/data/update_simple.bin +0 -0
  48. data/spec/fixtures/data/weights.bin +0 -0
  49. data/spec/fixtures/data_generator.0.9.8.php +0 -208
  50. data/spec/fixtures/data_generator.0.9.9.php +0 -225
  51. data/spec/fixtures/sphinx/configuration.erb +0 -38
  52. data/spec/fixtures/sphinx/people.spa +0 -0
  53. data/spec/fixtures/sphinx/people.spd +0 -0
  54. data/spec/fixtures/sphinx/people.sph +0 -0
  55. data/spec/fixtures/sphinx/people.spi +0 -0
  56. data/spec/fixtures/sphinx/people.spk +0 -0
  57. data/spec/fixtures/sphinx/people.spm +0 -0
  58. data/spec/fixtures/sphinx/people.spp +0 -0
  59. data/spec/fixtures/sphinx/searchd.log +0 -4430
  60. data/spec/fixtures/sphinx/searchd.query.log +0 -1234
  61. data/spec/fixtures/sphinx/spec.conf +0 -38
  62. data/spec/fixtures/sphinxapi.0.9.8.php +0 -1228
  63. data/spec/fixtures/sphinxapi.0.9.9.php +0 -1646
  64. data/spec/fixtures/sql/conf.example.yml +0 -3
  65. data/spec/fixtures/sql/conf.yml +0 -3
  66. data/spec/fixtures/sql/data.sql +0 -25000
  67. data/spec/fixtures/sql/structure.sql +0 -16
@@ -1,38 +0,0 @@
1
- indexer
2
- {
3
- mem_limit = 64M
4
- }
5
-
6
- searchd
7
- {
8
- port = 3313
9
- log = /Users/pat/Code/ruby/riddle/spec/fixtures/sphinx/searchd.log
10
- query_log = /Users/pat/Code/ruby/riddle/spec/fixtures/sphinx/searchd.query.log
11
- read_timeout = 5
12
- max_children = 30
13
- pid_file = /Users/pat/Code/ruby/riddle/spec/fixtures/sphinx/searchd.pid
14
- }
15
-
16
- source peoples
17
- {
18
- type = mysql
19
- sql_host = localhost
20
- sql_user = riddle
21
- sql_pass = riddle
22
- sql_db = riddle
23
-
24
- sql_query = SELECT id, first_name, middle_initial, last_name, gender, street_address, city, state, postcode, email, UNIX_TIMESTAMP(birthday) AS birthday FROM people WHERE id >= $start AND id <= $end
25
- sql_query_range = SELECT MIN(id), MAX(id) FROM people
26
- sql_query_info = SELECT * FROM people WHERE id = $id
27
- sql_attr_timestamp = birthday
28
- }
29
-
30
- index people
31
- {
32
- source = peoples
33
- morphology = stem_en
34
- path = /Users/pat/Code/ruby/riddle/spec/fixtures/sphinx/people
35
- charset_type = utf-8
36
- enable_star = 1
37
- min_prefix_len = 1
38
- }
@@ -1,1228 +0,0 @@
1
- <?php
2
-
3
- //
4
- // $Id: sphinxapi.php 1418 2008-08-28 15:30:05Z shodan $
5
- //
6
-
7
- //
8
- // Copyright (c) 2001-2008, Andrew Aksyonoff. All rights reserved.
9
- //
10
- // This program is free software; you can redistribute it and/or modify
11
- // it under the terms of the GNU General Public License. You should have
12
- // received a copy of the GPL license along with this program; if you
13
- // did not, you can find it at http://www.gnu.org/
14
- //
15
-
16
- /////////////////////////////////////////////////////////////////////////////
17
- // PHP version of Sphinx searchd client (PHP API)
18
- /////////////////////////////////////////////////////////////////////////////
19
-
20
- /// known searchd commands
21
- define ( "SEARCHD_COMMAND_SEARCH", 0 );
22
- define ( "SEARCHD_COMMAND_EXCERPT", 1 );
23
- define ( "SEARCHD_COMMAND_UPDATE", 2 );
24
- define ( "SEARCHD_COMMAND_KEYWORDS",3 );
25
-
26
- /// current client-side command implementation versions
27
- define ( "VER_COMMAND_SEARCH", 0x113 );
28
- define ( "VER_COMMAND_EXCERPT", 0x100 );
29
- define ( "VER_COMMAND_UPDATE", 0x101 );
30
- define ( "VER_COMMAND_KEYWORDS", 0x100 );
31
-
32
- /// known searchd status codes
33
- define ( "SEARCHD_OK", 0 );
34
- define ( "SEARCHD_ERROR", 1 );
35
- define ( "SEARCHD_RETRY", 2 );
36
- define ( "SEARCHD_WARNING", 3 );
37
-
38
- /// known match modes
39
- define ( "SPH_MATCH_ALL", 0 );
40
- define ( "SPH_MATCH_ANY", 1 );
41
- define ( "SPH_MATCH_PHRASE", 2 );
42
- define ( "SPH_MATCH_BOOLEAN", 3 );
43
- define ( "SPH_MATCH_EXTENDED", 4 );
44
- define ( "SPH_MATCH_FULLSCAN", 5 );
45
- define ( "SPH_MATCH_EXTENDED2", 6 ); // extended engine V2 (TEMPORARY, WILL BE REMOVED)
46
-
47
- /// known ranking modes (ext2 only)
48
- define ( "SPH_RANK_PROXIMITY_BM25", 0 ); ///< default mode, phrase proximity major factor and BM25 minor one
49
- define ( "SPH_RANK_BM25", 1 ); ///< statistical mode, BM25 ranking only (faster but worse quality)
50
- define ( "SPH_RANK_NONE", 2 ); ///< no ranking, all matches get a weight of 1
51
- define ( "SPH_RANK_WORDCOUNT", 3 ); ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
52
-
53
- /// known sort modes
54
- define ( "SPH_SORT_RELEVANCE", 0 );
55
- define ( "SPH_SORT_ATTR_DESC", 1 );
56
- define ( "SPH_SORT_ATTR_ASC", 2 );
57
- define ( "SPH_SORT_TIME_SEGMENTS", 3 );
58
- define ( "SPH_SORT_EXTENDED", 4 );
59
- define ( "SPH_SORT_EXPR", 5 );
60
-
61
- /// known filter types
62
- define ( "SPH_FILTER_VALUES", 0 );
63
- define ( "SPH_FILTER_RANGE", 1 );
64
- define ( "SPH_FILTER_FLOATRANGE", 2 );
65
-
66
- /// known attribute types
67
- define ( "SPH_ATTR_INTEGER", 1 );
68
- define ( "SPH_ATTR_TIMESTAMP", 2 );
69
- define ( "SPH_ATTR_ORDINAL", 3 );
70
- define ( "SPH_ATTR_BOOL", 4 );
71
- define ( "SPH_ATTR_FLOAT", 5 );
72
- define ( "SPH_ATTR_MULTI", 0x40000000 );
73
-
74
- /// known grouping functions
75
- define ( "SPH_GROUPBY_DAY", 0 );
76
- define ( "SPH_GROUPBY_WEEK", 1 );
77
- define ( "SPH_GROUPBY_MONTH", 2 );
78
- define ( "SPH_GROUPBY_YEAR", 3 );
79
- define ( "SPH_GROUPBY_ATTR", 4 );
80
- define ( "SPH_GROUPBY_ATTRPAIR", 5 );
81
-
82
-
83
- /// portably pack numeric to 64 unsigned bits, network order
84
- function sphPack64 ( $v )
85
- {
86
- assert ( is_numeric($v) );
87
-
88
- // x64 route
89
- if ( PHP_INT_SIZE>=8 )
90
- {
91
- $i = (int)$v;
92
- return pack ( "NN", $i>>32, $i&((1<<32)-1) );
93
- }
94
-
95
- // x32 route, bcmath
96
- $x = "4294967296";
97
- if ( function_exists("bcmul") )
98
- {
99
- $h = bcdiv ( $v, $x, 0 );
100
- $l = bcmod ( $v, $x );
101
- return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
102
- }
103
-
104
- // x32 route, 15 or less decimal digits
105
- // we can use float, because its actually double and has 52 precision bits
106
- if ( strlen($v)<=15 )
107
- {
108
- $f = (float)$v;
109
- $h = (int)($f/$x);
110
- $l = (int)($f-$x*$h);
111
- return pack ( "NN", $h, $l );
112
- }
113
-
114
- // x32 route, 16 or more decimal digits
115
- // well, let me know if you *really* need this
116
- die ( "INTERNAL ERROR: packing more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" );
117
- }
118
-
119
-
120
- /// portably unpack 64 unsigned bits, network order to numeric
121
- function sphUnpack64 ( $v )
122
- {
123
- list($h,$l) = array_values ( unpack ( "N*N*", $v ) );
124
-
125
- // x64 route
126
- if ( PHP_INT_SIZE>=8 )
127
- {
128
- if ( $h<0 ) $h += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
129
- if ( $l<0 ) $l += (1<<32);
130
- return ($h<<32) + $l;
131
- }
132
-
133
- // x32 route
134
- $h = sprintf ( "%u", $h );
135
- $l = sprintf ( "%u", $l );
136
- $x = "4294967296";
137
-
138
- // bcmath
139
- if ( function_exists("bcmul") )
140
- return bcadd ( $l, bcmul ( $x, $h ) );
141
-
142
- // no bcmath, 15 or less decimal digits
143
- // we can use float, because its actually double and has 52 precision bits
144
- if ( $h<1048576 )
145
- {
146
- $f = ((float)$h)*$x + (float)$l;
147
- return sprintf ( "%.0f", $f ); // builtin conversion is only about 39-40 bits precise!
148
- }
149
-
150
- // x32 route, 16 or more decimal digits
151
- // well, let me know if you *really* need this
152
- die ( "INTERNAL ERROR: unpacking more than 15-digit numeric on 32-bit PHP is not implemented yet (contact support)" );
153
- }
154
-
155
-
156
- /// sphinx searchd client class
157
- class SphinxClient
158
- {
159
- var $_host; ///< searchd host (default is "localhost")
160
- var $_port; ///< searchd port (default is 3312)
161
- var $_offset; ///< how many records to seek from result-set start (default is 0)
162
- var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
163
- var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
164
- var $_weights; ///< per-field weights (default is 1 for all fields)
165
- var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
166
- var $_sortby; ///< attribute to sort by (defualt is "")
167
- var $_min_id; ///< min ID to match (default is 0, which means no limit)
168
- var $_max_id; ///< max ID to match (default is 0, which means no limit)
169
- var $_filters; ///< search filters
170
- var $_groupby; ///< group-by attribute name
171
- var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
172
- var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
173
- var $_groupdistinct;///< group-by count-distinct attribute
174
- var $_maxmatches; ///< max matches to retrieve
175
- var $_cutoff; ///< cutoff to stop searching at (default is 0)
176
- var $_retrycount; ///< distributed retries count
177
- var $_retrydelay; ///< distributed retries delay
178
- var $_anchor; ///< geographical anchor point
179
- var $_indexweights; ///< per-index weights
180
- var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
181
- var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
182
- var $_fieldweights; ///< per-field-name weights
183
-
184
- var $_error; ///< last error message
185
- var $_warning; ///< last warning message
186
-
187
- var $_reqs; ///< requests array for multi-query
188
- var $_mbenc; ///< stored mbstring encoding
189
- var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
190
- var $_timeout; ///< connect timeout
191
-
192
- /////////////////////////////////////////////////////////////////////////////
193
- // common stuff
194
- /////////////////////////////////////////////////////////////////////////////
195
-
196
- /// create a new client object and fill defaults
197
- function SphinxClient ()
198
- {
199
- // per-client-object settings
200
- $this->_host = "localhost";
201
- $this->_port = 3312;
202
-
203
- // per-query settings
204
- $this->_offset = 0;
205
- $this->_limit = 20;
206
- $this->_mode = SPH_MATCH_ALL;
207
- $this->_weights = array ();
208
- $this->_sort = SPH_SORT_RELEVANCE;
209
- $this->_sortby = "";
210
- $this->_min_id = 0;
211
- $this->_max_id = 0;
212
- $this->_filters = array ();
213
- $this->_groupby = "";
214
- $this->_groupfunc = SPH_GROUPBY_DAY;
215
- $this->_groupsort = "@group desc";
216
- $this->_groupdistinct= "";
217
- $this->_maxmatches = 1000;
218
- $this->_cutoff = 0;
219
- $this->_retrycount = 0;
220
- $this->_retrydelay = 0;
221
- $this->_anchor = array ();
222
- $this->_indexweights= array ();
223
- $this->_ranker = SPH_RANK_PROXIMITY_BM25;
224
- $this->_maxquerytime= 0;
225
- $this->_fieldweights= array();
226
-
227
- $this->_error = ""; // per-reply fields (for single-query case)
228
- $this->_warning = "";
229
- $this->_reqs = array (); // requests storage (for multi-query case)
230
- $this->_mbenc = "";
231
- $this->_arrayresult = false;
232
- $this->_timeout = 0;
233
- }
234
-
235
- /// get last error message (string)
236
- function GetLastError ()
237
- {
238
- return $this->_error;
239
- }
240
-
241
- /// get last warning message (string)
242
- function GetLastWarning ()
243
- {
244
- return $this->_warning;
245
- }
246
-
247
- /// set searchd host name (string) and port (integer)
248
- function SetServer ( $host, $port )
249
- {
250
- assert ( is_string($host) );
251
- assert ( is_int($port) );
252
- $this->_host = $host;
253
- $this->_port = $port;
254
- }
255
-
256
- /// set server connection timeout (0 to remove)
257
- function SetConnectTimeout ( $timeout )
258
- {
259
- assert ( is_numeric($timeout) );
260
- $this->_timeout = $timeout;
261
- }
262
-
263
- /////////////////////////////////////////////////////////////////////////////
264
-
265
- /// enter mbstring workaround mode
266
- function _MBPush ()
267
- {
268
- $this->_mbenc = "";
269
- if ( ini_get ( "mbstring.func_overload" ) & 2 )
270
- {
271
- $this->_mbenc = mb_internal_encoding();
272
- mb_internal_encoding ( "latin1" );
273
- }
274
- }
275
-
276
- /// leave mbstring workaround mode
277
- function _MBPop ()
278
- {
279
- if ( $this->_mbenc )
280
- mb_internal_encoding ( $this->_mbenc );
281
- }
282
-
283
- /// connect to searchd server
284
- function _Connect ()
285
- {
286
- $errno = 0;
287
- $errstr = "";
288
- if ( $this->_timeout<=0 )
289
- $fp = @fsockopen ( $this->_host, $this->_port, $errno, $errstr );
290
- else
291
- $fp = @fsockopen ( $this->_host, $this->_port, $errno, $errstr, $this->_timeout );
292
-
293
- if ( !$fp )
294
- {
295
- $errstr = trim ( $errstr );
296
- $this->_error = "connection to {$this->_host}:{$this->_port} failed (errno=$errno, msg=$errstr)";
297
- return false;
298
- }
299
-
300
- // check version
301
- list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
302
- $v = (int)$v;
303
- if ( $v<1 )
304
- {
305
- fclose ( $fp );
306
- $this->_error = "expected searchd protocol version 1+, got version '$v'";
307
- return false;
308
- }
309
-
310
- // all ok, send my version
311
- fwrite ( $fp, pack ( "N", 1 ) );
312
- return $fp;
313
- }
314
-
315
- /// get and check response packet from searchd server
316
- function _GetResponse ( $fp, $client_ver )
317
- {
318
- $response = "";
319
- $len = 0;
320
-
321
- $header = fread ( $fp, 8 );
322
- if ( strlen($header)==8 )
323
- {
324
- list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
325
- $left = $len;
326
- while ( $left>0 && !feof($fp) )
327
- {
328
- $chunk = fread ( $fp, $left );
329
- if ( $chunk )
330
- {
331
- $response .= $chunk;
332
- $left -= strlen($chunk);
333
- }
334
- }
335
- }
336
- fclose ( $fp );
337
-
338
- // check response
339
- $read = strlen ( $response );
340
- if ( !$response || $read!=$len )
341
- {
342
- $this->_error = $len
343
- ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
344
- : "received zero-sized searchd response";
345
- return false;
346
- }
347
-
348
- // check status
349
- if ( $status==SEARCHD_WARNING )
350
- {
351
- list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
352
- $this->_warning = substr ( $response, 4, $wlen );
353
- return substr ( $response, 4+$wlen );
354
- }
355
- if ( $status==SEARCHD_ERROR )
356
- {
357
- $this->_error = "searchd error: " . substr ( $response, 4 );
358
- return false;
359
- }
360
- if ( $status==SEARCHD_RETRY )
361
- {
362
- $this->_error = "temporary searchd error: " . substr ( $response, 4 );
363
- return false;
364
- }
365
- if ( $status!=SEARCHD_OK )
366
- {
367
- $this->_error = "unknown status code '$status'";
368
- return false;
369
- }
370
-
371
- // check version
372
- if ( $ver<$client_ver )
373
- {
374
- $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
375
- $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
376
- }
377
-
378
- return $response;
379
- }
380
-
381
- /////////////////////////////////////////////////////////////////////////////
382
- // searching
383
- /////////////////////////////////////////////////////////////////////////////
384
-
385
- /// set offset and count into result set,
386
- /// and optionally set max-matches and cutoff limits
387
- function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
388
- {
389
- assert ( is_int($offset) );
390
- assert ( is_int($limit) );
391
- assert ( $offset>=0 );
392
- assert ( $limit>0 );
393
- assert ( $max>=0 );
394
- $this->_offset = $offset;
395
- $this->_limit = $limit;
396
- if ( $max>0 )
397
- $this->_maxmatches = $max;
398
- if ( $cutoff>0 )
399
- $this->_cutoff = $cutoff;
400
- }
401
-
402
- /// set maximum query time, in milliseconds, per-index
403
- /// integer, 0 means "do not limit"
404
- function SetMaxQueryTime ( $max )
405
- {
406
- assert ( is_int($max) );
407
- assert ( $max>=0 );
408
- $this->_maxquerytime = $max;
409
- }
410
-
411
- /// set matching mode
412
- function SetMatchMode ( $mode )
413
- {
414
- assert ( $mode==SPH_MATCH_ALL
415
- || $mode==SPH_MATCH_ANY
416
- || $mode==SPH_MATCH_PHRASE
417
- || $mode==SPH_MATCH_BOOLEAN
418
- || $mode==SPH_MATCH_EXTENDED
419
- || $mode==SPH_MATCH_FULLSCAN
420
- || $mode==SPH_MATCH_EXTENDED2 );
421
- $this->_mode = $mode;
422
- }
423
-
424
- /// set ranking mode
425
- function SetRankingMode ( $ranker )
426
- {
427
- assert ( $ranker==SPH_RANK_PROXIMITY_BM25
428
- || $ranker==SPH_RANK_BM25
429
- || $ranker==SPH_RANK_NONE
430
- || $ranker==SPH_RANK_WORDCOUNT );
431
- $this->_ranker = $ranker;
432
- }
433
-
434
- /// set matches sorting mode
435
- function SetSortMode ( $mode, $sortby="" )
436
- {
437
- assert (
438
- $mode==SPH_SORT_RELEVANCE ||
439
- $mode==SPH_SORT_ATTR_DESC ||
440
- $mode==SPH_SORT_ATTR_ASC ||
441
- $mode==SPH_SORT_TIME_SEGMENTS ||
442
- $mode==SPH_SORT_EXTENDED ||
443
- $mode==SPH_SORT_EXPR );
444
- assert ( is_string($sortby) );
445
- assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
446
-
447
- $this->_sort = $mode;
448
- $this->_sortby = $sortby;
449
- }
450
-
451
- /// bind per-field weights by order
452
- /// DEPRECATED; use SetFieldWeights() instead
453
- function SetWeights ( $weights )
454
- {
455
- assert ( is_array($weights) );
456
- foreach ( $weights as $weight )
457
- assert ( is_int($weight) );
458
-
459
- $this->_weights = $weights;
460
- }
461
-
462
- /// bind per-field weights by name
463
- function SetFieldWeights ( $weights )
464
- {
465
- assert ( is_array($weights) );
466
- foreach ( $weights as $name=>$weight )
467
- {
468
- assert ( is_string($name) );
469
- assert ( is_int($weight) );
470
- }
471
- $this->_fieldweights = $weights;
472
- }
473
-
474
- /// bind per-index weights by name
475
- function SetIndexWeights ( $weights )
476
- {
477
- assert ( is_array($weights) );
478
- foreach ( $weights as $index=>$weight )
479
- {
480
- assert ( is_string($index) );
481
- assert ( is_int($weight) );
482
- }
483
- $this->_indexweights = $weights;
484
- }
485
-
486
- /// set IDs range to match
487
- /// only match records if document ID is beetwen $min and $max (inclusive)
488
- function SetIDRange ( $min, $max )
489
- {
490
- assert ( is_numeric($min) );
491
- assert ( is_numeric($max) );
492
- assert ( $min<=$max );
493
- $this->_min_id = $min;
494
- $this->_max_id = $max;
495
- }
496
-
497
- /// set values set filter
498
- /// only match records where $attribute value is in given set
499
- function SetFilter ( $attribute, $values, $exclude=false )
500
- {
501
- assert ( is_string($attribute) );
502
- assert ( is_array($values) );
503
- assert ( count($values) );
504
-
505
- if ( is_array($values) && count($values) )
506
- {
507
- foreach ( $values as $value )
508
- assert ( is_numeric($value) );
509
-
510
- $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
511
- }
512
- }
513
-
514
- /// set range filter
515
- /// only match records if $attribute value is beetwen $min and $max (inclusive)
516
- function SetFilterRange ( $attribute, $min, $max, $exclude=false )
517
- {
518
- assert ( is_string($attribute) );
519
- assert ( is_int($min) );
520
- assert ( is_int($max) );
521
- assert ( $min<=$max );
522
-
523
- $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
524
- }
525
-
526
- /// set float range filter
527
- /// only match records if $attribute value is beetwen $min and $max (inclusive)
528
- function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
529
- {
530
- assert ( is_string($attribute) );
531
- assert ( is_float($min) );
532
- assert ( is_float($max) );
533
- assert ( $min<=$max );
534
-
535
- $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
536
- }
537
-
538
- /// setup anchor point for geosphere distance calculations
539
- /// required to use @geodist in filters and sorting
540
- /// latitude and longitude must be in radians
541
- function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long )
542
- {
543
- assert ( is_string($attrlat) );
544
- assert ( is_string($attrlong) );
545
- assert ( is_float($lat) );
546
- assert ( is_float($long) );
547
-
548
- $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long );
549
- }
550
-
551
- /// set grouping attribute and function
552
- function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
553
- {
554
- assert ( is_string($attribute) );
555
- assert ( is_string($groupsort) );
556
- assert ( $func==SPH_GROUPBY_DAY
557
- || $func==SPH_GROUPBY_WEEK
558
- || $func==SPH_GROUPBY_MONTH
559
- || $func==SPH_GROUPBY_YEAR
560
- || $func==SPH_GROUPBY_ATTR
561
- || $func==SPH_GROUPBY_ATTRPAIR );
562
-
563
- $this->_groupby = $attribute;
564
- $this->_groupfunc = $func;
565
- $this->_groupsort = $groupsort;
566
- }
567
-
568
- /// set count-distinct attribute for group-by queries
569
- function SetGroupDistinct ( $attribute )
570
- {
571
- assert ( is_string($attribute) );
572
- $this->_groupdistinct = $attribute;
573
- }
574
-
575
- /// set distributed retries count and delay
576
- function SetRetries ( $count, $delay=0 )
577
- {
578
- assert ( is_int($count) && $count>=0 );
579
- assert ( is_int($delay) && $delay>=0 );
580
- $this->_retrycount = $count;
581
- $this->_retrydelay = $delay;
582
- }
583
-
584
- /// set result set format (hash or array; hash by default)
585
- /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
586
- function SetArrayResult ( $arrayresult )
587
- {
588
- assert ( is_bool($arrayresult) );
589
- $this->_arrayresult = $arrayresult;
590
- }
591
-
592
- //////////////////////////////////////////////////////////////////////////////
593
-
594
- /// clear all filters (for multi-queries)
595
- function ResetFilters ()
596
- {
597
- $this->_filters = array();
598
- $this->_anchor = array();
599
- }
600
-
601
- /// clear groupby settings (for multi-queries)
602
- function ResetGroupBy ()
603
- {
604
- $this->_groupby = "";
605
- $this->_groupfunc = SPH_GROUPBY_DAY;
606
- $this->_groupsort = "@group desc";
607
- $this->_groupdistinct= "";
608
- }
609
-
610
- //////////////////////////////////////////////////////////////////////////////
611
-
612
- /// connect to searchd server, run given search query through given indexes,
613
- /// and return the search results
614
- function Query ( $query, $index="*", $comment="" )
615
- {
616
- assert ( empty($this->_reqs) );
617
-
618
- $this->AddQuery ( $query, $index, $comment );
619
- $results = $this->RunQueries ();
620
- $this->_reqs = array (); // just in case it failed too early
621
-
622
- if ( !is_array($results) )
623
- return false; // probably network error; error message should be already filled
624
-
625
- $this->_error = $results[0]["error"];
626
- $this->_warning = $results[0]["warning"];
627
- if ( $results[0]["status"]==SEARCHD_ERROR )
628
- return false;
629
- else
630
- return $results[0];
631
- }
632
-
633
- /// helper to pack floats in network byte order
634
- function _PackFloat ( $f )
635
- {
636
- $t1 = pack ( "f", $f ); // machine order
637
- list(,$t2) = unpack ( "L*", $t1 ); // int in machine order
638
- return pack ( "N", $t2 );
639
- }
640
-
641
- /// add query to multi-query batch
642
- /// returns index into results array from RunQueries() call
643
- function AddQuery ( $query, $index="*", $comment="" )
644
- {
645
- // mbstring workaround
646
- $this->_MBPush ();
647
-
648
- // build request
649
- $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits
650
- $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
651
- $req .= pack ( "N", strlen($query) ) . $query; // query itself
652
- $req .= pack ( "N", count($this->_weights) ); // weights
653
- foreach ( $this->_weights as $weight )
654
- $req .= pack ( "N", (int)$weight );
655
- $req .= pack ( "N", strlen($index) ) . $index; // indexes
656
- $req .= pack ( "N", 1 ); // id64 range marker
657
- $req .= sphPack64 ( $this->_min_id ) . sphPack64 ( $this->_max_id ); // id64 range
658
-
659
- // filters
660
- $req .= pack ( "N", count($this->_filters) );
661
- foreach ( $this->_filters as $filter )
662
- {
663
- $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
664
- $req .= pack ( "N", $filter["type"] );
665
- switch ( $filter["type"] )
666
- {
667
- case SPH_FILTER_VALUES:
668
- $req .= pack ( "N", count($filter["values"]) );
669
- foreach ( $filter["values"] as $value )
670
- $req .= pack ( "N", floatval($value) ); // this uberhack is to workaround 32bit signed int limit on x32 platforms
671
- break;
672
-
673
- case SPH_FILTER_RANGE:
674
- $req .= pack ( "NN", $filter["min"], $filter["max"] );
675
- break;
676
-
677
- case SPH_FILTER_FLOATRANGE:
678
- $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
679
- break;
680
-
681
- default:
682
- assert ( 0 && "internal error: unhandled filter type" );
683
- }
684
- $req .= pack ( "N", $filter["exclude"] );
685
- }
686
-
687
- // group-by clause, max-matches count, group-sort clause, cutoff count
688
- $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby;
689
- $req .= pack ( "N", $this->_maxmatches );
690
- $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort;
691
- $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay );
692
- $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct;
693
-
694
- // anchor point
695
- if ( empty($this->_anchor) )
696
- {
697
- $req .= pack ( "N", 0 );
698
- } else
699
- {
700
- $a =& $this->_anchor;
701
- $req .= pack ( "N", 1 );
702
- $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"];
703
- $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"];
704
- $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] );
705
- }
706
-
707
- // per-index weights
708
- $req .= pack ( "N", count($this->_indexweights) );
709
- foreach ( $this->_indexweights as $idx=>$weight )
710
- $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight );
711
-
712
- // max query time
713
- $req .= pack ( "N", $this->_maxquerytime );
714
-
715
- // per-field weights
716
- $req .= pack ( "N", count($this->_fieldweights) );
717
- foreach ( $this->_fieldweights as $field=>$weight )
718
- $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight );
719
-
720
- // comment
721
- $req .= pack ( "N", strlen($comment) ) . $comment;
722
-
723
- // mbstring workaround
724
- $this->_MBPop ();
725
-
726
- // store request to requests array
727
- $this->_reqs[] = $req;
728
- return count($this->_reqs)-1;
729
- }
730
-
731
- /// connect to searchd, run queries batch, and return an array of result sets
732
- function RunQueries ()
733
- {
734
- if ( empty($this->_reqs) )
735
- {
736
- $this->_error = "no queries defined, issue AddQuery() first";
737
- return false;
738
- }
739
-
740
- // mbstring workaround
741
- $this->_MBPush ();
742
-
743
- if (!( $fp = $this->_Connect() ))
744
- {
745
- $this->_MBPop ();
746
- return false;
747
- }
748
-
749
- ////////////////////////////
750
- // send query, get response
751
- ////////////////////////////
752
-
753
- $nreqs = count($this->_reqs);
754
- $req = join ( "", $this->_reqs );
755
- $len = 4+strlen($req);
756
- $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header
757
-
758
- fwrite ( $fp, $req, $len+8 );
759
- if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ))
760
- {
761
- $this->_MBPop ();
762
- return false;
763
- }
764
-
765
- $this->_reqs = array ();
766
-
767
- //////////////////
768
- // parse response
769
- //////////////////
770
-
771
- $p = 0; // current position
772
- $max = strlen($response); // max position for checks, to protect against broken responses
773
-
774
- $results = array ();
775
- for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ )
776
- {
777
- $results[] = array();
778
- $result =& $results[$ires];
779
-
780
- $result["error"] = "";
781
- $result["warning"] = "";
782
-
783
- // extract status
784
- list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
785
- $result["status"] = $status;
786
- if ( $status!=SEARCHD_OK )
787
- {
788
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
789
- $message = substr ( $response, $p, $len ); $p += $len;
790
-
791
- if ( $status==SEARCHD_WARNING )
792
- {
793
- $result["warning"] = $message;
794
- } else
795
- {
796
- $result["error"] = $message;
797
- continue;
798
- }
799
- }
800
-
801
- // read schema
802
- $fields = array ();
803
- $attrs = array ();
804
-
805
- list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
806
- while ( $nfields-->0 && $p<$max )
807
- {
808
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
809
- $fields[] = substr ( $response, $p, $len ); $p += $len;
810
- }
811
- $result["fields"] = $fields;
812
-
813
- list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
814
- while ( $nattrs-->0 && $p<$max )
815
- {
816
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
817
- $attr = substr ( $response, $p, $len ); $p += $len;
818
- list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
819
- $attrs[$attr] = $type;
820
- }
821
- $result["attrs"] = $attrs;
822
-
823
- // read match count
824
- list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
825
- list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
826
-
827
- // read matches
828
- $idx = -1;
829
- while ( $count-->0 && $p<$max )
830
- {
831
- // index into result array
832
- $idx++;
833
-
834
- // parse document id and weight
835
- if ( $id64 )
836
- {
837
- $doc = sphUnpack64 ( substr ( $response, $p, 8 ) ); $p += 8;
838
- list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
839
- } else
840
- {
841
- list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
842
- substr ( $response, $p, 8 ) ) );
843
- $p += 8;
844
-
845
- if ( PHP_INT_SIZE>=8 )
846
- {
847
- // x64 route, workaround broken unpack() in 5.2.2+
848
- if ( $doc<0 ) $doc += (1<<32);
849
- } else
850
- {
851
- // x32 route, workaround php signed/unsigned braindamage
852
- $doc = sprintf ( "%u", $doc );
853
- }
854
- }
855
- $weight = sprintf ( "%u", $weight );
856
-
857
- // create match entry
858
- if ( $this->_arrayresult )
859
- $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight );
860
- else
861
- $result["matches"][$doc]["weight"] = $weight;
862
-
863
- // parse and create attributes
864
- $attrvals = array ();
865
- foreach ( $attrs as $attr=>$type )
866
- {
867
- // handle floats
868
- if ( $type==SPH_ATTR_FLOAT )
869
- {
870
- list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
871
- list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
872
- $attrvals[$attr] = $fval;
873
- continue;
874
- }
875
-
876
- // handle everything else as unsigned ints
877
- list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
878
- if ( $type & SPH_ATTR_MULTI )
879
- {
880
- $attrvals[$attr] = array ();
881
- $nvalues = $val;
882
- while ( $nvalues-->0 && $p<$max )
883
- {
884
- list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
885
- $attrvals[$attr][] = sprintf ( "%u", $val );
886
- }
887
- } else
888
- {
889
- $attrvals[$attr] = sprintf ( "%u", $val );
890
- }
891
- }
892
-
893
- if ( $this->_arrayresult )
894
- $result["matches"][$idx]["attrs"] = $attrvals;
895
- else
896
- $result["matches"][$doc]["attrs"] = $attrvals;
897
- }
898
-
899
- list ( $total, $total_found, $msecs, $words ) =
900
- array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) );
901
- $result["total"] = sprintf ( "%u", $total );
902
- $result["total_found"] = sprintf ( "%u", $total_found );
903
- $result["time"] = sprintf ( "%.3f", $msecs/1000 );
904
- $p += 16;
905
-
906
- while ( $words-->0 && $p<$max )
907
- {
908
- list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
909
- $word = substr ( $response, $p, $len ); $p += $len;
910
- list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
911
- $result["words"][$word] = array (
912
- "docs"=>sprintf ( "%u", $docs ),
913
- "hits"=>sprintf ( "%u", $hits ) );
914
- }
915
- }
916
-
917
- $this->_MBPop ();
918
- return $results;
919
- }
920
-
921
- /////////////////////////////////////////////////////////////////////////////
922
- // excerpts generation
923
- /////////////////////////////////////////////////////////////////////////////
924
-
925
- /// connect to searchd server, and generate exceprts (snippets)
926
- /// of given documents for given query. returns false on failure,
927
- /// an array of snippets on success
928
- function BuildExcerpts ( $docs, $index, $words, $opts=array() )
929
- {
930
- assert ( is_array($docs) );
931
- assert ( is_string($index) );
932
- assert ( is_string($words) );
933
- assert ( is_array($opts) );
934
-
935
- $this->_MBPush ();
936
-
937
- if (!( $fp = $this->_Connect() ))
938
- {
939
- $this->_MBPop();
940
- return false;
941
- }
942
-
943
- /////////////////
944
- // fixup options
945
- /////////////////
946
-
947
- if ( !isset($opts["before_match"]) ) $opts["before_match"] = "<b>";
948
- if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>";
949
- if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
950
- if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
951
- if ( !isset($opts["around"]) ) $opts["around"] = 5;
952
- if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
953
- if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
954
- if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
955
- if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
956
-
957
- /////////////////
958
- // build request
959
- /////////////////
960
-
961
- // v.1.0 req
962
- $flags = 1; // remove spaces
963
- if ( $opts["exact_phrase"] ) $flags |= 2;
964
- if ( $opts["single_passage"] ) $flags |= 4;
965
- if ( $opts["use_boundaries"] ) $flags |= 8;
966
- if ( $opts["weight_order"] ) $flags |= 16;
967
- $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
968
- $req .= pack ( "N", strlen($index) ) . $index; // req index
969
- $req .= pack ( "N", strlen($words) ) . $words; // req words
970
-
971
- // options
972
- $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
973
- $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
974
- $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
975
- $req .= pack ( "N", (int)$opts["limit"] );
976
- $req .= pack ( "N", (int)$opts["around"] );
977
-
978
- // documents
979
- $req .= pack ( "N", count($docs) );
980
- foreach ( $docs as $doc )
981
- {
982
- assert ( is_string($doc) );
983
- $req .= pack ( "N", strlen($doc) ) . $doc;
984
- }
985
-
986
- ////////////////////////////
987
- // send query, get response
988
- ////////////////////////////
989
-
990
- $len = strlen($req);
991
- $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
992
- $wrote = fwrite ( $fp, $req, $len+8 );
993
- if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ))
994
- {
995
- $this->_MBPop ();
996
- return false;
997
- }
998
-
999
- //////////////////
1000
- // parse response
1001
- //////////////////
1002
-
1003
- $pos = 0;
1004
- $res = array ();
1005
- $rlen = strlen($response);
1006
- for ( $i=0; $i<count($docs); $i++ )
1007
- {
1008
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) );
1009
- $pos += 4;
1010
-
1011
- if ( $pos+$len > $rlen )
1012
- {
1013
- $this->_error = "incomplete reply";
1014
- $this->_MBPop ();
1015
- return false;
1016
- }
1017
- $res[] = $len ? substr ( $response, $pos, $len ) : "";
1018
- $pos += $len;
1019
- }
1020
-
1021
- $this->_MBPop ();
1022
- return $res;
1023
- }
1024
-
1025
-
1026
- /////////////////////////////////////////////////////////////////////////////
1027
- // keyword generation
1028
- /////////////////////////////////////////////////////////////////////////////
1029
-
1030
- /// connect to searchd server, and generate keyword list for a given query
1031
- /// returns false on failure,
1032
- /// an array of words on success
1033
- function BuildKeywords ( $query, $index, $hits )
1034
- {
1035
- assert ( is_string($query) );
1036
- assert ( is_string($index) );
1037
- assert ( is_bool($hits) );
1038
-
1039
- // Commented out for testing Riddle
1040
- // $this->_MBPush ();
1041
- //
1042
- // if (!( $fp = $this->_Connect() ))
1043
- // {
1044
- // $this->_MBPop();
1045
- // return false;
1046
- // }
1047
-
1048
- /////////////////
1049
- // build request
1050
- /////////////////
1051
-
1052
- // v.1.0 req
1053
- $req = pack ( "N", strlen($query) ) . $query; // req query
1054
- $req .= pack ( "N", strlen($index) ) . $index; // req index
1055
- $req .= pack ( "N", (int)$hits );
1056
-
1057
- // Line for testing Riddle:
1058
- return $req;
1059
-
1060
- ////////////////////////////
1061
- // send query, get response
1062
- ////////////////////////////
1063
-
1064
- $len = strlen($req);
1065
- $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
1066
- $wrote = fwrite ( $fp, $req, $len+8 );
1067
- if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ))
1068
- {
1069
- $this->_MBPop ();
1070
- return false;
1071
- }
1072
-
1073
- //////////////////
1074
- // parse response
1075
- //////////////////
1076
-
1077
- $pos = 0;
1078
- $res = array ();
1079
- $rlen = strlen($response);
1080
- list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) );
1081
- $pos += 4;
1082
- for ( $i=0; $i<$nwords; $i++ )
1083
- {
1084
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
1085
- $tokenized = $len ? substr ( $response, $pos, $len ) : "";
1086
- $pos += $len;
1087
-
1088
- list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
1089
- $normalized = $len ? substr ( $response, $pos, $len ) : "";
1090
- $pos += $len;
1091
-
1092
- $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized );
1093
-
1094
- if ( $hits )
1095
- {
1096
- list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) );
1097
- $pos += 8;
1098
- $res [$i]["docs"] = $ndocs;
1099
- $res [$i]["hits"] = $nhits;
1100
- }
1101
-
1102
- if ( $pos > $rlen )
1103
- {
1104
- $this->_error = "incomplete reply";
1105
- $this->_MBPop ();
1106
- return false;
1107
- }
1108
- }
1109
-
1110
- $this->_MBPop ();
1111
- return $res;
1112
- }
1113
-
1114
- function EscapeString ( $string )
1115
- {
1116
- $from = array ( '(',')','|','-','!','@','~','"','&', '/' );
1117
- $to = array ( '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/' );
1118
-
1119
- return str_replace ( $from, $to, $string );
1120
- }
1121
-
1122
- /////////////////////////////////////////////////////////////////////////////
1123
- // attribute updates
1124
- /////////////////////////////////////////////////////////////////////////////
1125
-
1126
- /// update given attribute values on given documents in given indexes
1127
- /// returns amount of updated documents (0 or more) on success, or -1 on failure
1128
- function UpdateAttributes ( $index, $attrs, $values )
1129
- {
1130
- // verify everything
1131
- assert ( is_string($index) );
1132
-
1133
- assert ( is_array($attrs) );
1134
- foreach ( $attrs as $attr )
1135
- assert ( is_string($attr) );
1136
-
1137
- assert ( is_array($values) );
1138
- foreach ( $values as $id=>$entry )
1139
- {
1140
- assert ( is_numeric($id) );
1141
- assert ( is_array($entry) );
1142
- assert ( count($entry)==count($attrs) );
1143
- foreach ( $entry as $v )
1144
- assert ( is_int($v) );
1145
- }
1146
-
1147
- // build request
1148
- $req = pack ( "N", strlen($index) ) . $index;
1149
-
1150
- $req .= pack ( "N", count($attrs) );
1151
- foreach ( $attrs as $attr )
1152
- $req .= pack ( "N", strlen($attr) ) . $attr;
1153
-
1154
- $req .= pack ( "N", count($values) );
1155
- foreach ( $values as $id=>$entry )
1156
- {
1157
- $req .= sphPack64 ( $id );
1158
- foreach ( $entry as $v )
1159
- $req .= pack ( "N", $v );
1160
- }
1161
-
1162
- // Line for testing Riddle:
1163
- return $req;
1164
-
1165
- // mbstring workaround
1166
- $this->_MBPush ();
1167
-
1168
- // connect, send query, get response
1169
- if (!( $fp = $this->_Connect() ))
1170
- {
1171
- $this->_MBPop ();
1172
- return -1;
1173
- }
1174
-
1175
- $len = strlen($req);
1176
- $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
1177
- fwrite ( $fp, $req, $len+8 );
1178
-
1179
- if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
1180
- {
1181
- $this->_MBPop ();
1182
- return -1;
1183
- }
1184
-
1185
- // parse response
1186
- list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
1187
- $this->_MBPop ();
1188
- return $updated;
1189
- }
1190
-
1191
- // Function for Riddle, copied from internal code.
1192
- function FilterOutput()
1193
- {
1194
- foreach ( $this->_filters as $filter )
1195
- {
1196
- $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
1197
- $req .= pack ( "N", $filter["type"] );
1198
- switch ( $filter["type"] )
1199
- {
1200
- case SPH_FILTER_VALUES:
1201
- $req .= pack ( "N", count($filter["values"]) );
1202
- foreach ( $filter["values"] as $value )
1203
- $req .= pack ( "N", floatval($value) ); // this uberhack is to workaround 32bit signed int limit on x32 platforms
1204
- break;
1205
-
1206
- case SPH_FILTER_RANGE:
1207
- $req .= pack ( "NN", $filter["min"], $filter["max"] );
1208
- break;
1209
-
1210
- case SPH_FILTER_FLOATRANGE:
1211
- $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
1212
- break;
1213
-
1214
- default:
1215
- assert ( 0 && "internal error: unhandled filter type" );
1216
- }
1217
- $req .= pack ( "N", $filter["exclude"] );
1218
- }
1219
-
1220
- return $req;
1221
- }
1222
- }
1223
-
1224
- //
1225
- // $Id: sphinxapi.php 1418 2008-08-28 15:30:05Z shodan $
1226
- //
1227
-
1228
- ?>