之前搭建的裤子库是单表的,建索引的时候也没考虑什么后续扩展,有小伙伴表示要玩多表查询,于是研究了下……

为嘛不用增量索引呢?本来一个表就10G多够大了,而且增量索引还得不时添加&合并索引……只是本机搭着玩玩,还是算了吧。

翻到一篇文章,里面说到了配置文件里的继承和重载,对于添加多个数据源还是挺有帮助的,摘抄下:

定义父类email

  
  
  1. source email {
  2. ....
  3. }

定义子类subemail继承email类的所有设置:

  
  
  1. source subemail : email { #除了sourceindex也可以使用继承
  2. ....
  3. }

子类中可以重载email中的设置

  
  
  1. source subemail : email {
  2. sql_host = www.ibos.com.cn #重载主机
  3. sql_query = SELECT * FROM subemail #重载sql_query语句
  4. }

其实继承很少被使用到,但有一个很实用的例子就是有很多数据源使用同一数据库的时候,继承就派上用场了

  
  
  1. source setdb { #setdb类只实现连接数据库
  2. sql_host = localhost
  3. sql_user = root
  4. sql_pass = root
  5. sql_db = ibos
  6. sql_port = 3306
  7. }
  8. souce email : setdb{ #继承setdb类
  9. sql_query = ... #直接写查询语句,而不用再写数据库信息
  10. }
  11. souce diary : setdb {
  12. sql_query = ...
  13. }
  14. souce article : setdb {
  15. sql_query = ...
  16. }
  17. souce forum : setdb {
  18. sql_query = ...
  19. }

然后我在上一次的配置文件基础上改了改,加上了另一个表做数据源。
但是在改 php 文件时发现个问题:之前的源码里用的是 $sql = "select * from table_1 where id in($ids)" ,现在加了另一个表以后就不好搞了。

因为两个表的 ID 字段都是从 1 开始自增的,如果用多表 Union 的话可能把两个表中的同一个 ID 的行取出来。有个解决办法就是把第二个表 ID 的自增起始数值改成第一个表 ID 的结束数值—— 不过这个方法只适用极少的情况……

接着百度了很久无果,谷歌也搜不到…… 后来把搜索关键词换为 coreseek indexes in different tables 就搜到一大堆了 Orz

根据这个 Using Sphinx with PHP with multiple indexes 的回答,把配置文件重新改了下:

  
  
  1. #源定义
  2. source table_1
  3. {
  4. type = mysql
  5. sql_host = localhost
  6. sql_user = test
  7. sql_pass = test
  8. sql_db = sed
  9. sql_port = 3306
  10. sql_query_pre = SET NAMES utf8
  11. sql_query = SELECT `id`, 1 AS table_id, `username`, `password`, `email`, `salt`, `from` FROM table_1
  12. sql_attr_uint = table_id #从SQL读取到的值必须为整数
  13. #sql_attr_timestamp = date_added #从SQL读取到的值必须为整数,作为时间属性
  14. sql_query_info_pre = SET NAMES utf8 #命令行查询时,设置正确的字符集
  15. sql_query_info = SELECT * WHERE ID=$id #命令行查询时,从数据库读取原始数据信息
  16. }
  17. source table_2 : table_1
  18. {
  19. sql_query = SELECT `id`, 2 AS table_id, `username`, `password`, `email`, `salt`, `from` FROM table_2
  20. }
  21. #index定义
  22. index table_1
  23. {
  24. source = table_1 #对应的source名称
  25. path = E:/SQL_DATA/coreseek/var/data/table_1 #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...
  26. docinfo = extern
  27. mlock = 0
  28. morphology = none
  29. min_word_len = 1
  30. ondisk_dict = 1
  31. html_strip = 0
  32. #中文分词配置,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/
  33. #charset_dictpath = /usr/local/mmseg3/etc/ #BSD、Linux环境下设置,/符号结尾
  34. charset_dictpath = E:/SQL_DATA/coreseek/etc/ #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...
  35. charset_type = zh_cn.utf-8
  36. }
  37. index table_2 : table_1
  38. {
  39. source = table_2
  40. path = E:/SQL_DATA/coreseek/var/data/table_2
  41. }
  42. #全局index定义
  43. indexer
  44. {
  45. mem_limit = 1024M
  46. }
  47. #searchd服务定义
  48. searchd
  49. {
  50. listen = 9000
  51. read_timeout = 5
  52. max_children = 30
  53. max_matches = 1000
  54. seamless_rotate = 0
  55. preopen_indexes = 0
  56. unlink_old = 1
  57. pid_file = E:/SQL_DATA/coreseek/var/log/searchd_mysql.pid #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...
  58. log = E:/SQL_DATA/coreseek/var/log/searchd_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...
  59. query_log = E:/SQL_DATA/coreseek/var/log/query_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/...
  60. binlog_path = #关闭binlog日志
  61. }

所以给返回的 matches 加个 table_id 的属性就好了,建好索引后查询时 matches 返回值类似这样:

  
  
  1. ["matches"]=>
  2. array(16) {
  3. [0]=>
  4. array(3) {
  5. ["id"]=>
  6. string(2) "68"
  7. ["weight"]=>
  8. string(1) "2"
  9. ["attrs"]=>
  10. array(1) {
  11. ["table_id"]=>
  12. string(1) "2"
  13. }
  14. }
  15. [1]=>
  16. array(3) {
  17. ["id"]=>
  18. string(3) "350"
  19. ["weight"]=>
  20. string(1) "2"
  21. ["attrs"]=>
  22. array(1) {
  23. ["table_id"]=>
  24. string(1) "1"
  25. }
  26. }

需要注意的是如果之前有把 searchd 注册成服务的话要记得换个端口……

最后改一下用于搜索的 PHP 文件(渣代码勿怪…):

  
  
  1. <?php
  2. // 引用sphinxapi类
  3. require "sphinxapi.php";
  4. //关闭错误提示
  5. error_reporting(E_ALL & ~E_NOTICE);
  6. $num = 0;
  7. if (!empty($_GET) && !empty($_GET['q'])) {
  8. $Keywords = strip_tags(trim($_GET['q']));
  9. if (!empty($_GET['m']) && 1 == $_GET['m']) {
  10. $Keywords = substr(md5($Keywords), 8, 16);
  11. }
  12. if (!empty($_GET['m']) && 2 == $_GET['m']) {
  13. $Keywords = md5($Keywords);
  14. }
  15. $cl = new SphinxClient();
  16. // 返回结果设置
  17. $cl->SetServer('127.0.0.1', 9000);
  18. $cl->SetConnectTimeout(3);
  19. $cl->SetArrayResult(true);
  20. // 设置是否全文匹配
  21. if (!empty($_GET) && !empty($_GET['f'])) {
  22. $cl->SetMatchMode(SPH_MATCH_ALL);
  23. } else {
  24. $cl->SetMatchMode(SPH_MATCH_ANY);
  25. }
  26. if (!empty($_GET) && !empty($_GET['p'])) {
  27. $p = !intval(trim($_GET['p'])) == 0 ? intval(trim($_GET['p'])) - 1 : 0;
  28. $p = $p * 20;
  29. // 我在sed.conf 设置了最大返回结果数1000。但是我在生成页码的时候最多生成20页,我想能满足大部分搜索需求了。
  30. // 以下语句表示从P参数偏移开始每次返回20条。
  31. $cl->setLimits($p, 20);
  32. } else {
  33. $cl->setLimits(0, 20);
  34. }
  35. $res = $cl->Query("$Keywords", "*");
  36. //var_dump($res);
  37. @mysql_connect("localhost", "test", "test"); //数据库账号密码
  38. mysql_select_db("sed"); //数据库库名名
  39. mysql_query("set names utf8");
  40. $tables = ['table_1', 'table_2']; //把表名放入数组
  41. function getResult($id, $table)
  42. {
  43. $sql = "select * from {$table} where id = " . $id;
  44. $result = mysql_query($sql);
  45. while ($row = mysql_fetch_array($result)) {
  46. echo "<tr><td>" . $row['username'] . "</td>";
  47. echo "<td>" . $row['email'] . "</td>";
  48. echo "<td>" . $row['password'] . "</td>";
  49. echo "<td>" . $row['salt'] . "</td>";
  50. echo "<td>" . $row['from'] . "</td></tr>";
  51. }
  52. }
  53. if ($res["total_found"]) {
  54. $num = $res["total_found"];
  55. } else {
  56. $num = 0;
  57. }
  58. }
  59. ?>
  60. <!DOCTYPE html>
  61. <html>
  62. <head>
  63. <title>The Web of Answers</title>
  64. <meta charset="UTF-8">
  65. <meta name="viewport" content="width=device-with,initial-scal=1">
  66. <link href="css/bootstrap.min.css" rel="stylesheet">
  67. <script src="js/jquery.js"></script>
  68. <script src="js/bootstrap.min.js"></script>
  69. <script>
  70. function check(form){
  71. if(form.q.value==""){
  72. alert("Not null !");
  73. form.q.focus();
  74. return false;
  75. }
  76. }
  77. </script>
  78. <style>
  79. h1 {
  80. font-family: Times New Roman, Lucida Handwriting;
  81. }
  82. body {
  83. background-image: url(img/bg.jpg);
  84. }
  85. </style>
  86. </head>
  87. <body>
  88. <div class="container" id="container">
  89. <div id="page-header">
  90. <h1 class="text-center"> The Web of Answers </h1>
  91. </div>
  92. <div class="row">
  93. <form action="" method="get" class="form-horizontal" role="form">
  94. <div id="checkbox" class="col-md-6 col-md-offset-3">
  95. <label class="checkbox-inline">
  96. <input type="checkbox" id="full" name="f" value="1"> 完整匹配
  97. </label>
  98. <label class="checkbox-inline">
  99. <input type="checkbox" id="md5_16" name="m" value="1">
  100. MD5匹配(16位)
  101. </label>
  102. <label class="checkbox-inline">
  103. <input type="checkbox" id="md5_32" name="m" value="2">
  104. MD5匹配(32位)
  105. </label>
  106. </div>
  107. <div class="input-group col-md-6 col-md-offset-3">
  108. <input type="text" class="form-control" name="q" placeholder="请输入" value="<?php echo strip_tags(trim($_GET['q']));?>">
  109. <div class="input-group-btn">
  110. <button type="submit" class="btn btn-primary" onclick="check(form)">Search</button>
  111. </div>
  112. </div>
  113. </form>
  114. </div>
  115. <br>
  116. <?php
  117. if (0 != $num) {
  118. echo "<div class=\"row\">
  119. <div class=\"alert alert-success alert-dismissible col-md-10 col-md-offset-1\" role=\"alert\">
  120. <button type=\"button\" class=\"close\" data-dismiss=\"alert\"><span aria-hidden=\"true\">&times;</span><span class=\"sr-only\">Close</span></button>
  121. 找到与<b>&nbsp{$Keywords}&nbsp</b>相关的结果 {$num} 个。用时 {$res['time']} 秒。</div>";
  122. echo "<div class=\"table-responsive col-md-10 col-md-offset-1\">
  123. <table class=\"table table-striped table-hover\">
  124. <tr>
  125. <th>Username</th>
  126. <th>Email</th>
  127. <th>Password</th>
  128. <th>Salt</th>
  129. <th>From</th>
  130. </tr>";
  131. if (is_array($res["matches"])) {
  132. foreach ($res["matches"] as $docinfo) {
  133. $table_id = $docinfo['attrs']['table_id'];
  134. getResult($docinfo['id'], $tables[$table_id - 1]);
  135. }
  136. }
  137. echo "</table></div></div>";
  138. } else {
  139. if (!empty($_GET) && !empty($_GET['q'])) {
  140. echo "<div class=\"alert alert-warning alert-dismissible col-md-10 col-md-offset-1\" role=\"alert\">
  141. <button type=\"button\" class=\"close\" data-dismiss=\"alert\"><span aria-hidden=\"true\">&times;</span><span class=\"sr-only\">Close</span></button>
  142. 找不到与<b>&nbsp{$Keywords}&nbsp</b>相关的结果。请更换其他关键词试试。</div></div>";
  143. }
  144. }
  145. ?>
  146. <div id="pages">
  147. <center>
  148. <nav>
  149. <ul class="pagination">
  150. <?php
  151. if ($num !== 0) {
  152. $pagecount = (int) ($num / 20);
  153. if (!($num % 20) == 0) {
  154. $pagecount = $pagecount + 1;
  155. }
  156. if ($pagecount > 20) {
  157. $pagecount = 20;
  158. }
  159. $highlightid = !intval(trim($_GET['p'])) == 0 ? intval(trim($_GET['p'])) : 1;
  160. for ($i = 1; $i <= $pagecount; $i++) {
  161. if ($highlightid == $i) {
  162. echo "<li class=\"active\"><a href=\"#\">{$i}<span class=\"sr-only\">(current)</span></a></li>";
  163. } else {
  164. echo "<li><a href=\"index.php?q={$Keywords}&p={$i}\">{$i}</a></li>";
  165. }
  166. }
  167. }
  168. ?>
  169. </ul>
  170. </nav>
  171. </center>
  172. </div>
  173. <div id="footer">
  174. <p class="text-center">
  175. The Web of Answers &copy;2010-2015 | Powered by b0rg
  176. </p>
  177. </div>
  178. </div>
  179. </body>
  180. </html>
转载地址:https://0x0d.im/archives/using-coreseek-with-PHP-with-multiple-indexes.html