riddle 1.0.3 → 1.0.4

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.
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
- ?>