test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. //
  2. // Copyright (c) 2017-2022, Manticoreee
  3. // Copyright (c) 2001-2016, Andrew Aksyonoff
  4. // Copyright (c) 2008-2016, Sphinx Technologies Inc
  5. // All rights reserved
  6. //
  7. // This program is free software; you can redistribute it and/or modify
  8. // it under the terms of the GNU Library General Public License. You should
  9. // have received a copy of the LGPL license along with this program; if you
  10. // did not, you can find it at http://www.gnu.org/
  11. //
  12. #include <stdarg.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #if _WIN32
  17. #include <winsock2.h>
  18. #endif
  19. #include "sphinxclient.h"
  20. static sphinx_bool g_smoke = SPH_FALSE;
  21. static int g_failed = 0;
  22. void die ( const char * template, ... )
  23. {
  24. va_list ap;
  25. va_start ( ap, template );
  26. printf ( "FATAL: " );
  27. vprintf ( template, ap );
  28. printf ( "\n" );
  29. va_end ( ap );
  30. exit ( 1 );
  31. }
  32. void net_init ()
  33. {
  34. #if _WIN32
  35. // init WSA on Windows
  36. WSADATA wsa_data;
  37. int wsa_startup_err;
  38. wsa_startup_err = WSAStartup ( WINSOCK_VERSION, &wsa_data );
  39. if ( wsa_startup_err )
  40. die ( "failed to initialize WinSock2: error %d", wsa_startup_err );
  41. #endif
  42. }
  43. void print_name (const char* szname )
  44. {
  45. printf ( "\n***************************\n* %s\n***************************\n", szname );
  46. }
  47. void test_query ( sphinx_client * client, const char * query, const char * index )
  48. {
  49. print_name ( __FUNCTION__);
  50. sphinx_result * res;
  51. int i, j, k, mva_len;
  52. unsigned int * mva;
  53. const char * field_names[2];
  54. int field_weights[2];
  55. field_names[0] = "title";
  56. field_names[1] = "content";
  57. field_weights[0] = 100;
  58. field_weights[1] = 1;
  59. sphinx_set_field_weights ( client, 2, field_names, field_weights );
  60. field_weights[0] = 1;
  61. field_weights[1] = 1;
  62. res = sphinx_query ( client, query, index, NULL );
  63. if ( !res )
  64. {
  65. g_failed += ( res==NULL );
  66. if ( !g_smoke )
  67. die ( "query failed: %s", sphinx_error(client) );
  68. }
  69. if ( g_smoke )
  70. printf ( "Query '%s' retrieved %d of %d matches.\n", query, res->total, res->total_found );
  71. else
  72. printf ( "Query '%s' retrieved %d of %d matches in %d.%03d sec.\n",
  73. query, res->total, res->total_found, res->time_msec/1000, res->time_msec%1000 );
  74. printf ( "Query stats:\n" );
  75. for ( i=0; i<res->num_words; i++ )
  76. printf ( "\t'%s' found %d times in %d documents\n",
  77. res->words[i].word, res->words[i].hits, res->words[i].docs );
  78. printf ( "\nMatches:\n" );
  79. for ( i=0; i<res->num_matches; i++ )
  80. {
  81. printf ( "%d. doc_id=%d, weight=%d", 1+i,
  82. (int)sphinx_get_id ( res, i ), sphinx_get_weight ( res, i ) );
  83. for ( j=0; j<res->num_attrs; j++ )
  84. {
  85. printf ( ", %s=", res->attr_names[j] );
  86. switch ( res->attr_types[j] )
  87. {
  88. case SPH_ATTR_MULTI64:
  89. case SPH_ATTR_MULTI:
  90. mva = sphinx_get_mva ( res, i, j );
  91. mva_len = *mva++;
  92. printf ( "(" );
  93. for ( k=0; k<mva_len; k++ )
  94. printf ( k ? ",%u" : "%u", ( res->attr_types[j]==SPH_ATTR_MULTI ? mva[k] : (unsigned int)sphinx_get_mva64_value ( mva, k ) ) );
  95. printf ( ")" );
  96. break;
  97. case SPH_ATTR_FLOAT: printf ( "%f", sphinx_get_float ( res, i, j ) ); break;
  98. case SPH_ATTR_STRING: printf ( "%s", sphinx_get_string ( res, i, j ) ); break;
  99. default: printf ( "%u", (unsigned int)sphinx_get_int ( res, i, j ) ); break;
  100. }
  101. }
  102. printf ( "\n" );
  103. }
  104. printf ( "\n" );
  105. }
  106. void test_excerpt ( sphinx_client * client )
  107. {
  108. print_name ( __FUNCTION__ );
  109. const char * docs[] =
  110. {
  111. "this is my test text to be highlighted, and for the sake of the testing we need to pump its length somewhat",
  112. "another test text to be highlighted, below limit",
  113. "test number three, without phrase match",
  114. "final test, not only without phrase match, but also above limit and with swapped phrase text test as well"
  115. };
  116. const int ndocs = sizeof(docs)/sizeof(docs[0]);
  117. const char * words = "test text";
  118. const char * index = "test1";
  119. sphinx_excerpt_options opts;
  120. char ** res;
  121. int i;
  122. sphinx_init_excerpt_options ( &opts );
  123. opts.limit = 60;
  124. opts.around = 3;
  125. opts.allow_empty = SPH_FALSE;
  126. res = sphinx_build_excerpts ( client, ndocs, (const char **)docs, index, words, &opts );
  127. if ( !res )
  128. {
  129. g_failed += ( res==NULL );
  130. if ( !g_smoke )
  131. die ( "query failed: %s", sphinx_error(client) );
  132. }
  133. for ( i=0; i<ndocs; i++ )
  134. printf ( "n=%d, res=%s\n", 1+i, res[i] );
  135. }
  136. void test_excerpt_spz ( sphinx_client * client )
  137. {
  138. print_name ( __FUNCTION__ );
  139. const char * docs[] =
  140. {
  141. "<efx_unidentified_table>"
  142. "The institutional investment manager it. Is Filing this report and."
  143. "<efx_test>"
  144. "It is signed hereby represent. That it is all information."
  145. "are It or is"
  146. "</efx_test>"
  147. "<efx_2>"
  148. "cool It is cooler"
  149. "</efx_2>"
  150. "It is another place!"
  151. "</efx_unidentified_table>"
  152. };
  153. const int ndocs = sizeof(docs)/sizeof(docs[0]);
  154. const char * words = "it is";
  155. const char * index = "test1";
  156. sphinx_excerpt_options opts;
  157. char ** res;
  158. int i, j;
  159. sphinx_init_excerpt_options ( &opts );
  160. opts.limit = 150;
  161. opts.limit_passages = 8;
  162. opts.around = 8;
  163. opts.html_strip_mode = "strip";
  164. opts.passage_boundary = "zone";
  165. opts.emit_zones = SPH_TRUE;
  166. for ( j=0; j<2; j++ )
  167. {
  168. if ( j==1 )
  169. {
  170. opts.passage_boundary = "sentence";
  171. opts.emit_zones = SPH_FALSE;
  172. }
  173. printf ( "passage_boundary=%s\n", opts.passage_boundary );
  174. res = sphinx_build_excerpts ( client, ndocs, (const char **)docs, index, words, &opts );
  175. if ( !res )
  176. die ( "query failed: %s", sphinx_error(client) );
  177. for ( i=0; i<ndocs; i++ )
  178. printf ( "n=%d, res=%s\n", 1+i, res[i] );
  179. printf ( "\n" );
  180. }
  181. }
  182. void test_persist_work ( sphinx_client * client )
  183. {
  184. print_name ( __FUNCTION__ );
  185. char * docs[] = { NULL };
  186. const char words[] = "that is";
  187. const char * index = "test1";
  188. const char filler[] = " no need to worry about ";
  189. sphinx_excerpt_options opts;
  190. char ** res;
  191. char * doc;
  192. int i ;
  193. // should be in sync with sphinxclient.c MAX_PACKET_LEN
  194. i = 8*1024*1024 + 50;
  195. docs[0] = malloc ( i );
  196. if ( !docs[0] )
  197. die ( "malloc failed at test_persist_work" );
  198. memcpy ( docs[0], words, sizeof(words)-1 );
  199. doc = docs[0] + sizeof(words)-1;
  200. while ( ( doc + sizeof(filler) )<docs[0]+i )
  201. {
  202. memcpy ( doc, filler, sizeof(filler)-1 );
  203. doc += sizeof(filler)-1;
  204. }
  205. *doc = '\0';
  206. sphinx_open ( client );
  207. for ( i=0; i<2; i++ )
  208. {
  209. if ( i==0 )
  210. {
  211. sphinx_init_excerpt_options ( &opts );
  212. opts.limit = 0;
  213. opts.limit_passages = 0;
  214. opts.around = 0;
  215. opts.html_strip_mode = "none";
  216. } else
  217. {
  218. sphinx_init_excerpt_options ( &opts );
  219. opts.limit = 500;
  220. opts.limit_words = 10;
  221. opts.limit_passages = 2;
  222. opts.around = 5;
  223. opts.html_strip_mode = "none";
  224. *( docs[0]+sizeof(words)+100 ) = '\0';
  225. }
  226. printf ( "n=%d,\t", i );
  227. res = sphinx_build_excerpts ( client, 1, (const char**)docs, index, words, &opts );
  228. if ( !res )
  229. {
  230. g_failed += ( res==NULL && i ); // 1st query fails, 2nd works
  231. printf ( "query failed: %s", sphinx_error(client) );
  232. } else
  233. {
  234. printf ( "res=%s", res[0] );
  235. free ( res );
  236. }
  237. printf ( "\n" );
  238. }
  239. sphinx_close ( client );
  240. printf ( "\n" );
  241. }
  242. void test_update ( sphinx_client * client, sphinx_uint64_t id )
  243. {
  244. print_name ( __FUNCTION__ );
  245. const char * attr = "group_id";
  246. const sphinx_int64_t val = 123;
  247. int res;
  248. res = sphinx_update_attributes ( client, "test1", 1, &attr, 1, &id, &val );
  249. if ( res<0 )
  250. g_failed++;
  251. if ( res<0 )
  252. printf ( "update failed: %s\n\n", sphinx_error(client) );
  253. else
  254. printf ( "update success, %d rows updated\n\n", res );
  255. }
  256. void test_update_mva ( sphinx_client * client )
  257. {
  258. print_name ( __FUNCTION__ );
  259. const char * attr = "tag";
  260. const sphinx_uint64_t id = 3;
  261. const unsigned int vals[] = { 7, 77, 177 };
  262. int res;
  263. res = sphinx_update_attributes_mva ( client, "test1", attr, id, sizeof(vals)/sizeof(vals[0]), vals );
  264. if ( res<0 )
  265. g_failed++;
  266. if ( res<0 )
  267. printf ( "update mva failed: %s\n\n", sphinx_error(client) );
  268. else
  269. printf ( "update mva success, %d rows updated\n\n", res );
  270. }
  271. void test_keywords ( sphinx_client * client )
  272. {
  273. print_name ( __FUNCTION__ );
  274. int i, nwords;
  275. sphinx_keyword_info * words;
  276. words = sphinx_build_keywords ( client, "hello test one", "test1", SPH_TRUE, &nwords );
  277. g_failed += ( words==NULL );
  278. if ( !words )
  279. {
  280. printf ( "build_keywords failed: %s\n\n", sphinx_error(client) );
  281. } else
  282. {
  283. printf ( "build_keywords result:\n" );
  284. for ( i=0; i<nwords; i++ )
  285. printf ( "%d. tokenized=%s, normalized=%s, docs=%d, hits=%d\n", 1+i,
  286. words[i].tokenized, words[i].normalized,
  287. words[i].num_docs, words[i].num_hits );
  288. printf ( "\n" );
  289. }
  290. }
  291. void test_status ( sphinx_client * client )
  292. {
  293. print_name ( __FUNCTION__ );
  294. int num_rows, num_cols, i, j, k;
  295. char ** status;
  296. status = sphinx_status ( client, &num_rows, &num_cols );
  297. if ( !status )
  298. {
  299. g_failed++;
  300. printf ( "status failed: %s\n\n", sphinx_error(client) );
  301. return;
  302. }
  303. k = 0;
  304. for ( i=0; i<num_rows; i++ )
  305. {
  306. if ( !g_smoke ||
  307. ( strstr ( status[k], "time" )==NULL
  308. && strstr ( status[k], "wall" )==NULL
  309. && strstr ( status[k], "wait" )==NULL
  310. && strstr ( status[k], "connect_avg" )==NULL
  311. && strstr ( status[k], "connect_max")==NULL
  312. && strstr( status[k], "version" )==NULL
  313. && strstr( status[k], "qcache_max_bytes" )==NULL
  314. && strstr( status[k], "mysql_version" )==NULL
  315. && strstr ( status[k], "workers_total" )==NULL
  316. && strstr ( status[k], "workers_active" )==NULL
  317. && strstr ( status[k], "agent_tfo")==NULL
  318. && strstr ( status[k], "load") == NULL
  319. && strstr ( status[k], "connect_count" )==NULL ))
  320. {
  321. for ( j=0; j<num_cols; j++, k++ )
  322. printf ( ( j==0 ) ? "%s:" : " %s", status[k] );
  323. printf ( "\n" );
  324. } else
  325. k += num_cols;
  326. }
  327. printf ( "\n" );
  328. sphinx_status_destroy ( status, num_rows, num_cols );
  329. }
  330. void test_group_by ( sphinx_client * client, const char * attr )
  331. {
  332. print_name ( __FUNCTION__ );
  333. sphinx_set_groupby ( client, attr, SPH_GROUPBY_ATTR, "@group asc" );
  334. test_query ( client, "is", "test1" );
  335. sphinx_reset_groupby ( client );
  336. }
  337. void test_filter ( sphinx_client * client )
  338. {
  339. print_name ( __FUNCTION__ );
  340. const char * attr_group = "group_id";
  341. const char * attr_mva = "tag";
  342. sphinx_int64_t filter_group = { 1 };
  343. sphinx_int64_t filter_mva = { 7 };
  344. int i;
  345. sphinx_bool mva;
  346. for ( i=0; i<2; i++ )
  347. {
  348. mva = ( i==1 );
  349. sphinx_add_filter ( client, mva ? attr_mva : attr_group, 1, mva ? &filter_mva : &filter_group, SPH_FALSE );
  350. test_query ( client, "is", "test1" );
  351. sphinx_reset_filters ( client );
  352. }
  353. }
  354. void title ( const char * name )
  355. {
  356. if ( g_smoke || !name )
  357. return;
  358. printf ( "-> %s <-\n\n", name );
  359. }
  360. int main ( int argc, char ** argv )
  361. {
  362. int i, port = 0;
  363. sphinx_client * client;
  364. // sphinx_uint64_t override_docid = 2;
  365. // unsigned int override_value = 2000;
  366. for ( i=1; i<argc; i++ )
  367. {
  368. if ( strcmp ( argv[i], "--smoke" )==0 )
  369. g_smoke = SPH_TRUE;
  370. else if ( strcmp ( argv[i], "--port" )==0 && i+1<argc )
  371. port = (int)strtoul ( argv[i+1], NULL, 10 );
  372. }
  373. net_init ();
  374. client = sphinx_create ( SPH_TRUE );
  375. if ( !client )
  376. die ( "failed to create client" );
  377. if ( port )
  378. sphinx_set_server ( client, "127.0.0.1", port );
  379. sphinx_set_match_mode ( client, SPH_MATCH_EXTENDED2 );
  380. sphinx_set_sort_mode ( client, SPH_SORT_RELEVANCE, NULL );
  381. // excerpt + keywords
  382. title ( "excerpt" );
  383. test_excerpt ( client );
  384. test_excerpt_spz ( client );
  385. title ( "keywords" );
  386. test_keywords ( client );
  387. // search phase 0
  388. title ( "search phase 0" );
  389. test_query ( client, "is", "test1" );
  390. test_query ( client, "is test", "test1" );
  391. test_query ( client, "test number", "test1" );
  392. test_query ( client, "is", "dist" );
  393. // group_by (attr; mva) + filter
  394. title ( "group_by (attr; mva) + filter" );
  395. title ( "group_by attr" );
  396. test_group_by ( client, "group_id" );
  397. // group_by mva
  398. title ( "group_by mva" );
  399. test_group_by ( client, "tag" );
  400. // filter
  401. title ( "filter" );
  402. test_filter ( client );
  403. // update (attr; mva) + sort (descending id)
  404. title ( "update (attr; mva) + sort (descending id)" );
  405. test_update ( client, 2 );
  406. test_update_mva ( client );
  407. sphinx_set_sort_mode ( client, SPH_SORT_EXTENDED, "idd desc" );
  408. test_query ( client, "is", "test1" );
  409. // persistence connection
  410. sphinx_open ( client );
  411. // update (attr) + sort (default)
  412. title ( "update (attr) + sort (default)" );
  413. test_update ( client, 4 );
  414. test_update ( client, 3 );
  415. sphinx_set_sort_mode ( client, SPH_SORT_RELEVANCE, NULL );
  416. test_query ( client, "is", "test1" );
  417. sphinx_cleanup ( client );
  418. // group_by (attr; mva) + filter + post update
  419. title ( "group_by (attr; mva) + filter + post update" );
  420. title ( "group_by attr" );
  421. test_group_by ( client, "group_id" );
  422. title ( "group_by mva" );
  423. test_group_by ( client, "tag" );
  424. title ( "filter" );
  425. test_filter ( client );
  426. // select
  427. title ( "select" );
  428. sphinx_set_select ( client, "*, group_id*1000+@id*10 AS q" );
  429. test_query ( client, "is", "test1" );
  430. // override
  431. // title ( "override" );
  432. // sphinx_add_override ( client, "group_id", &override_docid, 1, &override_value );
  433. // test_query ( client, "is", "test1" );
  434. // group_by (override attr)
  435. title ( "group_by (override attr)" );
  436. test_group_by ( client, "group_id" );
  437. sphinx_close ( client );
  438. test_status ( client );
  439. // long queries
  440. title ( "long queries vs persist connection" );
  441. test_persist_work ( client );
  442. sphinx_destroy ( client );
  443. if ( g_smoke && g_failed )
  444. {
  445. printf ( "%d error(s)\n", g_failed );
  446. exit ( 1 );
  447. }
  448. return 0;
  449. }