riddle 2.1.0 → 2.2.0

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