sphinx 0.9.10.2122 → 2.1.1.3711

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/.gitignore +5 -2
  2. data/.travis.yml +8 -0
  3. data/CHANGELOG.md +71 -0
  4. data/Gemfile +4 -0
  5. data/README.md +221 -0
  6. data/Rakefile +40 -35
  7. data/lib/sphinx.rb +3 -8
  8. data/lib/sphinx/client.rb +223 -70
  9. data/lib/sphinx/constants.rb +23 -8
  10. data/lib/sphinx/response.rb +6 -6
  11. data/lib/sphinx/timeout.rb +1 -2
  12. data/lib/sphinx/version.rb +3 -0
  13. data/spec/client_response_spec.rb +77 -64
  14. data/spec/client_spec.rb +68 -31
  15. data/spec/client_validations_spec.rb +61 -7
  16. data/spec/fixtures/requests/default_search.dat +0 -0
  17. data/spec/fixtures/requests/default_search_index.dat +0 -0
  18. data/spec/fixtures/requests/excerpt_custom.dat +0 -0
  19. data/spec/fixtures/requests/excerpt_default.dat +0 -0
  20. data/spec/fixtures/requests/excerpt_flags.dat +0 -0
  21. data/spec/fixtures/requests/field_weights.dat +0 -0
  22. data/spec/fixtures/requests/filter.dat +0 -0
  23. data/spec/fixtures/requests/filter_exclude.dat +0 -0
  24. data/spec/fixtures/requests/filter_float_range.dat +0 -0
  25. data/spec/fixtures/requests/filter_float_range_exclude.dat +0 -0
  26. data/spec/fixtures/requests/filter_range.dat +0 -0
  27. data/spec/fixtures/requests/filter_range_exclude.dat +0 -0
  28. data/spec/fixtures/requests/filter_range_int64.dat +0 -0
  29. data/spec/fixtures/requests/filter_ranges.dat +0 -0
  30. data/spec/fixtures/requests/filters.dat +0 -0
  31. data/spec/fixtures/requests/filters_different.dat +0 -0
  32. data/spec/fixtures/requests/geo_anchor.dat +0 -0
  33. data/spec/fixtures/requests/group_by_attr.dat +0 -0
  34. data/spec/fixtures/requests/group_by_attrpair.dat +0 -0
  35. data/spec/fixtures/requests/group_by_day.dat +0 -0
  36. data/spec/fixtures/requests/group_by_day_sort.dat +0 -0
  37. data/spec/fixtures/requests/group_by_month.dat +0 -0
  38. data/spec/fixtures/requests/group_by_week.dat +0 -0
  39. data/spec/fixtures/requests/group_by_year.dat +0 -0
  40. data/spec/fixtures/requests/group_distinct.dat +0 -0
  41. data/spec/fixtures/requests/id_range.dat +0 -0
  42. data/spec/fixtures/requests/id_range64.dat +0 -0
  43. data/spec/fixtures/requests/index_weights.dat +0 -0
  44. data/spec/fixtures/requests/keywords.dat +0 -0
  45. data/spec/fixtures/requests/limits.dat +0 -0
  46. data/spec/fixtures/requests/limits_cutoff.dat +0 -0
  47. data/spec/fixtures/requests/limits_max.dat +0 -0
  48. data/spec/fixtures/requests/limits_max_cutoff.dat +0 -0
  49. data/spec/fixtures/requests/match_all.dat +0 -0
  50. data/spec/fixtures/requests/match_any.dat +0 -0
  51. data/spec/fixtures/requests/match_boolean.dat +0 -0
  52. data/spec/fixtures/requests/match_extended.dat +0 -0
  53. data/spec/fixtures/requests/match_extended2.dat +0 -0
  54. data/spec/fixtures/requests/match_fullscan.dat +0 -0
  55. data/spec/fixtures/requests/match_phrase.dat +0 -0
  56. data/spec/fixtures/requests/max_query_time.dat +0 -0
  57. data/spec/fixtures/requests/miltiple_queries.dat +0 -0
  58. data/spec/fixtures/requests/outer_select.dat +0 -0
  59. data/spec/fixtures/requests/override.dat +0 -0
  60. data/spec/fixtures/{default_search.php → requests/php/default_search.php} +1 -1
  61. data/spec/fixtures/{default_search_index.php → requests/php/default_search_index.php} +1 -1
  62. data/spec/fixtures/{excerpt_custom.php → requests/php/excerpt_custom.php} +1 -1
  63. data/spec/fixtures/{excerpt_default.php → requests/php/excerpt_default.php} +1 -1
  64. data/spec/fixtures/{excerpt_flags.php → requests/php/excerpt_flags.php} +1 -1
  65. data/spec/fixtures/{field_weights.php → requests/php/field_weights.php} +1 -1
  66. data/spec/fixtures/{filter.php → requests/php/filter.php} +1 -1
  67. data/spec/fixtures/{filter_exclude.php → requests/php/filter_exclude.php} +1 -1
  68. data/spec/fixtures/{filter_float_range.php → requests/php/filter_float_range.php} +1 -1
  69. data/spec/fixtures/{filter_float_range_exclude.php → requests/php/filter_float_range_exclude.php} +1 -1
  70. data/spec/fixtures/{filter_range.php → requests/php/filter_range.php} +1 -1
  71. data/spec/fixtures/{filter_range_exclude.php → requests/php/filter_range_exclude.php} +1 -1
  72. data/spec/fixtures/{filter_range_int64.php → requests/php/filter_range_int64.php} +1 -1
  73. data/spec/fixtures/{filter_ranges.php → requests/php/filter_ranges.php} +1 -1
  74. data/spec/fixtures/{filters.php → requests/php/filters.php} +1 -1
  75. data/spec/fixtures/{filters_different.php → requests/php/filters_different.php} +1 -1
  76. data/spec/fixtures/{geo_anchor.php → requests/php/geo_anchor.php} +1 -1
  77. data/spec/fixtures/{group_by_attr.php → requests/php/group_by_attr.php} +1 -1
  78. data/spec/fixtures/{group_by_attrpair.php → requests/php/group_by_attrpair.php} +1 -1
  79. data/spec/fixtures/{group_by_day.php → requests/php/group_by_day.php} +1 -1
  80. data/spec/fixtures/{group_by_day_sort.php → requests/php/group_by_day_sort.php} +1 -1
  81. data/spec/fixtures/{group_by_month.php → requests/php/group_by_month.php} +1 -1
  82. data/spec/fixtures/{group_by_week.php → requests/php/group_by_week.php} +1 -1
  83. data/spec/fixtures/{group_by_year.php → requests/php/group_by_year.php} +1 -1
  84. data/spec/fixtures/{group_distinct.php → requests/php/group_distinct.php} +1 -1
  85. data/spec/fixtures/{id_range.php → requests/php/id_range.php} +1 -1
  86. data/spec/fixtures/{id_range64.php → requests/php/id_range64.php} +1 -1
  87. data/spec/fixtures/{index_weights.php → requests/php/index_weights.php} +1 -1
  88. data/spec/fixtures/{keywords.php → requests/php/keywords.php} +1 -1
  89. data/spec/fixtures/{limits.php → requests/php/limits.php} +1 -1
  90. data/spec/fixtures/{limits_cutoff.php → requests/php/limits_cutoff.php} +1 -1
  91. data/spec/fixtures/{limits_max.php → requests/php/limits_max.php} +1 -1
  92. data/spec/fixtures/{limits_max_cutoff.php → requests/php/limits_max_cutoff.php} +1 -1
  93. data/spec/fixtures/{match_all.php → requests/php/match_all.php} +1 -1
  94. data/spec/fixtures/{match_any.php → requests/php/match_any.php} +1 -1
  95. data/spec/fixtures/{match_boolean.php → requests/php/match_boolean.php} +1 -1
  96. data/spec/fixtures/{match_extended.php → requests/php/match_extended.php} +1 -1
  97. data/spec/fixtures/{match_extended2.php → requests/php/match_extended2.php} +1 -1
  98. data/spec/fixtures/{match_fullscan.php → requests/php/match_fullscan.php} +1 -1
  99. data/spec/fixtures/{match_phrase.php → requests/php/match_phrase.php} +1 -1
  100. data/spec/fixtures/{max_query_time.php → requests/php/max_query_time.php} +1 -1
  101. data/spec/fixtures/{miltiple_queries.php → requests/php/miltiple_queries.php} +1 -1
  102. data/spec/fixtures/requests/php/outer_select.php +9 -0
  103. data/spec/fixtures/{set_override.php → requests/php/override.php} +1 -1
  104. data/spec/fixtures/requests/php/query_flag.php +13 -0
  105. data/spec/fixtures/requests/php/query_flag_after_reset.php +19 -0
  106. data/spec/fixtures/{ranking_bm25.php → requests/php/ranking_bm25.php} +1 -1
  107. data/spec/fixtures/requests/php/ranking_expr.php +9 -0
  108. data/spec/fixtures/{ranking_fieldmask.php → requests/php/ranking_fieldmask.php} +1 -1
  109. data/spec/fixtures/{ranking_matchany.php → requests/php/ranking_matchany.php} +1 -1
  110. data/spec/fixtures/{ranking_none.php → requests/php/ranking_none.php} +1 -1
  111. data/spec/fixtures/{ranking_proximity.php → requests/php/ranking_proximity.php} +1 -1
  112. data/spec/fixtures/{ranking_proximity_bm25.php → requests/php/ranking_proximity_bm25.php} +1 -1
  113. data/spec/fixtures/{ranking_sph04.php → requests/php/ranking_sph04.php} +1 -1
  114. data/spec/fixtures/{ranking_wordcount.php → requests/php/ranking_wordcount.php} +1 -1
  115. data/spec/fixtures/{retries.php → requests/php/retries.php} +1 -1
  116. data/spec/fixtures/{retries_delay.php → requests/php/retries_delay.php} +1 -1
  117. data/spec/fixtures/{select.php → requests/php/select.php} +1 -1
  118. data/spec/fixtures/{sort_attr_asc.php → requests/php/sort_attr_asc.php} +1 -1
  119. data/spec/fixtures/{sort_attr_desc.php → requests/php/sort_attr_desc.php} +1 -1
  120. data/spec/fixtures/{sort_expr.php → requests/php/sort_expr.php} +1 -1
  121. data/spec/fixtures/{sort_extended.php → requests/php/sort_extended.php} +1 -1
  122. data/spec/fixtures/{sort_relevance.php → requests/php/sort_relevance.php} +1 -1
  123. data/spec/fixtures/{sort_time_segments.php → requests/php/sort_time_segments.php} +1 -1
  124. data/spec/fixtures/{update_attributes.php → requests/php/update_attributes.php} +1 -1
  125. data/spec/fixtures/{update_attributes_mva.php → requests/php/update_attributes_mva.php} +1 -1
  126. data/spec/fixtures/{weights.php → requests/php/weights.php} +1 -1
  127. data/spec/fixtures/requests/query_flag.dat +0 -0
  128. data/spec/fixtures/requests/query_flag_after_reset.dat +0 -0
  129. data/spec/fixtures/requests/ranking_bm25.dat +0 -0
  130. data/spec/fixtures/requests/ranking_expr.dat +0 -0
  131. data/spec/fixtures/requests/ranking_fieldmask.dat +0 -0
  132. data/spec/fixtures/requests/ranking_matchany.dat +0 -0
  133. data/spec/fixtures/requests/ranking_none.dat +0 -0
  134. data/spec/fixtures/requests/ranking_proximity.dat +0 -0
  135. data/spec/fixtures/requests/ranking_proximity_bm25.dat +0 -0
  136. data/spec/fixtures/requests/ranking_sph04.dat +0 -0
  137. data/spec/fixtures/requests/ranking_wordcount.dat +0 -0
  138. data/spec/fixtures/requests/retries.dat +0 -0
  139. data/spec/fixtures/requests/retries_delay.dat +0 -0
  140. data/spec/fixtures/requests/select.dat +0 -0
  141. data/spec/fixtures/requests/sort_attr_asc.dat +0 -0
  142. data/spec/fixtures/requests/sort_attr_desc.dat +0 -0
  143. data/spec/fixtures/requests/sort_expr.dat +0 -0
  144. data/spec/fixtures/requests/sort_extended.dat +0 -0
  145. data/spec/fixtures/requests/sort_relevance.dat +0 -0
  146. data/spec/fixtures/requests/sort_time_segments.dat +0 -0
  147. data/spec/fixtures/requests/update_attributes.dat +0 -0
  148. data/spec/fixtures/requests/update_attributes_mva.dat +0 -0
  149. data/spec/fixtures/requests/weights.dat +0 -0
  150. data/spec/fixtures/responses/build_excerpts.dat +0 -0
  151. data/spec/fixtures/responses/build_keywords.dat +0 -0
  152. data/spec/fixtures/responses/flush_attributes.dat +0 -0
  153. data/spec/fixtures/responses/flush_attrs.dat +2 -0
  154. data/spec/fixtures/responses/open.dat +0 -0
  155. data/spec/fixtures/responses/open_twice.dat +0 -0
  156. data/spec/fixtures/responses/php/build_excerpts.php +8 -0
  157. data/spec/fixtures/responses/php/build_keywords.php +8 -0
  158. data/spec/fixtures/responses/php/flush_attributes.php +8 -0
  159. data/spec/fixtures/responses/php/open.php +8 -0
  160. data/spec/fixtures/responses/php/open_twice.php +9 -0
  161. data/spec/fixtures/responses/php/query.php +8 -0
  162. data/spec/fixtures/responses/php/query_error.php +8 -0
  163. data/spec/fixtures/responses/php/query_id64.php +8 -0
  164. data/spec/fixtures/responses/php/run_queries.php +10 -0
  165. data/spec/fixtures/responses/php/run_queries_error.php +9 -0
  166. data/spec/fixtures/responses/php/status.php +8 -0
  167. data/spec/fixtures/responses/php/update_attributes.php +8 -0
  168. data/spec/fixtures/responses/php/update_attributes_mva.php +8 -0
  169. data/spec/fixtures/responses/query.dat +0 -0
  170. data/spec/fixtures/responses/query_error.dat +0 -0
  171. data/spec/fixtures/responses/query_id64.dat +0 -0
  172. data/spec/fixtures/responses/run_queries.dat +0 -0
  173. data/spec/fixtures/responses/run_queries_error.dat +0 -0
  174. data/spec/fixtures/responses/status.dat +0 -0
  175. data/spec/fixtures/responses/update_attributes.dat +0 -0
  176. data/spec/fixtures/responses/update_attributes_mva.dat +0 -0
  177. data/spec/fixtures/sphinxapi.php +217 -45
  178. data/spec/spec_helper.rb +18 -3
  179. data/spec/sphinx/sphinx-id64.conf +6 -6
  180. data/spec/sphinx/sphinx.conf +6 -6
  181. data/sphinx.gemspec +19 -121
  182. metadata +268 -105
  183. data/README.rdoc +0 -243
  184. data/VERSION.yml +0 -5
  185. data/init.rb +0 -1
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->FlushAttributes();
7
+
8
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Open();
7
+
8
+ ?>
@@ -0,0 +1,9 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Open();
7
+ $cl->Open();
8
+
9
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Query('wifi', 'test1');
7
+
8
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Query('wifi', 'fakeindex');
7
+
8
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Query('wifi', 'test2');
7
+
8
+ ?>
@@ -0,0 +1,10 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->AddQuery('wifi', 'test1');
7
+ $cl->AddQuery('gprs', 'test1');
8
+ $cl->RunQueries();
9
+
10
+ ?>
@@ -0,0 +1,9 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->AddQuery('wifi', 'fakeindex');
7
+ $cl->RunQueries();
8
+
9
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->Status();
7
+
8
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->UpdateAttributes('test1', array('group_id'), array(2 => array(1)));
7
+
8
+ ?>
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ require ("spec/fixtures/sphinxapi.php");
4
+
5
+ $cl = new SphinxClient();
6
+ $cl->UpdateAttributes('test1', array('tags'), array(2 => array(array(1, 2, 3, 4, 5, 6, 7, 8, 9))), true);
7
+
8
+ ?>
@@ -5,7 +5,9 @@
5
5
  //
6
6
 
7
7
  //
8
- // Copyright (c) 2001-2008, Andrew Aksyonoff. All rights reserved.
8
+ // Copyright (c) 2001-2013, Andrew Aksyonoff
9
+ // Copyright (c) 2008-2013, Sphinx Technologies Inc
10
+ // All rights reserved
9
11
  //
10
12
  // This program is free software; you can redistribute it and/or modify
11
13
  // it under the terms of the GNU General Public License. You should have
@@ -24,13 +26,12 @@ define ( "SEARCHD_COMMAND_UPDATE", 2 );
24
26
  define ( "SEARCHD_COMMAND_KEYWORDS", 3 );
25
27
  define ( "SEARCHD_COMMAND_PERSIST", 4 );
26
28
  define ( "SEARCHD_COMMAND_STATUS", 5 );
27
- define ( "SEARCHD_COMMAND_QUERY", 6 );
28
29
  define ( "SEARCHD_COMMAND_FLUSHATTRS", 7 );
29
30
 
30
31
  /// current client-side command implementation versions
31
- define ( "VER_COMMAND_SEARCH", 0x117 );
32
- define ( "VER_COMMAND_EXCERPT", 0x100 );
33
- define ( "VER_COMMAND_UPDATE", 0x102 );
32
+ define ( "VER_COMMAND_SEARCH", 0x11D );
33
+ define ( "VER_COMMAND_EXCERPT", 0x104 );
34
+ define ( "VER_COMMAND_UPDATE", 0x103 );
34
35
  define ( "VER_COMMAND_KEYWORDS", 0x100 );
35
36
  define ( "VER_COMMAND_STATUS", 0x100 );
36
37
  define ( "VER_COMMAND_QUERY", 0x100 );
@@ -60,7 +61,8 @@ define ( "SPH_RANK_PROXIMITY", 4 );
60
61
  define ( "SPH_RANK_MATCHANY", 5 );
61
62
  define ( "SPH_RANK_FIELDMASK", 6 );
62
63
  define ( "SPH_RANK_SPH04", 7 );
63
- define ( "SPH_RANK_TOTAL", 8 );
64
+ define ( "SPH_RANK_EXPR", 8 );
65
+ define ( "SPH_RANK_TOTAL", 9 );
64
66
 
65
67
  /// known sort modes
66
68
  define ( "SPH_SORT_RELEVANCE", 0 );
@@ -83,7 +85,9 @@ define ( "SPH_ATTR_BOOL", 4 );
83
85
  define ( "SPH_ATTR_FLOAT", 5 );
84
86
  define ( "SPH_ATTR_BIGINT", 6 );
85
87
  define ( "SPH_ATTR_STRING", 7 );
86
- define ( "SPH_ATTR_MULTI", 0x40000000 );
88
+ define ( "SPH_ATTR_FACTORS", 1001 );
89
+ define ( "SPH_ATTR_MULTI", 0x40000001 );
90
+ define ( "SPH_ATTR_MULTI64", 0x40000002 );
87
91
 
88
92
  /// known grouping functions
89
93
  define ( "SPH_GROUPBY_DAY", 0 );
@@ -349,6 +353,11 @@ function sphUnpackI64 ( $v )
349
353
  $mq = floor($m/10000000.0);
350
354
  $l = $m - $mq*10000000.0 + $c;
351
355
  $h = $q*4294967296.0 + $r*429.0 + $mq;
356
+ if ( $l==10000000 )
357
+ {
358
+ $l = 0;
359
+ $h += 1;
360
+ }
352
361
 
353
362
  $h = sprintf ( "%.0f", $h );
354
363
  $l = sprintf ( "%07.0f", $l );
@@ -358,11 +367,40 @@ function sphUnpackI64 ( $v )
358
367
  }
359
368
 
360
369
 
370
+ function sphFixUint ( $value )
371
+ {
372
+ if ( PHP_INT_SIZE>=8 )
373
+ {
374
+ // x64 route, workaround broken unpack() in 5.2.2+
375
+ if ( $value<0 ) $value += (1<<32);
376
+ return $value;
377
+ }
378
+ else
379
+ {
380
+ // x32 route, workaround php signed/unsigned braindamage
381
+ return sprintf ( "%u", $value );
382
+ }
383
+ }
384
+
385
+ function SetBit ( $flag, $bit, $on )
386
+ {
387
+ if ( $on )
388
+ {
389
+ $flag += ( 1<<$bit );
390
+ } else
391
+ {
392
+ $reset = 255 ^ ( 1<<$bit );
393
+ $flag = $flag & $reset;
394
+ }
395
+ return $flag;
396
+ }
397
+
398
+
361
399
  /// sphinx searchd client class
362
400
  class SphinxClient
363
401
  {
364
402
  var $_host; ///< searchd host (default is "localhost")
365
- var $_port; ///< searchd port (default is 3312)
403
+ var $_port; ///< searchd port (default is 9312)
366
404
  var $_offset; ///< how many records to seek from result-set start (default is 0)
367
405
  var $_limit; ///< how many records to return from result-set starting at offset (default is 20)
368
406
  var $_mode; ///< query matching mode (default is SPH_MATCH_ALL)
@@ -383,10 +421,17 @@ class SphinxClient
383
421
  var $_anchor; ///< geographical anchor point
384
422
  var $_indexweights; ///< per-index weights
385
423
  var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25)
424
+ var $_rankexpr; ///< ranking mode expression (for SPH_RANK_EXPR)
386
425
  var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit)
387
426
  var $_fieldweights; ///< per-field-name weights
388
427
  var $_overrides; ///< per-query attribute values overrides
389
428
  var $_select; ///< select-list (attributes or expressions, with optional aliases)
429
+ var $_query_flags; ///< per-query various flags
430
+ var $_predictedtime; ///< per-query max_predicted_time
431
+ var $_outerorderby; ///< outer match sort by
432
+ var $_outeroffset; ///< outer offset
433
+ var $_outerlimit; ///< outer limit
434
+ var $_hasouter;
390
435
 
391
436
  var $_error; ///< last error message
392
437
  var $_warning; ///< last warning message
@@ -406,7 +451,7 @@ class SphinxClient
406
451
  {
407
452
  // per-client-object settings
408
453
  $this->_host = "localhost";
409
- $this->_port = 3312;
454
+ $this->_port = 9312;
410
455
  $this->_path = false;
411
456
  $this->_socket = false;
412
457
 
@@ -431,10 +476,17 @@ class SphinxClient
431
476
  $this->_anchor = array ();
432
477
  $this->_indexweights= array ();
433
478
  $this->_ranker = SPH_RANK_PROXIMITY_BM25;
479
+ $this->_rankexpr = "";
434
480
  $this->_maxquerytime= 0;
435
481
  $this->_fieldweights= array();
436
482
  $this->_overrides = array();
437
483
  $this->_select = "*";
484
+ $this->_query_flags = 0;
485
+ $this->_predictedtime = 0;
486
+ $this->_outerorderby = "";
487
+ $this->_outeroffset = 0;
488
+ $this->_outerlimit = 0;
489
+ $this->_hasouter = false;
438
490
 
439
491
  $this->_error = ""; // per-reply fields (for single-query case)
440
492
  $this->_warning = "";
@@ -485,9 +537,10 @@ class SphinxClient
485
537
  return;
486
538
  }
487
539
 
488
- assert ( is_int($port) );
489
540
  $this->_host = $host;
490
- $this->_port = $port;
541
+ if ( is_int($port) )
542
+ if ( $port )
543
+ $this->_port = $port;
491
544
  $this->_path = '';
492
545
 
493
546
  }
@@ -534,13 +587,20 @@ class SphinxClient
534
587
  /// connect to searchd server
535
588
  function _Connect ()
536
589
  {
537
- return fopen('php://stdout', 'w');
538
- }
590
+ if ($_ENV['SPHINX_MOCK_REQUEST']) {
591
+ return fopen('php://stdout', 'w');
592
+ }
539
593
 
540
- function _OldConnect ()
541
- {
542
- if ( $this->_socket !== false )
543
- return $this->_socket;
594
+ if ( $this->_socket!==false )
595
+ {
596
+ // we are in persistent connection mode, so we have a socket
597
+ // however, need to check whether it's still alive
598
+ if ( !@feof ( $this->_socket ) )
599
+ return $this->_socket;
600
+
601
+ // force reopen
602
+ $this->_socket = false;
603
+ }
544
604
 
545
605
  $errno = 0;
546
606
  $errstr = "";
@@ -612,7 +672,7 @@ class SphinxClient
612
672
  $left = $len;
613
673
  while ( $left>0 && !feof($fp) )
614
674
  {
615
- $chunk = fread ( $fp, $left );
675
+ $chunk = fread ( $fp, min ( 8192, $left ) );
616
676
  if ( $chunk )
617
677
  {
618
678
  $response .= $chunk;
@@ -623,6 +683,11 @@ class SphinxClient
623
683
  if ( $this->_socket === false )
624
684
  fclose ( $fp );
625
685
 
686
+ if ($_ENV['SPHINX_MOCK_RESPONSE']) {
687
+ echo $header;
688
+ echo $response;
689
+ }
690
+
626
691
  // check response
627
692
  $read = strlen ( $response );
628
693
  if ( !$response || $read!=$len )
@@ -710,10 +775,12 @@ class SphinxClient
710
775
  }
711
776
 
712
777
  /// set ranking mode
713
- function SetRankingMode ( $ranker )
778
+ function SetRankingMode ( $ranker, $rankexpr="" )
714
779
  {
715
- assert ( $ranker>=0 && $ranker<SPH_RANK_TOTAL );
780
+ assert ( $ranker===0 || $ranker>=1 && $ranker<SPH_RANK_TOTAL );
781
+ assert ( is_string($rankexpr) );
716
782
  $this->_ranker = $ranker;
783
+ $this->_rankexpr = $rankexpr;
717
784
  }
718
785
 
719
786
  /// set matches sorting mode
@@ -892,7 +959,48 @@ class SphinxClient
892
959
  assert ( is_string ( $select ) );
893
960
  $this->_select = $select;
894
961
  }
962
+
963
+ function SetQueryFlag ( $flag_name, $flag_value )
964
+ {
965
+ $known_names = array ( "reverse_scan", "sort_method", "max_predicted_time", "boolean_simplify", "idf" );
966
+ $flags = array (
967
+ "reverse_scan" => array ( 0, 1 ),
968
+ "sort_method" => array ( "pq", "kbuffer" ),
969
+ "max_predicted_time" => array ( 0 ),
970
+ "boolean_simplify" => array ( true, false ),
971
+ "idf" => array ("normalized", "plain" )
972
+ );
973
+
974
+ assert ( isset ( $flag_name, $known_names ) );
975
+ assert ( in_array( $flag_value, $flags[$flag_name], true ) || ( $flag_name=="max_predicted_time" && is_int ( $flag_value ) && $flag_value>=0 ) );
976
+
977
+ if ( $flag_name=="reverse_scan" ) $this->_query_flags = SetBit ( $this->_query_flags, 0, $flag_value==1 );
978
+ if ( $flag_name=="sort_method" ) $this->_query_flags = SetBit ( $this->_query_flags, 1, $flag_value=="kbuffer" );
979
+ if ( $flag_name=="max_predicted_time" )
980
+ {
981
+ $this->_query_flags = SetBit ( $this->_query_flags, 2, $flag_value>0 );
982
+ $this->_predictedtime = (int)$flag_value;
983
+ }
984
+ if ( $flag_name=="boolean_simplify" ) $this->_query_flags = SetBit ( $this->_query_flags, 3, $flag_value );
985
+ if ( $flag_name=="idf" ) $this->_query_flags = SetBit ( $this->_query_flags, 4, $flag_value=="plain" );
986
+ }
987
+
988
+ /// set outer order by parameters
989
+ function SetOuterSelect ( $orderby, $offset, $limit )
990
+ {
991
+ assert ( is_string($orderby) );
992
+ assert ( is_int($offset) );
993
+ assert ( is_int($limit) );
994
+ assert ( $offset>=0 );
995
+ assert ( $limit>0 );
895
996
 
997
+ $this->_outerorderby = $orderby;
998
+ $this->_outeroffset = $offset;
999
+ $this->_outerlimit = $limit;
1000
+ $this->_hasouter = true;
1001
+ }
1002
+
1003
+
896
1004
  //////////////////////////////////////////////////////////////////////////////
897
1005
 
898
1006
  /// clear all filters (for multi-queries)
@@ -916,6 +1024,20 @@ class SphinxClient
916
1024
  {
917
1025
  $this->_overrides = array ();
918
1026
  }
1027
+
1028
+ function ResetQueryFlag ()
1029
+ {
1030
+ $this->_query_flags = 0;
1031
+ $this->_predictedtime = 0;
1032
+ }
1033
+
1034
+ function ResetOuterSelect ()
1035
+ {
1036
+ $this->_outerorderby = '';
1037
+ $this->_outeroffset = 0;
1038
+ $this->_outerlimit = 0;
1039
+ $this->_hasouter = false;
1040
+ }
919
1041
 
920
1042
  //////////////////////////////////////////////////////////////////////////////
921
1043
 
@@ -956,7 +1078,10 @@ class SphinxClient
956
1078
  $this->_MBPush ();
957
1079
 
958
1080
  // build request
959
- $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits
1081
+ $req = pack ( "NNNNN", $this->_query_flags, $this->_offset, $this->_limit, $this->_mode, $this->_ranker );
1082
+ if ( $this->_ranker==SPH_RANK_EXPR )
1083
+ $req .= pack ( "N", strlen($this->_rankexpr) ) . $this->_rankexpr;
1084
+ $req .= pack ( "N", $this->_sort ); // (deprecated) sort mode
960
1085
  $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby;
961
1086
  $req .= pack ( "N", strlen($query) ) . $query; // query itself
962
1087
  $req .= pack ( "N", count($this->_weights) ); // weights
@@ -1053,6 +1178,17 @@ class SphinxClient
1053
1178
 
1054
1179
  // select-list
1055
1180
  $req .= pack ( "N", strlen($this->_select) ) . $this->_select;
1181
+
1182
+ // max_predicted_time
1183
+ if ( $this->_predictedtime>0 )
1184
+ $req .= pack ( "N", (int)$this->_predictedtime );
1185
+
1186
+ $req .= pack ( "N", strlen($this->_outerorderby) ) . $this->_outerorderby;
1187
+ $req .= pack ( "NN", $this->_outeroffset, $this->_outerlimit );
1188
+ if ( $this->_hasouter )
1189
+ $req .= pack ( "N", 1 );
1190
+ else
1191
+ $req .= pack ( "N", 0 );
1056
1192
 
1057
1193
  // mbstring workaround
1058
1194
  $this->_MBPop ();
@@ -1083,8 +1219,8 @@ class SphinxClient
1083
1219
  // send query, get response
1084
1220
  $nreqs = count($this->_reqs);
1085
1221
  $req = join ( "", $this->_reqs );
1086
- $len = 4+strlen($req);
1087
- $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header
1222
+ $len = 8+strlen($req);
1223
+ $req = pack ( "nnNNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, 0, $nreqs ) . $req; // add header
1088
1224
 
1089
1225
  if ( !( $this->_Send ( $fp, $req, $len+8 ) ) ||
1090
1226
  !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) )
@@ -1177,16 +1313,7 @@ class SphinxClient
1177
1313
  list ( $doc, $weight ) = array_values ( unpack ( "N*N*",
1178
1314
  substr ( $response, $p, 8 ) ) );
1179
1315
  $p += 8;
1180
-
1181
- if ( PHP_INT_SIZE>=8 )
1182
- {
1183
- // x64 route, workaround broken unpack() in 5.2.2+
1184
- if ( $doc<0 ) $doc += (1<<32);
1185
- } else
1186
- {
1187
- // x32 route, workaround php signed/unsigned braindamage
1188
- $doc = sprintf ( "%u", $doc );
1189
- }
1316
+ $doc = sphFixUint($doc);
1190
1317
  }
1191
1318
  $weight = sprintf ( "%u", $weight );
1192
1319
 
@@ -1218,22 +1345,35 @@ class SphinxClient
1218
1345
 
1219
1346
  // handle everything else as unsigned ints
1220
1347
  list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1221
- if ( $type & SPH_ATTR_MULTI )
1348
+ if ( $type==SPH_ATTR_MULTI )
1222
1349
  {
1223
1350
  $attrvals[$attr] = array ();
1224
1351
  $nvalues = $val;
1225
1352
  while ( $nvalues-->0 && $p<$max )
1226
1353
  {
1227
1354
  list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
1228
- $attrvals[$attr][] = sprintf ( "%u", $val );
1355
+ $attrvals[$attr][] = sphFixUint($val);
1356
+ }
1357
+ } else if ( $type==SPH_ATTR_MULTI64 )
1358
+ {
1359
+ $attrvals[$attr] = array ();
1360
+ $nvalues = $val;
1361
+ while ( $nvalues>0 && $p<$max )
1362
+ {
1363
+ $attrvals[$attr][] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8;
1364
+ $nvalues -= 2;
1229
1365
  }
1230
1366
  } else if ( $type==SPH_ATTR_STRING )
1231
1367
  {
1232
1368
  $attrvals[$attr] = substr ( $response, $p, $val );
1233
1369
  $p += $val;
1370
+ } else if ( $type==SPH_ATTR_FACTORS )
1371
+ {
1372
+ $attrvals[$attr] = substr ( $response, $p, $val-4 );
1373
+ $p += $val-4;
1234
1374
  } else
1235
1375
  {
1236
- $attrvals[$attr] = sprintf ( "%u", $val );
1376
+ $attrvals[$attr] = sphFixUint($val);
1237
1377
  }
1238
1378
  }
1239
1379
 
@@ -1295,24 +1435,40 @@ class SphinxClient
1295
1435
  if ( !isset($opts["after_match"]) ) $opts["after_match"] = "</b>";
1296
1436
  if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... ";
1297
1437
  if ( !isset($opts["limit"]) ) $opts["limit"] = 256;
1438
+ if ( !isset($opts["limit_passages"]) ) $opts["limit_passages"] = 0;
1439
+ if ( !isset($opts["limit_words"]) ) $opts["limit_words"] = 0;
1298
1440
  if ( !isset($opts["around"]) ) $opts["around"] = 5;
1299
1441
  if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false;
1300
1442
  if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false;
1301
1443
  if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false;
1302
1444
  if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false;
1303
1445
  if ( !isset($opts["query_mode"]) ) $opts["query_mode"] = false;
1446
+ if ( !isset($opts["force_all_words"]) ) $opts["force_all_words"] = false;
1447
+ if ( !isset($opts["start_passage_id"]) ) $opts["start_passage_id"] = 1;
1448
+ if ( !isset($opts["load_files"]) ) $opts["load_files"] = false;
1449
+ if ( !isset($opts["html_strip_mode"]) ) $opts["html_strip_mode"] = "index";
1450
+ if ( !isset($opts["allow_empty"]) ) $opts["allow_empty"] = false;
1451
+ if ( !isset($opts["passage_boundary"]) ) $opts["passage_boundary"] = "none";
1452
+ if ( !isset($opts["emit_zones"]) ) $opts["emit_zones"] = false;
1453
+ if ( !isset($opts["load_files_scattered"]) ) $opts["load_files_scattered"] = false;
1454
+
1304
1455
 
1305
1456
  /////////////////
1306
1457
  // build request
1307
1458
  /////////////////
1308
1459
 
1309
- // v.1.0 req
1460
+ // v.1.2 req
1310
1461
  $flags = 1; // remove spaces
1311
1462
  if ( $opts["exact_phrase"] ) $flags |= 2;
1312
1463
  if ( $opts["single_passage"] ) $flags |= 4;
1313
1464
  if ( $opts["use_boundaries"] ) $flags |= 8;
1314
1465
  if ( $opts["weight_order"] ) $flags |= 16;
1315
1466
  if ( $opts["query_mode"] ) $flags |= 32;
1467
+ if ( $opts["force_all_words"] ) $flags |= 64;
1468
+ if ( $opts["load_files"] ) $flags |= 128;
1469
+ if ( $opts["allow_empty"] ) $flags |= 256;
1470
+ if ( $opts["emit_zones"] ) $flags |= 512;
1471
+ if ( $opts["load_files_scattered"] ) $flags |= 1024;
1316
1472
  $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags
1317
1473
  $req .= pack ( "N", strlen($index) ) . $index; // req index
1318
1474
  $req .= pack ( "N", strlen($words) ) . $words; // req words
@@ -1321,8 +1477,10 @@ class SphinxClient
1321
1477
  $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"];
1322
1478
  $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"];
1323
1479
  $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"];
1324
- $req .= pack ( "N", (int)$opts["limit"] );
1325
- $req .= pack ( "N", (int)$opts["around"] );
1480
+ $req .= pack ( "NN", (int)$opts["limit"], (int)$opts["around"] );
1481
+ $req .= pack ( "NNN", (int)$opts["limit_passages"], (int)$opts["limit_words"], (int)$opts["start_passage_id"] ); // v.1.2
1482
+ $req .= pack ( "N", strlen($opts["html_strip_mode"]) ) . $opts["html_strip_mode"];
1483
+ $req .= pack ( "N", strlen($opts["passage_boundary"]) ) . $opts["passage_boundary"];
1326
1484
 
1327
1485
  // documents
1328
1486
  $req .= pack ( "N", count($docs) );
@@ -1470,11 +1628,12 @@ class SphinxClient
1470
1628
 
1471
1629
  /// batch update given attributes in given rows in given indexes
1472
1630
  /// returns amount of updated documents (0 or more) on success, or -1 on failure
1473
- function UpdateAttributes ( $index, $attrs, $values, $mva=false )
1631
+ function UpdateAttributes ( $index, $attrs, $values, $mva=false, $ignorenonexistent=false )
1474
1632
  {
1475
1633
  // verify everything
1476
1634
  assert ( is_string($index) );
1477
1635
  assert ( is_bool($mva) );
1636
+ assert ( is_bool($ignorenonexistent) );
1478
1637
 
1479
1638
  assert ( is_array($attrs) );
1480
1639
  foreach ( $attrs as $attr )
@@ -1499,9 +1658,11 @@ class SphinxClient
1499
1658
  }
1500
1659
 
1501
1660
  // build request
1661
+ $this->_MBPush ();
1502
1662
  $req = pack ( "N", strlen($index) ) . $index;
1503
1663
 
1504
1664
  $req .= pack ( "N", count($attrs) );
1665
+ $req .= pack ( "N", $ignorenonexistent ? 1 : 0 );
1505
1666
  foreach ( $attrs as $attr )
1506
1667
  {
1507
1668
  $req .= pack ( "N", strlen($attr) ) . $attr;
@@ -1523,18 +1684,28 @@ class SphinxClient
1523
1684
 
1524
1685
  // connect, send query, get response
1525
1686
  if (!( $fp = $this->_Connect() ))
1687
+ {
1688
+ $this->_MBPop ();
1526
1689
  return -1;
1690
+ }
1527
1691
 
1528
1692
  $len = strlen($req);
1529
1693
  $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header
1530
1694
  if ( !$this->_Send ( $fp, $req, $len+8 ) )
1695
+ {
1696
+ $this->_MBPop ();
1531
1697
  return -1;
1698
+ }
1532
1699
 
1533
1700
  if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) ))
1701
+ {
1702
+ $this->_MBPop ();
1534
1703
  return -1;
1704
+ }
1535
1705
 
1536
1706
  // parse response
1537
1707
  list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) );
1708
+ $this->_MBPop ();
1538
1709
  return $updated;
1539
1710
  }
1540
1711
 
@@ -1616,13 +1787,13 @@ class SphinxClient
1616
1787
  // flush
1617
1788
  //////////////////////////////////////////////////////////////////////////
1618
1789
 
1619
- function FlushAttrs ()
1790
+ function FlushAttributes ()
1620
1791
  {
1621
1792
  $this->_MBPush ();
1622
1793
  if (!( $fp = $this->_Connect() ))
1623
1794
  {
1624
1795
  $this->_MBPop();
1625
- return false;
1796
+ return -1;
1626
1797
  }
1627
1798
 
1628
1799
  $req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0
@@ -1630,19 +1801,20 @@ class SphinxClient
1630
1801
  !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) )
1631
1802
  {
1632
1803
  $this->_MBPop ();
1633
- return false;
1804
+ return -1;
1634
1805
  }
1635
1806
 
1636
1807
  $tag = -1;
1637
1808
  if ( strlen($response)==4 )
1638
1809
  list(,$tag) = unpack ( "N*", $response );
1810
+ else
1811
+ $this->_error = "unexpected response length";
1639
1812
 
1640
1813
  $this->_MBPop ();
1641
1814
  return $tag;
1642
1815
  }
1643
-
1644
1816
  }
1645
1817
 
1646
1818
  //
1647
1819
  // $Id$
1648
- //
1820
+ //