sphinx 0.9.10.2122 → 2.1.1.3711

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 (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
+ //