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