riddle 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +1 -0
- data/lib/riddle.rb +30 -4
- data/lib/riddle/0.9.8.rb +1 -0
- data/lib/riddle/0.9.9.rb +2 -0
- data/lib/riddle/auto_version.rb +11 -0
- data/lib/riddle/client.rb +2 -0
- data/lib/riddle/configuration.rb +3 -1
- data/lib/riddle/configuration/sql_source.rb +13 -0
- data/lib/riddle/controller.rb +29 -2
- data/spec/functional/excerpt_spec.rb +2 -2
- data/spec/functional/status_spec.rb +1 -1
- data/spec/riddle/auto_version_spec.rb +24 -0
- data/spec/riddle/client_spec.rb +11 -0
- data/spec/riddle/configuration_spec.rb +11 -0
- data/spec/riddle/controller_spec.rb +29 -0
- data/spec/riddle_spec.rb +27 -0
- data/spec/spec_helper.rb +1 -4
- data/spec/unit/client_spec.rb +1 -1
- data/spec/unit/configuration/searchd_spec.rb +2 -2
- data/spec/unit/configuration/sql_source_spec.rb +21 -0
- metadata +8 -48
- data/spec/fixtures/data/anchor.bin +0 -0
- data/spec/fixtures/data/any.bin +0 -0
- data/spec/fixtures/data/boolean.bin +0 -0
- data/spec/fixtures/data/comment.bin +0 -0
- data/spec/fixtures/data/distinct.bin +0 -0
- data/spec/fixtures/data/field_weights.bin +0 -0
- data/spec/fixtures/data/filter.bin +0 -0
- data/spec/fixtures/data/filter_array.bin +0 -0
- data/spec/fixtures/data/filter_array_exclude.bin +0 -0
- data/spec/fixtures/data/filter_boolean.bin +0 -0
- data/spec/fixtures/data/filter_floats.bin +0 -0
- data/spec/fixtures/data/filter_floats_exclude.bin +0 -0
- data/spec/fixtures/data/filter_range.bin +0 -0
- data/spec/fixtures/data/filter_range_exclude.bin +0 -0
- data/spec/fixtures/data/group.bin +0 -0
- data/spec/fixtures/data/index.bin +0 -0
- data/spec/fixtures/data/index_weights.bin +0 -0
- data/spec/fixtures/data/keywords_with_hits.bin +0 -0
- data/spec/fixtures/data/keywords_without_hits.bin +0 -0
- data/spec/fixtures/data/overrides.bin +0 -0
- data/spec/fixtures/data/phrase.bin +0 -0
- data/spec/fixtures/data/rank_mode.bin +0 -0
- data/spec/fixtures/data/select.bin +0 -0
- data/spec/fixtures/data/simple.bin +0 -0
- data/spec/fixtures/data/sort.bin +0 -0
- data/spec/fixtures/data/update_simple.bin +0 -0
- data/spec/fixtures/data/weights.bin +0 -0
- data/spec/fixtures/data_generator.0.9.8.php +0 -208
- data/spec/fixtures/data_generator.0.9.9.php +0 -225
- data/spec/fixtures/sphinx/configuration.erb +0 -38
- data/spec/fixtures/sphinx/people.spa +0 -0
- data/spec/fixtures/sphinx/people.spd +0 -0
- data/spec/fixtures/sphinx/people.sph +0 -0
- data/spec/fixtures/sphinx/people.spi +0 -0
- data/spec/fixtures/sphinx/people.spk +0 -0
- data/spec/fixtures/sphinx/people.spm +0 -0
- data/spec/fixtures/sphinx/people.spp +0 -0
- data/spec/fixtures/sphinx/searchd.log +0 -4430
- data/spec/fixtures/sphinx/searchd.query.log +0 -1234
- data/spec/fixtures/sphinx/spec.conf +0 -38
- data/spec/fixtures/sphinxapi.0.9.8.php +0 -1228
- data/spec/fixtures/sphinxapi.0.9.9.php +0 -1646
- data/spec/fixtures/sql/conf.example.yml +0 -3
- data/spec/fixtures/sql/conf.yml +0 -3
- data/spec/fixtures/sql/data.sql +0 -25000
- data/spec/fixtures/sql/structure.sql +0 -16
@@ -1,1646 +0,0 @@
|
|
1
|
-
<?php
|
2
|
-
|
3
|
-
//
|
4
|
-
// $Id: sphinxapi.php 1775 2009-04-06 22:15:58Z 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
|
-
define ( "SEARCHD_COMMAND_PERSIST", 4 );
|
26
|
-
define ( "SEARCHD_COMMAND_STATUS", 5 );
|
27
|
-
define ( "SEARCHD_COMMAND_QUERY", 6 );
|
28
|
-
|
29
|
-
/// current client-side command implementation versions
|
30
|
-
define ( "VER_COMMAND_SEARCH", 0x116 );
|
31
|
-
define ( "VER_COMMAND_EXCERPT", 0x100 );
|
32
|
-
define ( "VER_COMMAND_UPDATE", 0x102 );
|
33
|
-
define ( "VER_COMMAND_KEYWORDS", 0x100 );
|
34
|
-
define ( "VER_COMMAND_STATUS", 0x100 );
|
35
|
-
define ( "VER_COMMAND_QUERY", 0x100 );
|
36
|
-
|
37
|
-
/// known searchd status codes
|
38
|
-
define ( "SEARCHD_OK", 0 );
|
39
|
-
define ( "SEARCHD_ERROR", 1 );
|
40
|
-
define ( "SEARCHD_RETRY", 2 );
|
41
|
-
define ( "SEARCHD_WARNING", 3 );
|
42
|
-
|
43
|
-
/// known match modes
|
44
|
-
define ( "SPH_MATCH_ALL", 0 );
|
45
|
-
define ( "SPH_MATCH_ANY", 1 );
|
46
|
-
define ( "SPH_MATCH_PHRASE", 2 );
|
47
|
-
define ( "SPH_MATCH_BOOLEAN", 3 );
|
48
|
-
define ( "SPH_MATCH_EXTENDED", 4 );
|
49
|
-
define ( "SPH_MATCH_FULLSCAN", 5 );
|
50
|
-
define ( "SPH_MATCH_EXTENDED2", 6 ); // extended engine V2 (TEMPORARY, WILL BE REMOVED)
|
51
|
-
|
52
|
-
/// known ranking modes (ext2 only)
|
53
|
-
define ( "SPH_RANK_PROXIMITY_BM25", 0 ); ///< default mode, phrase proximity major factor and BM25 minor one
|
54
|
-
define ( "SPH_RANK_BM25", 1 ); ///< statistical mode, BM25 ranking only (faster but worse quality)
|
55
|
-
define ( "SPH_RANK_NONE", 2 ); ///< no ranking, all matches get a weight of 1
|
56
|
-
define ( "SPH_RANK_WORDCOUNT", 3 ); ///< simple word-count weighting, rank is a weighted sum of per-field keyword occurence counts
|
57
|
-
define ( "SPH_RANK_PROXIMITY", 4 );
|
58
|
-
define ( "SPH_RANK_MATCHANY", 5 );
|
59
|
-
define ( "SPH_RANK_FIELDMASK", 6 );
|
60
|
-
|
61
|
-
/// known sort modes
|
62
|
-
define ( "SPH_SORT_RELEVANCE", 0 );
|
63
|
-
define ( "SPH_SORT_ATTR_DESC", 1 );
|
64
|
-
define ( "SPH_SORT_ATTR_ASC", 2 );
|
65
|
-
define ( "SPH_SORT_TIME_SEGMENTS", 3 );
|
66
|
-
define ( "SPH_SORT_EXTENDED", 4 );
|
67
|
-
define ( "SPH_SORT_EXPR", 5 );
|
68
|
-
|
69
|
-
/// known filter types
|
70
|
-
define ( "SPH_FILTER_VALUES", 0 );
|
71
|
-
define ( "SPH_FILTER_RANGE", 1 );
|
72
|
-
define ( "SPH_FILTER_FLOATRANGE", 2 );
|
73
|
-
|
74
|
-
/// known attribute types
|
75
|
-
define ( "SPH_ATTR_INTEGER", 1 );
|
76
|
-
define ( "SPH_ATTR_TIMESTAMP", 2 );
|
77
|
-
define ( "SPH_ATTR_ORDINAL", 3 );
|
78
|
-
define ( "SPH_ATTR_BOOL", 4 );
|
79
|
-
define ( "SPH_ATTR_FLOAT", 5 );
|
80
|
-
define ( "SPH_ATTR_BIGINT", 6 );
|
81
|
-
define ( "SPH_ATTR_MULTI", 0x40000000 );
|
82
|
-
|
83
|
-
/// known grouping functions
|
84
|
-
define ( "SPH_GROUPBY_DAY", 0 );
|
85
|
-
define ( "SPH_GROUPBY_WEEK", 1 );
|
86
|
-
define ( "SPH_GROUPBY_MONTH", 2 );
|
87
|
-
define ( "SPH_GROUPBY_YEAR", 3 );
|
88
|
-
define ( "SPH_GROUPBY_ATTR", 4 );
|
89
|
-
define ( "SPH_GROUPBY_ATTRPAIR", 5 );
|
90
|
-
|
91
|
-
// important properties of PHP's integers:
|
92
|
-
// - always signed (one bit short of PHP_INT_SIZE)
|
93
|
-
// - conversion from string to int is saturated
|
94
|
-
// - float is double
|
95
|
-
// - div converts arguments to floats
|
96
|
-
// - mod converts arguments to ints
|
97
|
-
|
98
|
-
// the packing code below works as follows:
|
99
|
-
// - when we got an int, just pack it
|
100
|
-
// if performance is a problem, this is the branch users should aim for
|
101
|
-
//
|
102
|
-
// - otherwise, we got a number in string form
|
103
|
-
// this might be due to different reasons, but we assume that this is
|
104
|
-
// because it didn't fit into PHP int
|
105
|
-
//
|
106
|
-
// - factor the string into high and low ints for packing
|
107
|
-
// - if we have bcmath, then it is used
|
108
|
-
// - if we don't, we have to do it manually (this is the fun part)
|
109
|
-
//
|
110
|
-
// - x64 branch does factoring using ints
|
111
|
-
// - x32 (ab)uses floats, since we can't fit unsigned 32-bit number into an int
|
112
|
-
//
|
113
|
-
// unpacking routines are pretty much the same.
|
114
|
-
// - return ints if we can
|
115
|
-
// - otherwise format number into a string
|
116
|
-
|
117
|
-
/// pack 64-bit signed
|
118
|
-
function sphPackI64 ( $v )
|
119
|
-
{
|
120
|
-
assert ( is_numeric($v) );
|
121
|
-
|
122
|
-
// x64
|
123
|
-
if ( PHP_INT_SIZE>=8 )
|
124
|
-
{
|
125
|
-
$v = (int)$v;
|
126
|
-
return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
|
127
|
-
}
|
128
|
-
|
129
|
-
// x32, int
|
130
|
-
if ( is_int($v) )
|
131
|
-
return pack ( "NN", $v < 0 ? -1 : 0, $v );
|
132
|
-
|
133
|
-
// x32, bcmath
|
134
|
-
if ( function_exists("bcmul") )
|
135
|
-
{
|
136
|
-
if ( bccomp ( $v, 0 ) == -1 )
|
137
|
-
$v = bcadd ( "18446744073709551616", $v );
|
138
|
-
$h = bcdiv ( $v, "4294967296", 0 );
|
139
|
-
$l = bcmod ( $v, "4294967296" );
|
140
|
-
return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
|
141
|
-
}
|
142
|
-
|
143
|
-
// x32, no-bcmath
|
144
|
-
$p = max(0, strlen($v) - 13);
|
145
|
-
$lo = abs((float)substr($v, $p));
|
146
|
-
$hi = abs((float)substr($v, 0, $p));
|
147
|
-
|
148
|
-
$m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912
|
149
|
-
$q = floor($m/4294967296.0);
|
150
|
-
$l = $m - ($q*4294967296.0);
|
151
|
-
$h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328
|
152
|
-
|
153
|
-
if ( $v<0 )
|
154
|
-
{
|
155
|
-
if ( $l==0 )
|
156
|
-
$h = 4294967296.0 - $h;
|
157
|
-
else
|
158
|
-
{
|
159
|
-
$h = 4294967295.0 - $h;
|
160
|
-
$l = 4294967296.0 - $l;
|
161
|
-
}
|
162
|
-
}
|
163
|
-
return pack ( "NN", $h, $l );
|
164
|
-
}
|
165
|
-
|
166
|
-
/// pack 64-bit unsigned
|
167
|
-
function sphPackU64 ( $v )
|
168
|
-
{
|
169
|
-
assert ( is_numeric($v) );
|
170
|
-
|
171
|
-
// x64
|
172
|
-
if ( PHP_INT_SIZE>=8 )
|
173
|
-
{
|
174
|
-
assert ( $v>=0 );
|
175
|
-
|
176
|
-
// x64, int
|
177
|
-
if ( is_int($v) )
|
178
|
-
return pack ( "NN", $v>>32, $v&0xFFFFFFFF );
|
179
|
-
|
180
|
-
// x64, bcmath
|
181
|
-
if ( function_exists("bcmul") )
|
182
|
-
{
|
183
|
-
$h = bcdiv ( $v, 4294967296, 0 );
|
184
|
-
$l = bcmod ( $v, 4294967296 );
|
185
|
-
return pack ( "NN", $h, $l );
|
186
|
-
}
|
187
|
-
|
188
|
-
// x64, no-bcmath
|
189
|
-
$p = max ( 0, strlen($v) - 13 );
|
190
|
-
$lo = (int)substr ( $v, $p );
|
191
|
-
$hi = (int)substr ( $v, 0, $p );
|
192
|
-
|
193
|
-
$m = $lo + $hi*1316134912;
|
194
|
-
$l = $m % 4294967296;
|
195
|
-
$h = $hi*2328 + (int)($m/4294967296);
|
196
|
-
|
197
|
-
return pack ( "NN", $h, $l );
|
198
|
-
}
|
199
|
-
|
200
|
-
// x32, int
|
201
|
-
if ( is_int($v) )
|
202
|
-
return pack ( "NN", 0, $v );
|
203
|
-
|
204
|
-
// x32, bcmath
|
205
|
-
if ( function_exists("bcmul") )
|
206
|
-
{
|
207
|
-
$h = bcdiv ( $v, "4294967296", 0 );
|
208
|
-
$l = bcmod ( $v, "4294967296" );
|
209
|
-
return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit
|
210
|
-
}
|
211
|
-
|
212
|
-
// x32, no-bcmath
|
213
|
-
$p = max(0, strlen($v) - 13);
|
214
|
-
$lo = (float)substr($v, $p);
|
215
|
-
$hi = (float)substr($v, 0, $p);
|
216
|
-
|
217
|
-
$m = $lo + $hi*1316134912.0;
|
218
|
-
$q = floor($m / 4294967296.0);
|
219
|
-
$l = $m - ($q * 4294967296.0);
|
220
|
-
$h = $hi*2328.0 + $q;
|
221
|
-
|
222
|
-
return pack ( "NN", $h, $l );
|
223
|
-
}
|
224
|
-
|
225
|
-
// unpack 64-bit unsigned
|
226
|
-
function sphUnpackU64 ( $v )
|
227
|
-
{
|
228
|
-
list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
|
229
|
-
|
230
|
-
if ( PHP_INT_SIZE>=8 )
|
231
|
-
{
|
232
|
-
if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
|
233
|
-
if ( $lo<0 ) $lo += (1<<32);
|
234
|
-
|
235
|
-
// x64, int
|
236
|
-
if ( $hi<=2147483647 )
|
237
|
-
return ($hi<<32) + $lo;
|
238
|
-
|
239
|
-
// x64, bcmath
|
240
|
-
if ( function_exists("bcmul") )
|
241
|
-
return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
|
242
|
-
|
243
|
-
// x64, no-bcmath
|
244
|
-
$C = 100000;
|
245
|
-
$h = ((int)($hi / $C) << 32) + (int)($lo / $C);
|
246
|
-
$l = (($hi % $C) << 32) + ($lo % $C);
|
247
|
-
if ( $l>$C )
|
248
|
-
{
|
249
|
-
$h += (int)($l / $C);
|
250
|
-
$l = $l % $C;
|
251
|
-
}
|
252
|
-
|
253
|
-
if ( $h==0 )
|
254
|
-
return $l;
|
255
|
-
return sprintf ( "%d%05d", $h, $l );
|
256
|
-
}
|
257
|
-
|
258
|
-
// x32, int
|
259
|
-
if ( $hi==0 )
|
260
|
-
{
|
261
|
-
if ( $lo>0 )
|
262
|
-
return $lo;
|
263
|
-
return sprintf ( "%u", $lo );
|
264
|
-
}
|
265
|
-
|
266
|
-
$hi = sprintf ( "%u", $hi );
|
267
|
-
$lo = sprintf ( "%u", $lo );
|
268
|
-
|
269
|
-
// x32, bcmath
|
270
|
-
if ( function_exists("bcmul") )
|
271
|
-
return bcadd ( $lo, bcmul ( $hi, "4294967296" ) );
|
272
|
-
|
273
|
-
// x32, no-bcmath
|
274
|
-
$hi = (float)$hi;
|
275
|
-
$lo = (float)$lo;
|
276
|
-
|
277
|
-
$q = floor($hi/10000000.0);
|
278
|
-
$r = $hi - $q*10000000.0;
|
279
|
-
$m = $lo + $r*4967296.0;
|
280
|
-
$mq = floor($m/10000000.0);
|
281
|
-
$l = $m - $mq*10000000.0;
|
282
|
-
$h = $q*4294967296.0 + $r*429.0 + $mq;
|
283
|
-
|
284
|
-
$h = sprintf ( "%.0f", $h );
|
285
|
-
$l = sprintf ( "%07.0f", $l );
|
286
|
-
if ( $h=="0" )
|
287
|
-
return sprintf( "%.0f", (float)$l );
|
288
|
-
return $h . $l;
|
289
|
-
}
|
290
|
-
|
291
|
-
// unpack 64-bit signed
|
292
|
-
function sphUnpackI64 ( $v )
|
293
|
-
{
|
294
|
-
list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) );
|
295
|
-
|
296
|
-
// x64
|
297
|
-
if ( PHP_INT_SIZE>=8 )
|
298
|
-
{
|
299
|
-
if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again
|
300
|
-
if ( $lo<0 ) $lo += (1<<32);
|
301
|
-
|
302
|
-
return ($hi<<32) + $lo;
|
303
|
-
}
|
304
|
-
|
305
|
-
// x32, int
|
306
|
-
if ( $hi==0 )
|
307
|
-
{
|
308
|
-
if ( $lo>0 )
|
309
|
-
return $lo;
|
310
|
-
return sprintf ( "%u", $lo );
|
311
|
-
}
|
312
|
-
// x32, int
|
313
|
-
elseif ( $hi==-1 )
|
314
|
-
{
|
315
|
-
if ( $lo<0 )
|
316
|
-
return $lo;
|
317
|
-
return sprintf ( "%.0f", $lo - 4294967296.0 );
|
318
|
-
}
|
319
|
-
|
320
|
-
$neg = "";
|
321
|
-
$c = 0;
|
322
|
-
if ( $hi<0 )
|
323
|
-
{
|
324
|
-
$hi = ~$hi;
|
325
|
-
$lo = ~$lo;
|
326
|
-
$c = 1;
|
327
|
-
$neg = "-";
|
328
|
-
}
|
329
|
-
|
330
|
-
$hi = sprintf ( "%u", $hi );
|
331
|
-
$lo = sprintf ( "%u", $lo );
|
332
|
-
|
333
|
-
// x32, bcmath
|
334
|
-
if ( function_exists("bcmul") )
|
335
|
-
return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c );
|
336
|
-
|
337
|
-
// x32, no-bcmath
|
338
|
-
$hi = (float)$hi;
|
339
|
-
$lo = (float)$lo;
|
340
|
-
|
341
|
-
$q = floor($hi/10000000.0);
|
342
|
-
$r = $hi - $q*10000000.0;
|
343
|
-
$m = $lo + $r*4967296.0;
|
344
|
-
$mq = floor($m/10000000.0);
|
345
|
-
$l = $m - $mq*10000000.0 + $c;
|
346
|
-
$h = $q*4294967296.0 + $r*429.0 + $mq;
|
347
|
-
|
348
|
-
$h = sprintf ( "%.0f", $h );
|
349
|
-
$l = sprintf ( "%07.0f", $l );
|
350
|
-
if ( $h=="0" )
|
351
|
-
return $neg . sprintf( "%.0f", (float)$l );
|
352
|
-
return $neg . $h . $l;
|
353
|
-
}
|
354
|
-
|
355
|
-
|
356
|
-
/// sphinx searchd client class
|
357
|
-
class SphinxClient
|
358
|
-
{
|
359
|
-
var $_host; ///< searchd host (default is "localhost")
|
360
|
-
var $_port; ///< searchd port (default is 3312)
|
361
|
-
var $_offset; ///< how many records to seek from result-set start (default is 0)
|
362
|
-
var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
|
363
|
-
var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
|
364
|
-
var $_weights; ///< per-field weights (default is 1 for all fields)
|
365
|
-
var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE)
|
366
|
-
var $_sortby; ///< attribute to sort by (defualt is "")
|
367
|
-
var $_min_id; ///< min ID to match (default is 0, which means no limit)
|
368
|
-
var $_max_id; ///< max ID to match (default is 0, which means no limit)
|
369
|
-
var $_filters; ///< search filters
|
370
|
-
var $_groupby; ///< group-by attribute name
|
371
|
-
var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with)
|
372
|
-
var $_groupsort; ///< group-by sorting clause (to sort groups in result set with)
|
373
|
-
var $_groupdistinct;///< group-by count-distinct attribute
|
374
|
-
var $_maxmatches; ///< max matches to retrieve
|
375
|
-
var $_cutoff; ///< cutoff to stop searching at (default is 0)
|
376
|
-
var $_retrycount; ///< distributed retries count
|
377
|
-
var $_retrydelay; ///< distributed retries delay
|
378
|
-
var $_anchor; ///< geographical anchor point
|
379
|
-
var $_indexweights; ///< per-index weights
|
380
|
-
var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
|
381
|
-
var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
|
382
|
-
var $_fieldweights; ///< per-field-name weights
|
383
|
-
var $_overrides; ///< per-query attribute values overrides
|
384
|
-
var $_select; ///< select-list (attributes or expressions, with optional aliases)
|
385
|
-
|
386
|
-
var $_error; ///< last error message
|
387
|
-
var $_warning; ///< last warning message
|
388
|
-
var $_connerror; ///< connection error vs remote error flag
|
389
|
-
|
390
|
-
var $_reqs; ///< requests array for multi-query
|
391
|
-
var $_mbenc; ///< stored mbstring encoding
|
392
|
-
var $_arrayresult; ///< whether $result["matches"] should be a hash or an array
|
393
|
-
var $_timeout; ///< connect timeout
|
394
|
-
|
395
|
-
/////////////////////////////////////////////////////////////////////////////
|
396
|
-
// common stuff
|
397
|
-
/////////////////////////////////////////////////////////////////////////////
|
398
|
-
|
399
|
-
/// create a new client object and fill defaults
|
400
|
-
function SphinxClient ()
|
401
|
-
{
|
402
|
-
// per-client-object settings
|
403
|
-
$this->_host = "localhost";
|
404
|
-
$this->_port = 3312;
|
405
|
-
$this->_path = false;
|
406
|
-
$this->_socket = false;
|
407
|
-
|
408
|
-
// per-query settings
|
409
|
-
$this->_offset = 0;
|
410
|
-
$this->_limit = 20;
|
411
|
-
$this->_mode = SPH_MATCH_ALL;
|
412
|
-
$this->_weights = array ();
|
413
|
-
$this->_sort = SPH_SORT_RELEVANCE;
|
414
|
-
$this->_sortby = "";
|
415
|
-
$this->_min_id = 0;
|
416
|
-
$this->_max_id = 0;
|
417
|
-
$this->_filters = array ();
|
418
|
-
$this->_groupby = "";
|
419
|
-
$this->_groupfunc = SPH_GROUPBY_DAY;
|
420
|
-
$this->_groupsort = "@group desc";
|
421
|
-
$this->_groupdistinct= "";
|
422
|
-
$this->_maxmatches = 1000;
|
423
|
-
$this->_cutoff = 0;
|
424
|
-
$this->_retrycount = 0;
|
425
|
-
$this->_retrydelay = 0;
|
426
|
-
$this->_anchor = array ();
|
427
|
-
$this->_indexweights= array ();
|
428
|
-
$this->_ranker = SPH_RANK_PROXIMITY_BM25;
|
429
|
-
$this->_maxquerytime= 0;
|
430
|
-
$this->_fieldweights= array();
|
431
|
-
$this->_overrides = array();
|
432
|
-
$this->_select = "*";
|
433
|
-
|
434
|
-
$this->_error = ""; // per-reply fields (for single-query case)
|
435
|
-
$this->_warning = "";
|
436
|
-
$this->_connerror = false;
|
437
|
-
|
438
|
-
$this->_reqs = array (); // requests storage (for multi-query case)
|
439
|
-
$this->_mbenc = "";
|
440
|
-
$this->_arrayresult = false;
|
441
|
-
$this->_timeout = 0;
|
442
|
-
}
|
443
|
-
|
444
|
-
function __destruct()
|
445
|
-
{
|
446
|
-
if ( $this->_socket !== false )
|
447
|
-
fclose ( $this->_socket );
|
448
|
-
}
|
449
|
-
|
450
|
-
/// get last error message (string)
|
451
|
-
function GetLastError ()
|
452
|
-
{
|
453
|
-
return $this->_error;
|
454
|
-
}
|
455
|
-
|
456
|
-
/// get last warning message (string)
|
457
|
-
function GetLastWarning ()
|
458
|
-
{
|
459
|
-
return $this->_warning;
|
460
|
-
}
|
461
|
-
|
462
|
-
/// get last error flag (to tell network connection errors from searchd errors or broken responses)
|
463
|
-
function IsConnectError()
|
464
|
-
{
|
465
|
-
return $this->_connerror;
|
466
|
-
}
|
467
|
-
|
468
|
-
/// set searchd host name (string) and port (integer)
|
469
|
-
function SetServer ( $host, $port = 0 )
|
470
|
-
{
|
471
|
-
assert ( is_string($host) );
|
472
|
-
if ( $host[0] == '/')
|
473
|
-
{
|
474
|
-
$this->_path = 'unix://' . $host;
|
475
|
-
return;
|
476
|
-
}
|
477
|
-
if ( substr ( $host, 0, 7 )=="unix://" )
|
478
|
-
{
|
479
|
-
$this->_path = $host;
|
480
|
-
return;
|
481
|
-
}
|
482
|
-
|
483
|
-
assert ( is_int($port) );
|
484
|
-
$this->_host = $host;
|
485
|
-
$this->_port = $port;
|
486
|
-
$this->_path = '';
|
487
|
-
|
488
|
-
}
|
489
|
-
|
490
|
-
/// set server connection timeout (0 to remove)
|
491
|
-
function SetConnectTimeout ( $timeout )
|
492
|
-
{
|
493
|
-
assert ( is_numeric($timeout) );
|
494
|
-
$this->_timeout = $timeout;
|
495
|
-
}
|
496
|
-
|
497
|
-
|
498
|
-
function _Send ( $handle, $data, $length )
|
499
|
-
{
|
500
|
-
if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length )
|
501
|
-
{
|
502
|
-
$this->_error = 'connection unexpectedly closed (timed out?)';
|
503
|
-
$this->_connerror = true;
|
504
|
-
return false;
|
505
|
-
}
|
506
|
-
return true;
|
507
|
-
}
|
508
|
-
|
509
|
-
/////////////////////////////////////////////////////////////////////////////
|
510
|
-
|
511
|
-
/// enter mbstring workaround mode
|
512
|
-
function _MBPush ()
|
513
|
-
{
|
514
|
-
$this->_mbenc = "";
|
515
|
-
if ( ini_get ( "mbstring.func_overload" ) & 2 )
|
516
|
-
{
|
517
|
-
$this->_mbenc = mb_internal_encoding();
|
518
|
-
mb_internal_encoding ( "latin1" );
|
519
|
-
}
|
520
|
-
}
|
521
|
-
|
522
|
-
/// leave mbstring workaround mode
|
523
|
-
function _MBPop ()
|
524
|
-
{
|
525
|
-
if ( $this->_mbenc )
|
526
|
-
mb_internal_encoding ( $this->_mbenc );
|
527
|
-
}
|
528
|
-
|
529
|
-
/// connect to searchd server
|
530
|
-
function _Connect ()
|
531
|
-
{
|
532
|
-
if ( $this->_socket !== false )
|
533
|
-
return $this->_socket;
|
534
|
-
|
535
|
-
$errno = 0;
|
536
|
-
$errstr = "";
|
537
|
-
$this->_connerror = false;
|
538
|
-
|
539
|
-
if ( $this->_path )
|
540
|
-
{
|
541
|
-
$host = $this->_path;
|
542
|
-
$port = 0;
|
543
|
-
}
|
544
|
-
else
|
545
|
-
{
|
546
|
-
$host = $this->_host;
|
547
|
-
$port = $this->_port;
|
548
|
-
}
|
549
|
-
|
550
|
-
if ( $this->_timeout<=0 )
|
551
|
-
$fp = @fsockopen ( $host, $port, $errno, $errstr );
|
552
|
-
else
|
553
|
-
$fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout );
|
554
|
-
|
555
|
-
if ( !$fp )
|
556
|
-
{
|
557
|
-
if ( $this->_path )
|
558
|
-
$location = $this->_path;
|
559
|
-
else
|
560
|
-
$location = "{$this->_host}:{$this->_port}";
|
561
|
-
|
562
|
-
$errstr = trim ( $errstr );
|
563
|
-
$this->_error = "connection to $location failed (errno=$errno, msg=$errstr)";
|
564
|
-
$this->_connerror = true;
|
565
|
-
return false;
|
566
|
-
}
|
567
|
-
|
568
|
-
// send my version
|
569
|
-
// this is a subtle part. we must do it before (!) reading back from searchd.
|
570
|
-
// because otherwise under some conditions (reported on FreeBSD for instance)
|
571
|
-
// TCP stack could throttle write-write-read pattern because of Nagle.
|
572
|
-
if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) )
|
573
|
-
{
|
574
|
-
fclose ( $fp );
|
575
|
-
$this->_error = "failed to send client protocol version";
|
576
|
-
return false;
|
577
|
-
}
|
578
|
-
|
579
|
-
// check version
|
580
|
-
list(,$v) = unpack ( "N*", fread ( $fp, 4 ) );
|
581
|
-
$v = (int)$v;
|
582
|
-
if ( $v<1 )
|
583
|
-
{
|
584
|
-
fclose ( $fp );
|
585
|
-
$this->_error = "expected searchd protocol version 1+, got version '$v'";
|
586
|
-
return false;
|
587
|
-
}
|
588
|
-
|
589
|
-
return $fp;
|
590
|
-
}
|
591
|
-
|
592
|
-
/// get and check response packet from searchd server
|
593
|
-
function _GetResponse ( $fp, $client_ver )
|
594
|
-
{
|
595
|
-
$response = "";
|
596
|
-
$len = 0;
|
597
|
-
|
598
|
-
$header = fread ( $fp, 8 );
|
599
|
-
if ( strlen($header)==8 )
|
600
|
-
{
|
601
|
-
list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) );
|
602
|
-
$left = $len;
|
603
|
-
while ( $left>0 && !feof($fp) )
|
604
|
-
{
|
605
|
-
$chunk = fread ( $fp, $left );
|
606
|
-
if ( $chunk )
|
607
|
-
{
|
608
|
-
$response .= $chunk;
|
609
|
-
$left -= strlen($chunk);
|
610
|
-
}
|
611
|
-
}
|
612
|
-
}
|
613
|
-
if ( $this->_socket === false )
|
614
|
-
fclose ( $fp );
|
615
|
-
|
616
|
-
// check response
|
617
|
-
$read = strlen ( $response );
|
618
|
-
if ( !$response || $read!=$len )
|
619
|
-
{
|
620
|
-
$this->_error = $len
|
621
|
-
? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)"
|
622
|
-
: "received zero-sized searchd response";
|
623
|
-
return false;
|
624
|
-
}
|
625
|
-
|
626
|
-
// check status
|
627
|
-
if ( $status==SEARCHD_WARNING )
|
628
|
-
{
|
629
|
-
list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) );
|
630
|
-
$this->_warning = substr ( $response, 4, $wlen );
|
631
|
-
return substr ( $response, 4+$wlen );
|
632
|
-
}
|
633
|
-
if ( $status==SEARCHD_ERROR )
|
634
|
-
{
|
635
|
-
$this->_error = "searchd error: " . substr ( $response, 4 );
|
636
|
-
return false;
|
637
|
-
}
|
638
|
-
if ( $status==SEARCHD_RETRY )
|
639
|
-
{
|
640
|
-
$this->_error = "temporary searchd error: " . substr ( $response, 4 );
|
641
|
-
return false;
|
642
|
-
}
|
643
|
-
if ( $status!=SEARCHD_OK )
|
644
|
-
{
|
645
|
-
$this->_error = "unknown status code '$status'";
|
646
|
-
return false;
|
647
|
-
}
|
648
|
-
|
649
|
-
// check version
|
650
|
-
if ( $ver<$client_ver )
|
651
|
-
{
|
652
|
-
$this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work",
|
653
|
-
$ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff );
|
654
|
-
}
|
655
|
-
|
656
|
-
return $response;
|
657
|
-
}
|
658
|
-
|
659
|
-
/////////////////////////////////////////////////////////////////////////////
|
660
|
-
// searching
|
661
|
-
/////////////////////////////////////////////////////////////////////////////
|
662
|
-
|
663
|
-
/// set offset and count into result set,
|
664
|
-
/// and optionally set max-matches and cutoff limits
|
665
|
-
function SetLimits ( $offset, $limit, $max=0, $cutoff=0 )
|
666
|
-
{
|
667
|
-
assert ( is_int($offset) );
|
668
|
-
assert ( is_int($limit) );
|
669
|
-
assert ( $offset>=0 );
|
670
|
-
assert ( $limit>0 );
|
671
|
-
assert ( $max>=0 );
|
672
|
-
$this->_offset = $offset;
|
673
|
-
$this->_limit = $limit;
|
674
|
-
if ( $max>0 )
|
675
|
-
$this->_maxmatches = $max;
|
676
|
-
if ( $cutoff>0 )
|
677
|
-
$this->_cutoff = $cutoff;
|
678
|
-
}
|
679
|
-
|
680
|
-
/// set maximum query time, in milliseconds, per-index
|
681
|
-
/// integer, 0 means "do not limit"
|
682
|
-
function SetMaxQueryTime ( $max )
|
683
|
-
{
|
684
|
-
assert ( is_int($max) );
|
685
|
-
assert ( $max>=0 );
|
686
|
-
$this->_maxquerytime = $max;
|
687
|
-
}
|
688
|
-
|
689
|
-
/// set matching mode
|
690
|
-
function SetMatchMode ( $mode )
|
691
|
-
{
|
692
|
-
assert ( $mode==SPH_MATCH_ALL
|
693
|
-
|| $mode==SPH_MATCH_ANY
|
694
|
-
|| $mode==SPH_MATCH_PHRASE
|
695
|
-
|| $mode==SPH_MATCH_BOOLEAN
|
696
|
-
|| $mode==SPH_MATCH_EXTENDED
|
697
|
-
|| $mode==SPH_MATCH_FULLSCAN
|
698
|
-
|| $mode==SPH_MATCH_EXTENDED2 );
|
699
|
-
$this->_mode = $mode;
|
700
|
-
}
|
701
|
-
|
702
|
-
/// set ranking mode
|
703
|
-
function SetRankingMode ( $ranker )
|
704
|
-
{
|
705
|
-
assert ( $ranker==SPH_RANK_PROXIMITY_BM25
|
706
|
-
|| $ranker==SPH_RANK_BM25
|
707
|
-
|| $ranker==SPH_RANK_NONE
|
708
|
-
|| $ranker==SPH_RANK_WORDCOUNT
|
709
|
-
|| $ranker==SPH_RANK_PROXIMITY );
|
710
|
-
$this->_ranker = $ranker;
|
711
|
-
}
|
712
|
-
|
713
|
-
/// set matches sorting mode
|
714
|
-
function SetSortMode ( $mode, $sortby="" )
|
715
|
-
{
|
716
|
-
assert (
|
717
|
-
$mode==SPH_SORT_RELEVANCE ||
|
718
|
-
$mode==SPH_SORT_ATTR_DESC ||
|
719
|
-
$mode==SPH_SORT_ATTR_ASC ||
|
720
|
-
$mode==SPH_SORT_TIME_SEGMENTS ||
|
721
|
-
$mode==SPH_SORT_EXTENDED ||
|
722
|
-
$mode==SPH_SORT_EXPR );
|
723
|
-
assert ( is_string($sortby) );
|
724
|
-
assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 );
|
725
|
-
|
726
|
-
$this->_sort = $mode;
|
727
|
-
$this->_sortby = $sortby;
|
728
|
-
}
|
729
|
-
|
730
|
-
/// bind per-field weights by order
|
731
|
-
/// DEPRECATED; use SetFieldWeights() instead
|
732
|
-
function SetWeights ( $weights )
|
733
|
-
{
|
734
|
-
assert ( is_array($weights) );
|
735
|
-
foreach ( $weights as $weight )
|
736
|
-
assert ( is_int($weight) );
|
737
|
-
|
738
|
-
$this->_weights = $weights;
|
739
|
-
}
|
740
|
-
|
741
|
-
/// bind per-field weights by name
|
742
|
-
function SetFieldWeights ( $weights )
|
743
|
-
{
|
744
|
-
assert ( is_array($weights) );
|
745
|
-
foreach ( $weights as $name=>$weight )
|
746
|
-
{
|
747
|
-
assert ( is_string($name) );
|
748
|
-
assert ( is_int($weight) );
|
749
|
-
}
|
750
|
-
$this->_fieldweights = $weights;
|
751
|
-
}
|
752
|
-
|
753
|
-
/// bind per-index weights by name
|
754
|
-
function SetIndexWeights ( $weights )
|
755
|
-
{
|
756
|
-
assert ( is_array($weights) );
|
757
|
-
foreach ( $weights as $index=>$weight )
|
758
|
-
{
|
759
|
-
assert ( is_string($index) );
|
760
|
-
assert ( is_int($weight) );
|
761
|
-
}
|
762
|
-
$this->_indexweights = $weights;
|
763
|
-
}
|
764
|
-
|
765
|
-
/// set IDs range to match
|
766
|
-
/// only match records if document ID is beetwen $min and $max (inclusive)
|
767
|
-
function SetIDRange ( $min, $max )
|
768
|
-
{
|
769
|
-
assert ( is_numeric($min) );
|
770
|
-
assert ( is_numeric($max) );
|
771
|
-
assert ( $min<=$max );
|
772
|
-
$this->_min_id = $min;
|
773
|
-
$this->_max_id = $max;
|
774
|
-
}
|
775
|
-
|
776
|
-
/// set values set filter
|
777
|
-
/// only match records where $attribute value is in given set
|
778
|
-
function SetFilter ( $attribute, $values, $exclude=false )
|
779
|
-
{
|
780
|
-
assert ( is_string($attribute) );
|
781
|
-
assert ( is_array($values) );
|
782
|
-
assert ( count($values) );
|
783
|
-
|
784
|
-
if ( is_array($values) && count($values) )
|
785
|
-
{
|
786
|
-
foreach ( $values as $value )
|
787
|
-
assert ( is_numeric($value) );
|
788
|
-
|
789
|
-
$this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values );
|
790
|
-
}
|
791
|
-
}
|
792
|
-
|
793
|
-
/// set range filter
|
794
|
-
/// only match records if $attribute value is beetwen $min and $max (inclusive)
|
795
|
-
function SetFilterRange ( $attribute, $min, $max, $exclude=false )
|
796
|
-
{
|
797
|
-
assert ( is_string($attribute) );
|
798
|
-
assert ( is_numeric($min) );
|
799
|
-
assert ( is_numeric($max) );
|
800
|
-
assert ( $min<=$max );
|
801
|
-
|
802
|
-
$this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
|
803
|
-
}
|
804
|
-
|
805
|
-
/// set float range filter
|
806
|
-
/// only match records if $attribute value is beetwen $min and $max (inclusive)
|
807
|
-
function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false )
|
808
|
-
{
|
809
|
-
assert ( is_string($attribute) );
|
810
|
-
assert ( is_float($min) );
|
811
|
-
assert ( is_float($max) );
|
812
|
-
assert ( $min<=$max );
|
813
|
-
|
814
|
-
$this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max );
|
815
|
-
}
|
816
|
-
|
817
|
-
/// setup anchor point for geosphere distance calculations
|
818
|
-
/// required to use @geodist in filters and sorting
|
819
|
-
/// latitude and longitude must be in radians
|
820
|
-
function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long )
|
821
|
-
{
|
822
|
-
assert ( is_string($attrlat) );
|
823
|
-
assert ( is_string($attrlong) );
|
824
|
-
assert ( is_float($lat) );
|
825
|
-
assert ( is_float($long) );
|
826
|
-
|
827
|
-
$this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long );
|
828
|
-
}
|
829
|
-
|
830
|
-
/// set grouping attribute and function
|
831
|
-
function SetGroupBy ( $attribute, $func, $groupsort="@group desc" )
|
832
|
-
{
|
833
|
-
assert ( is_string($attribute) );
|
834
|
-
assert ( is_string($groupsort) );
|
835
|
-
assert ( $func==SPH_GROUPBY_DAY
|
836
|
-
|| $func==SPH_GROUPBY_WEEK
|
837
|
-
|| $func==SPH_GROUPBY_MONTH
|
838
|
-
|| $func==SPH_GROUPBY_YEAR
|
839
|
-
|| $func==SPH_GROUPBY_ATTR
|
840
|
-
|| $func==SPH_GROUPBY_ATTRPAIR );
|
841
|
-
|
842
|
-
$this->_groupby = $attribute;
|
843
|
-
$this->_groupfunc = $func;
|
844
|
-
$this->_groupsort = $groupsort;
|
845
|
-
}
|
846
|
-
|
847
|
-
/// set count-distinct attribute for group-by queries
|
848
|
-
function SetGroupDistinct ( $attribute )
|
849
|
-
{
|
850
|
-
assert ( is_string($attribute) );
|
851
|
-
$this->_groupdistinct = $attribute;
|
852
|
-
}
|
853
|
-
|
854
|
-
/// set distributed retries count and delay
|
855
|
-
function SetRetries ( $count, $delay=0 )
|
856
|
-
{
|
857
|
-
assert ( is_int($count) && $count>=0 );
|
858
|
-
assert ( is_int($delay) && $delay>=0 );
|
859
|
-
$this->_retrycount = $count;
|
860
|
-
$this->_retrydelay = $delay;
|
861
|
-
}
|
862
|
-
|
863
|
-
/// set result set format (hash or array; hash by default)
|
864
|
-
/// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs
|
865
|
-
function SetArrayResult ( $arrayresult )
|
866
|
-
{
|
867
|
-
assert ( is_bool($arrayresult) );
|
868
|
-
$this->_arrayresult = $arrayresult;
|
869
|
-
}
|
870
|
-
|
871
|
-
/// set attribute values override
|
872
|
-
/// there can be only one override per attribute
|
873
|
-
/// $values must be a hash that maps document IDs to attribute values
|
874
|
-
function SetOverride ( $attrname, $attrtype, $values )
|
875
|
-
{
|
876
|
-
assert ( is_string ( $attrname ) );
|
877
|
-
assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) );
|
878
|
-
assert ( is_array ( $values ) );
|
879
|
-
|
880
|
-
$this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values );
|
881
|
-
}
|
882
|
-
|
883
|
-
/// set select-list (attributes or expressions), SQL-like syntax
|
884
|
-
function SetSelect ( $select )
|
885
|
-
{
|
886
|
-
assert ( is_string ( $select ) );
|
887
|
-
$this->_select = $select;
|
888
|
-
}
|
889
|
-
|
890
|
-
//////////////////////////////////////////////////////////////////////////////
|
891
|
-
|
892
|
-
/// clear all filters (for multi-queries)
|
893
|
-
function ResetFilters ()
|
894
|
-
{
|
895
|
-
$this->_filters = array();
|
896
|
-
$this->_anchor = array();
|
897
|
-
}
|
898
|
-
|
899
|
-
/// clear groupby settings (for multi-queries)
|
900
|
-
function ResetGroupBy ()
|
901
|
-
{
|
902
|
-
$this->_groupby = "";
|
903
|
-
$this->_groupfunc = SPH_GROUPBY_DAY;
|
904
|
-
$this->_groupsort = "@group desc";
|
905
|
-
$this->_groupdistinct= "";
|
906
|
-
}
|
907
|
-
|
908
|
-
/// clear all attribute value overrides (for multi-queries)
|
909
|
-
function ResetOverrides ()
|
910
|
-
{
|
911
|
-
$this->_overrides = array ();
|
912
|
-
}
|
913
|
-
|
914
|
-
//////////////////////////////////////////////////////////////////////////////
|
915
|
-
|
916
|
-
/// connect to searchd server, run given search query through given indexes,
|
917
|
-
/// and return the search results
|
918
|
-
function Query ( $query, $index="*", $comment="" )
|
919
|
-
{
|
920
|
-
assert ( empty($this->_reqs) );
|
921
|
-
|
922
|
-
$this->AddQuery ( $query, $index, $comment );
|
923
|
-
$results = $this->RunQueries ();
|
924
|
-
$this->_reqs = array (); // just in case it failed too early
|
925
|
-
|
926
|
-
if ( !is_array($results) )
|
927
|
-
return false; // probably network error; error message should be already filled
|
928
|
-
|
929
|
-
$this->_error = $results[0]["error"];
|
930
|
-
$this->_warning = $results[0]["warning"];
|
931
|
-
if ( $results[0]["status"]==SEARCHD_ERROR )
|
932
|
-
return false;
|
933
|
-
else
|
934
|
-
return $results[0];
|
935
|
-
}
|
936
|
-
|
937
|
-
/// helper to pack floats in network byte order
|
938
|
-
function _PackFloat ( $f )
|
939
|
-
{
|
940
|
-
$t1 = pack ( "f", $f ); // machine order
|
941
|
-
list(,$t2) = unpack ( "L*", $t1 ); // int in machine order
|
942
|
-
return pack ( "N", $t2 );
|
943
|
-
}
|
944
|
-
|
945
|
-
/// add query to multi-query batch
|
946
|
-
/// returns index into results array from RunQueries() call
|
947
|
-
function AddQuery ( $query, $index="*", $comment="" )
|
948
|
-
{
|
949
|
-
// mbstring workaround
|
950
|
-
$this->_MBPush ();
|
951
|
-
|
952
|
-
// build request
|
953
|
-
$req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits
|
954
|
-
$req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
|
955
|
-
$req .= pack ( "N", strlen($query) ) . $query; // query itself
|
956
|
-
$req .= pack ( "N", count($this->_weights) ); // weights
|
957
|
-
foreach ( $this->_weights as $weight )
|
958
|
-
$req .= pack ( "N", (int)$weight );
|
959
|
-
$req .= pack ( "N", strlen($index) ) . $index; // indexes
|
960
|
-
$req .= pack ( "N", 1 ); // id64 range marker
|
961
|
-
$req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range
|
962
|
-
|
963
|
-
// filters
|
964
|
-
$req .= pack ( "N", count($this->_filters) );
|
965
|
-
foreach ( $this->_filters as $filter )
|
966
|
-
{
|
967
|
-
$req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
|
968
|
-
$req .= pack ( "N", $filter["type"] );
|
969
|
-
switch ( $filter["type"] )
|
970
|
-
{
|
971
|
-
case SPH_FILTER_VALUES:
|
972
|
-
$req .= pack ( "N", count($filter["values"]) );
|
973
|
-
foreach ( $filter["values"] as $value )
|
974
|
-
$req .= sphPackI64 ( $value );
|
975
|
-
break;
|
976
|
-
|
977
|
-
case SPH_FILTER_RANGE:
|
978
|
-
$req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
|
979
|
-
break;
|
980
|
-
|
981
|
-
case SPH_FILTER_FLOATRANGE:
|
982
|
-
$req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
|
983
|
-
break;
|
984
|
-
|
985
|
-
default:
|
986
|
-
assert ( 0 && "internal error: unhandled filter type" );
|
987
|
-
}
|
988
|
-
$req .= pack ( "N", $filter["exclude"] );
|
989
|
-
}
|
990
|
-
|
991
|
-
// group-by clause, max-matches count, group-sort clause, cutoff count
|
992
|
-
$req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby;
|
993
|
-
$req .= pack ( "N", $this->_maxmatches );
|
994
|
-
$req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort;
|
995
|
-
$req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay );
|
996
|
-
$req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct;
|
997
|
-
|
998
|
-
// anchor point
|
999
|
-
if ( empty($this->_anchor) )
|
1000
|
-
{
|
1001
|
-
$req .= pack ( "N", 0 );
|
1002
|
-
} else
|
1003
|
-
{
|
1004
|
-
$a =& $this->_anchor;
|
1005
|
-
$req .= pack ( "N", 1 );
|
1006
|
-
$req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"];
|
1007
|
-
$req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"];
|
1008
|
-
$req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] );
|
1009
|
-
}
|
1010
|
-
|
1011
|
-
// per-index weights
|
1012
|
-
$req .= pack ( "N", count($this->_indexweights) );
|
1013
|
-
foreach ( $this->_indexweights as $idx=>$weight )
|
1014
|
-
$req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight );
|
1015
|
-
|
1016
|
-
// max query time
|
1017
|
-
$req .= pack ( "N", $this->_maxquerytime );
|
1018
|
-
|
1019
|
-
// per-field weights
|
1020
|
-
$req .= pack ( "N", count($this->_fieldweights) );
|
1021
|
-
foreach ( $this->_fieldweights as $field=>$weight )
|
1022
|
-
$req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight );
|
1023
|
-
|
1024
|
-
// comment
|
1025
|
-
$req .= pack ( "N", strlen($comment) ) . $comment;
|
1026
|
-
|
1027
|
-
// attribute overrides
|
1028
|
-
$req .= pack ( "N", count($this->_overrides) );
|
1029
|
-
foreach ( $this->_overrides as $key => $entry )
|
1030
|
-
{
|
1031
|
-
$req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"];
|
1032
|
-
$req .= pack ( "NN", $entry["type"], count($entry["values"]) );
|
1033
|
-
foreach ( $entry["values"] as $id=>$val )
|
1034
|
-
{
|
1035
|
-
assert ( is_numeric($id) );
|
1036
|
-
assert ( is_numeric($val) );
|
1037
|
-
|
1038
|
-
$req .= sphPackU64 ( $id );
|
1039
|
-
switch ( $entry["type"] )
|
1040
|
-
{
|
1041
|
-
case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break;
|
1042
|
-
case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break;
|
1043
|
-
default: $req .= pack ( "N", $val ); break;
|
1044
|
-
}
|
1045
|
-
}
|
1046
|
-
}
|
1047
|
-
|
1048
|
-
// select-list
|
1049
|
-
$req .= pack ( "N", strlen($this->_select) ) . $this->_select;
|
1050
|
-
|
1051
|
-
// mbstring workaround
|
1052
|
-
$this->_MBPop ();
|
1053
|
-
|
1054
|
-
// store request to requests array
|
1055
|
-
$this->_reqs[] = $req;
|
1056
|
-
return count($this->_reqs)-1;
|
1057
|
-
}
|
1058
|
-
|
1059
|
-
/// connect to searchd, run queries batch, and return an array of result sets
|
1060
|
-
function RunQueries ()
|
1061
|
-
{
|
1062
|
-
if ( empty($this->_reqs) )
|
1063
|
-
{
|
1064
|
-
$this->_error = "no queries defined, issue AddQuery() first";
|
1065
|
-
return false;
|
1066
|
-
}
|
1067
|
-
|
1068
|
-
// mbstring workaround
|
1069
|
-
$this->_MBPush ();
|
1070
|
-
|
1071
|
-
if (!( $fp = $this->_Connect() ))
|
1072
|
-
{
|
1073
|
-
$this->_MBPop ();
|
1074
|
-
return false;
|
1075
|
-
}
|
1076
|
-
|
1077
|
-
// send query, get response
|
1078
|
-
$nreqs = count($this->_reqs);
|
1079
|
-
$req = join ( "", $this->_reqs );
|
1080
|
-
$len = 4+strlen($req);
|
1081
|
-
$req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header
|
1082
|
-
|
1083
|
-
if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
|
1084
|
-
!( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
|
1085
|
-
{
|
1086
|
-
$this->_MBPop ();
|
1087
|
-
return false;
|
1088
|
-
}
|
1089
|
-
|
1090
|
-
// query sent ok; we can reset reqs now
|
1091
|
-
$this->_reqs = array ();
|
1092
|
-
|
1093
|
-
// parse and return response
|
1094
|
-
return $this->_ParseSearchResponse ( $response, $nreqs );
|
1095
|
-
}
|
1096
|
-
|
1097
|
-
/// parse and return search query (or queries) response
|
1098
|
-
function _ParseSearchResponse ( $response, $nreqs )
|
1099
|
-
{
|
1100
|
-
$p = 0; // current position
|
1101
|
-
$max = strlen($response); // max position for checks, to protect against broken responses
|
1102
|
-
|
1103
|
-
$results = array ();
|
1104
|
-
for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ )
|
1105
|
-
{
|
1106
|
-
$results[] = array();
|
1107
|
-
$result =& $results[$ires];
|
1108
|
-
|
1109
|
-
$result["error"] = "";
|
1110
|
-
$result["warning"] = "";
|
1111
|
-
|
1112
|
-
// extract status
|
1113
|
-
list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1114
|
-
$result["status"] = $status;
|
1115
|
-
if ( $status!=SEARCHD_OK )
|
1116
|
-
{
|
1117
|
-
list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1118
|
-
$message = substr ( $response, $p, $len ); $p += $len;
|
1119
|
-
|
1120
|
-
if ( $status==SEARCHD_WARNING )
|
1121
|
-
{
|
1122
|
-
$result["warning"] = $message;
|
1123
|
-
} else
|
1124
|
-
{
|
1125
|
-
$result["error"] = $message;
|
1126
|
-
continue;
|
1127
|
-
}
|
1128
|
-
}
|
1129
|
-
|
1130
|
-
// read schema
|
1131
|
-
$fields = array ();
|
1132
|
-
$attrs = array ();
|
1133
|
-
|
1134
|
-
list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1135
|
-
while ( $nfields-->0 && $p<$max )
|
1136
|
-
{
|
1137
|
-
list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1138
|
-
$fields[] = substr ( $response, $p, $len ); $p += $len;
|
1139
|
-
}
|
1140
|
-
$result["fields"] = $fields;
|
1141
|
-
|
1142
|
-
list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1143
|
-
while ( $nattrs-->0 && $p<$max )
|
1144
|
-
{
|
1145
|
-
list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1146
|
-
$attr = substr ( $response, $p, $len ); $p += $len;
|
1147
|
-
list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1148
|
-
$attrs[$attr] = $type;
|
1149
|
-
}
|
1150
|
-
$result["attrs"] = $attrs;
|
1151
|
-
|
1152
|
-
// read match count
|
1153
|
-
list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1154
|
-
list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1155
|
-
|
1156
|
-
// read matches
|
1157
|
-
$idx = -1;
|
1158
|
-
while ( $count-->0 && $p<$max )
|
1159
|
-
{
|
1160
|
-
// index into result array
|
1161
|
-
$idx++;
|
1162
|
-
|
1163
|
-
// parse document id and weight
|
1164
|
-
if ( $id64 )
|
1165
|
-
{
|
1166
|
-
$doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8;
|
1167
|
-
list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1168
|
-
}
|
1169
|
-
else
|
1170
|
-
{
|
1171
|
-
list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
|
1172
|
-
substr ( $response, $p, 8 ) ) );
|
1173
|
-
$p += 8;
|
1174
|
-
|
1175
|
-
if ( PHP_INT_SIZE>=8 )
|
1176
|
-
{
|
1177
|
-
// x64 route, workaround broken unpack() in 5.2.2+
|
1178
|
-
if ( $doc<0 ) $doc += (1<<32);
|
1179
|
-
} else
|
1180
|
-
{
|
1181
|
-
// x32 route, workaround php signed/unsigned braindamage
|
1182
|
-
$doc = sprintf ( "%u", $doc );
|
1183
|
-
}
|
1184
|
-
}
|
1185
|
-
$weight = sprintf ( "%u", $weight );
|
1186
|
-
|
1187
|
-
// create match entry
|
1188
|
-
if ( $this->_arrayresult )
|
1189
|
-
$result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight );
|
1190
|
-
else
|
1191
|
-
$result["matches"][$doc]["weight"] = $weight;
|
1192
|
-
|
1193
|
-
// parse and create attributes
|
1194
|
-
$attrvals = array ();
|
1195
|
-
foreach ( $attrs as $attr=>$type )
|
1196
|
-
{
|
1197
|
-
// handle 64bit ints
|
1198
|
-
if ( $type==SPH_ATTR_BIGINT )
|
1199
|
-
{
|
1200
|
-
$attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
|
1201
|
-
continue;
|
1202
|
-
}
|
1203
|
-
|
1204
|
-
// handle floats
|
1205
|
-
if ( $type==SPH_ATTR_FLOAT )
|
1206
|
-
{
|
1207
|
-
list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1208
|
-
list(,$fval) = unpack ( "f*", pack ( "L", $uval ) );
|
1209
|
-
$attrvals[$attr] = $fval;
|
1210
|
-
continue;
|
1211
|
-
}
|
1212
|
-
|
1213
|
-
// handle everything else as unsigned ints
|
1214
|
-
list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1215
|
-
if ( $type & SPH_ATTR_MULTI )
|
1216
|
-
{
|
1217
|
-
$attrvals[$attr] = array ();
|
1218
|
-
$nvalues = $val;
|
1219
|
-
while ( $nvalues-->0 && $p<$max )
|
1220
|
-
{
|
1221
|
-
list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1222
|
-
$attrvals[$attr][] = sprintf ( "%u", $val );
|
1223
|
-
}
|
1224
|
-
} else
|
1225
|
-
{
|
1226
|
-
$attrvals[$attr] = sprintf ( "%u", $val );
|
1227
|
-
}
|
1228
|
-
}
|
1229
|
-
|
1230
|
-
if ( $this->_arrayresult )
|
1231
|
-
$result["matches"][$idx]["attrs"] = $attrvals;
|
1232
|
-
else
|
1233
|
-
$result["matches"][$doc]["attrs"] = $attrvals;
|
1234
|
-
}
|
1235
|
-
|
1236
|
-
list ( $total, $total_found, $msecs, $words ) =
|
1237
|
-
array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) );
|
1238
|
-
$result["total"] = sprintf ( "%u", $total );
|
1239
|
-
$result["total_found"] = sprintf ( "%u", $total_found );
|
1240
|
-
$result["time"] = sprintf ( "%.3f", $msecs/1000 );
|
1241
|
-
$p += 16;
|
1242
|
-
|
1243
|
-
while ( $words-->0 && $p<$max )
|
1244
|
-
{
|
1245
|
-
list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1246
|
-
$word = substr ( $response, $p, $len ); $p += $len;
|
1247
|
-
list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
|
1248
|
-
$result["words"][$word] = array (
|
1249
|
-
"docs"=>sprintf ( "%u", $docs ),
|
1250
|
-
"hits"=>sprintf ( "%u", $hits ) );
|
1251
|
-
}
|
1252
|
-
}
|
1253
|
-
|
1254
|
-
$this->_MBPop ();
|
1255
|
-
return $results;
|
1256
|
-
}
|
1257
|
-
|
1258
|
-
/////////////////////////////////////////////////////////////////////////////
|
1259
|
-
// excerpts generation
|
1260
|
-
/////////////////////////////////////////////////////////////////////////////
|
1261
|
-
|
1262
|
-
/// connect to searchd server, and generate exceprts (snippets)
|
1263
|
-
/// of given documents for given query. returns false on failure,
|
1264
|
-
/// an array of snippets on success
|
1265
|
-
function BuildExcerpts ( $docs, $index, $words, $opts=array() )
|
1266
|
-
{
|
1267
|
-
assert ( is_array($docs) );
|
1268
|
-
assert ( is_string($index) );
|
1269
|
-
assert ( is_string($words) );
|
1270
|
-
assert ( is_array($opts) );
|
1271
|
-
|
1272
|
-
$this->_MBPush ();
|
1273
|
-
|
1274
|
-
if (!( $fp = $this->_Connect() ))
|
1275
|
-
{
|
1276
|
-
$this->_MBPop();
|
1277
|
-
return false;
|
1278
|
-
}
|
1279
|
-
|
1280
|
-
/////////////////
|
1281
|
-
// fixup options
|
1282
|
-
/////////////////
|
1283
|
-
|
1284
|
-
if ( !isset($opts["before_match"]) ) $opts["before_match"] = "<b>";
|
1285
|
-
if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>";
|
1286
|
-
if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
|
1287
|
-
if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
|
1288
|
-
if ( !isset($opts["around"]) ) $opts["around"] = 5;
|
1289
|
-
if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
|
1290
|
-
if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
|
1291
|
-
if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
|
1292
|
-
if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
|
1293
|
-
|
1294
|
-
/////////////////
|
1295
|
-
// build request
|
1296
|
-
/////////////////
|
1297
|
-
|
1298
|
-
// v.1.0 req
|
1299
|
-
$flags = 1; // remove spaces
|
1300
|
-
if ( $opts["exact_phrase"] ) $flags |= 2;
|
1301
|
-
if ( $opts["single_passage"] ) $flags |= 4;
|
1302
|
-
if ( $opts["use_boundaries"] ) $flags |= 8;
|
1303
|
-
if ( $opts["weight_order"] ) $flags |= 16;
|
1304
|
-
$req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
|
1305
|
-
$req .= pack ( "N", strlen($index) ) . $index; // req index
|
1306
|
-
$req .= pack ( "N", strlen($words) ) . $words; // req words
|
1307
|
-
|
1308
|
-
// options
|
1309
|
-
$req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
|
1310
|
-
$req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
|
1311
|
-
$req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
|
1312
|
-
$req .= pack ( "N", (int)$opts["limit"] );
|
1313
|
-
$req .= pack ( "N", (int)$opts["around"] );
|
1314
|
-
|
1315
|
-
// documents
|
1316
|
-
$req .= pack ( "N", count($docs) );
|
1317
|
-
foreach ( $docs as $doc )
|
1318
|
-
{
|
1319
|
-
assert ( is_string($doc) );
|
1320
|
-
$req .= pack ( "N", strlen($doc) ) . $doc;
|
1321
|
-
}
|
1322
|
-
|
1323
|
-
////////////////////////////
|
1324
|
-
// send query, get response
|
1325
|
-
////////////////////////////
|
1326
|
-
|
1327
|
-
$len = strlen($req);
|
1328
|
-
$req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header
|
1329
|
-
if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
|
1330
|
-
!( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) )
|
1331
|
-
{
|
1332
|
-
$this->_MBPop ();
|
1333
|
-
return false;
|
1334
|
-
}
|
1335
|
-
|
1336
|
-
//////////////////
|
1337
|
-
// parse response
|
1338
|
-
//////////////////
|
1339
|
-
|
1340
|
-
$pos = 0;
|
1341
|
-
$res = array ();
|
1342
|
-
$rlen = strlen($response);
|
1343
|
-
for ( $i=0; $i<count($docs); $i++ )
|
1344
|
-
{
|
1345
|
-
list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) );
|
1346
|
-
$pos += 4;
|
1347
|
-
|
1348
|
-
if ( $pos+$len > $rlen )
|
1349
|
-
{
|
1350
|
-
$this->_error = "incomplete reply";
|
1351
|
-
$this->_MBPop ();
|
1352
|
-
return false;
|
1353
|
-
}
|
1354
|
-
$res[] = $len ? substr ( $response, $pos, $len ) : "";
|
1355
|
-
$pos += $len;
|
1356
|
-
}
|
1357
|
-
|
1358
|
-
$this->_MBPop ();
|
1359
|
-
return $res;
|
1360
|
-
}
|
1361
|
-
|
1362
|
-
|
1363
|
-
/////////////////////////////////////////////////////////////////////////////
|
1364
|
-
// keyword generation
|
1365
|
-
/////////////////////////////////////////////////////////////////////////////
|
1366
|
-
|
1367
|
-
/// connect to searchd server, and generate keyword list for a given query
|
1368
|
-
/// returns false on failure,
|
1369
|
-
/// an array of words on success
|
1370
|
-
function BuildKeywords ( $query, $index, $hits )
|
1371
|
-
{
|
1372
|
-
assert ( is_string($query) );
|
1373
|
-
assert ( is_string($index) );
|
1374
|
-
assert ( is_bool($hits) );
|
1375
|
-
|
1376
|
-
// Commented out for testing Riddle
|
1377
|
-
// $this->_MBPush ();
|
1378
|
-
//
|
1379
|
-
// if (!( $fp = $this->_Connect() ))
|
1380
|
-
// {
|
1381
|
-
// $this->_MBPop();
|
1382
|
-
// return false;
|
1383
|
-
// }
|
1384
|
-
|
1385
|
-
/////////////////
|
1386
|
-
// build request
|
1387
|
-
/////////////////
|
1388
|
-
|
1389
|
-
// v.1.0 req
|
1390
|
-
$req = pack ( "N", strlen($query) ) . $query; // req query
|
1391
|
-
$req .= pack ( "N", strlen($index) ) . $index; // req index
|
1392
|
-
$req .= pack ( "N", (int)$hits );
|
1393
|
-
|
1394
|
-
// Line for testing Riddle:
|
1395
|
-
return $req;
|
1396
|
-
|
1397
|
-
////////////////////////////
|
1398
|
-
// send query, get response
|
1399
|
-
////////////////////////////
|
1400
|
-
|
1401
|
-
$len = strlen($req);
|
1402
|
-
$req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header
|
1403
|
-
if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
|
1404
|
-
!( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) )
|
1405
|
-
{
|
1406
|
-
$this->_MBPop ();
|
1407
|
-
return false;
|
1408
|
-
}
|
1409
|
-
|
1410
|
-
//////////////////
|
1411
|
-
// parse response
|
1412
|
-
//////////////////
|
1413
|
-
|
1414
|
-
$pos = 0;
|
1415
|
-
$res = array ();
|
1416
|
-
$rlen = strlen($response);
|
1417
|
-
list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) );
|
1418
|
-
$pos += 4;
|
1419
|
-
for ( $i=0; $i<$nwords; $i++ )
|
1420
|
-
{
|
1421
|
-
list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
|
1422
|
-
$tokenized = $len ? substr ( $response, $pos, $len ) : "";
|
1423
|
-
$pos += $len;
|
1424
|
-
|
1425
|
-
list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4;
|
1426
|
-
$normalized = $len ? substr ( $response, $pos, $len ) : "";
|
1427
|
-
$pos += $len;
|
1428
|
-
|
1429
|
-
$res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized );
|
1430
|
-
|
1431
|
-
if ( $hits )
|
1432
|
-
{
|
1433
|
-
list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) );
|
1434
|
-
$pos += 8;
|
1435
|
-
$res [$i]["docs"] = $ndocs;
|
1436
|
-
$res [$i]["hits"] = $nhits;
|
1437
|
-
}
|
1438
|
-
|
1439
|
-
if ( $pos > $rlen )
|
1440
|
-
{
|
1441
|
-
$this->_error = "incomplete reply";
|
1442
|
-
$this->_MBPop ();
|
1443
|
-
return false;
|
1444
|
-
}
|
1445
|
-
}
|
1446
|
-
|
1447
|
-
$this->_MBPop ();
|
1448
|
-
return $res;
|
1449
|
-
}
|
1450
|
-
|
1451
|
-
function EscapeString ( $string )
|
1452
|
-
{
|
1453
|
-
$from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' );
|
1454
|
-
$to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' );
|
1455
|
-
|
1456
|
-
return str_replace ( $from, $to, $string );
|
1457
|
-
}
|
1458
|
-
|
1459
|
-
/////////////////////////////////////////////////////////////////////////////
|
1460
|
-
// attribute updates
|
1461
|
-
/////////////////////////////////////////////////////////////////////////////
|
1462
|
-
|
1463
|
-
/// batch update given attributes in given rows in given indexes
|
1464
|
-
/// returns amount of updated documents (0 or more) on success, or -1 on failure
|
1465
|
-
function UpdateAttributes ( $index, $attrs, $values, $mva=false )
|
1466
|
-
{
|
1467
|
-
// verify everything
|
1468
|
-
assert ( is_string($index) );
|
1469
|
-
assert ( is_bool($mva) );
|
1470
|
-
|
1471
|
-
assert ( is_array($attrs) );
|
1472
|
-
foreach ( $attrs as $attr )
|
1473
|
-
assert ( is_string($attr) );
|
1474
|
-
|
1475
|
-
assert ( is_array($values) );
|
1476
|
-
foreach ( $values as $id=>$entry )
|
1477
|
-
{
|
1478
|
-
assert ( is_numeric($id) );
|
1479
|
-
assert ( is_array($entry) );
|
1480
|
-
assert ( count($entry)==count($attrs) );
|
1481
|
-
foreach ( $entry as $v )
|
1482
|
-
{
|
1483
|
-
if ( $mva )
|
1484
|
-
{
|
1485
|
-
assert ( is_array($v) );
|
1486
|
-
foreach ( $v as $vv )
|
1487
|
-
assert ( is_int($vv) );
|
1488
|
-
} else
|
1489
|
-
assert ( is_int($v) );
|
1490
|
-
}
|
1491
|
-
}
|
1492
|
-
|
1493
|
-
// build request
|
1494
|
-
$req = pack ( "N", strlen($index) ) . $index;
|
1495
|
-
|
1496
|
-
$req .= pack ( "N", count($attrs) );
|
1497
|
-
foreach ( $attrs as $attr )
|
1498
|
-
{
|
1499
|
-
$req .= pack ( "N", strlen($attr) ) . $attr;
|
1500
|
-
$req .= pack ( "N", $mva ? 1 : 0 );
|
1501
|
-
}
|
1502
|
-
|
1503
|
-
$req .= pack ( "N", count($values) );
|
1504
|
-
foreach ( $values as $id=>$entry )
|
1505
|
-
{
|
1506
|
-
$req .= sphPackU64 ( $id );
|
1507
|
-
foreach ( $entry as $v )
|
1508
|
-
{
|
1509
|
-
$req .= pack ( "N", $mva ? count($v) : $v );
|
1510
|
-
if ( $mva )
|
1511
|
-
foreach ( $v as $vv )
|
1512
|
-
$req .= pack ( "N", $vv );
|
1513
|
-
}
|
1514
|
-
}
|
1515
|
-
|
1516
|
-
// Line for testing Riddle:
|
1517
|
-
return $req;
|
1518
|
-
|
1519
|
-
// connect, send query, get response
|
1520
|
-
if (!( $fp = $this->_Connect() ))
|
1521
|
-
return -1;
|
1522
|
-
|
1523
|
-
$len = strlen($req);
|
1524
|
-
$req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
|
1525
|
-
if ( !$this->_Send ( $fp, $req, $len+8 ) )
|
1526
|
-
return -1;
|
1527
|
-
|
1528
|
-
if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
|
1529
|
-
return -1;
|
1530
|
-
|
1531
|
-
// parse response
|
1532
|
-
list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
|
1533
|
-
return $updated;
|
1534
|
-
}
|
1535
|
-
|
1536
|
-
/////////////////////////////////////////////////////////////////////////////
|
1537
|
-
// persistent connections
|
1538
|
-
/////////////////////////////////////////////////////////////////////////////
|
1539
|
-
|
1540
|
-
function Open()
|
1541
|
-
{
|
1542
|
-
if ( $this->_socket !== false )
|
1543
|
-
{
|
1544
|
-
$this->_error = 'already connected';
|
1545
|
-
return false;
|
1546
|
-
}
|
1547
|
-
if ( !$fp = $this->_Connect() )
|
1548
|
-
return false;
|
1549
|
-
|
1550
|
-
// command, command version = 0, body length = 4, body = 1
|
1551
|
-
$req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 );
|
1552
|
-
if ( !$this->_Send ( $fp, $req, 12 ) )
|
1553
|
-
return false;
|
1554
|
-
|
1555
|
-
$this->_socket = $fp;
|
1556
|
-
return true;
|
1557
|
-
}
|
1558
|
-
|
1559
|
-
function Close()
|
1560
|
-
{
|
1561
|
-
if ( $this->_socket === false )
|
1562
|
-
{
|
1563
|
-
$this->_error = 'not connected';
|
1564
|
-
return false;
|
1565
|
-
}
|
1566
|
-
|
1567
|
-
fclose ( $this->_socket );
|
1568
|
-
$this->_socket = false;
|
1569
|
-
|
1570
|
-
return true;
|
1571
|
-
}
|
1572
|
-
|
1573
|
-
//////////////////////////////////////////////////////////////////////////
|
1574
|
-
// status
|
1575
|
-
//////////////////////////////////////////////////////////////////////////
|
1576
|
-
|
1577
|
-
function Status ()
|
1578
|
-
{
|
1579
|
-
$this->_MBPush ();
|
1580
|
-
if (!( $fp = $this->_Connect() ))
|
1581
|
-
{
|
1582
|
-
$this->_MBPop();
|
1583
|
-
return false;
|
1584
|
-
}
|
1585
|
-
|
1586
|
-
$req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1
|
1587
|
-
if ( !( $this->_Send ( $fp, $req, 12 ) ) ||
|
1588
|
-
!( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) )
|
1589
|
-
{
|
1590
|
-
$this->_MBPop ();
|
1591
|
-
return false;
|
1592
|
-
}
|
1593
|
-
|
1594
|
-
$res = substr ( $response, 4 ); // just ignore length, error handling, etc
|
1595
|
-
$p = 0;
|
1596
|
-
list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8;
|
1597
|
-
|
1598
|
-
$res = array();
|
1599
|
-
for ( $i=0; $i<$rows; $i++ )
|
1600
|
-
for ( $j=0; $j<$cols; $j++ )
|
1601
|
-
{
|
1602
|
-
list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
|
1603
|
-
$res[$i][] = substr ( $response, $p, $len ); $p += $len;
|
1604
|
-
}
|
1605
|
-
|
1606
|
-
$this->_MBPop ();
|
1607
|
-
return $res;
|
1608
|
-
}
|
1609
|
-
|
1610
|
-
// Added for Riddle - code is taken from AddQuery
|
1611
|
-
function FilterOutput()
|
1612
|
-
{
|
1613
|
-
$req = "";
|
1614
|
-
foreach ( $this->_filters as $filter )
|
1615
|
-
{
|
1616
|
-
$req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"];
|
1617
|
-
$req .= pack ( "N", $filter["type"] );
|
1618
|
-
switch ( $filter["type"] )
|
1619
|
-
{
|
1620
|
-
case SPH_FILTER_VALUES:
|
1621
|
-
$req .= pack ( "N", count($filter["values"]) );
|
1622
|
-
foreach ( $filter["values"] as $value )
|
1623
|
-
$req .= sphPackI64 ( $value );
|
1624
|
-
break;
|
1625
|
-
|
1626
|
-
case SPH_FILTER_RANGE:
|
1627
|
-
$req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] );
|
1628
|
-
break;
|
1629
|
-
|
1630
|
-
case SPH_FILTER_FLOATRANGE:
|
1631
|
-
$req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] );
|
1632
|
-
break;
|
1633
|
-
|
1634
|
-
default:
|
1635
|
-
assert ( 0 && "internal error: unhandled filter type" );
|
1636
|
-
}
|
1637
|
-
$req .= pack ( "N", $filter["exclude"] );
|
1638
|
-
}
|
1639
|
-
|
1640
|
-
return $req;
|
1641
|
-
}
|
1642
|
-
}
|
1643
|
-
|
1644
|
-
//
|
1645
|
-
// $Id: sphinxapi.php 1775 2009-04-06 22:15:58Z shodan $
|
1646
|
-
//
|