程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 實現一個簡單的mysql帶權重的中文全文搜索

實現一個簡單的mysql帶權重的中文全文搜索

編輯:關於PHP編程

實現一個簡單的mysql帶權重的中文全文搜索
自己在寫一個web,希望對數據庫做全文檢索。但是google了解到,由於中文分詞的緣故,mysql只支持英文的全文搜索,想支持中文的,需要各種插件or實現一些比較復雜的機制,而買的虛擬主機並不支持這些復雜的東西。仔細想了下,因為自己需求的功能也比較簡單,主要是2個字段的搜索,且數據量不大,即便增加幾個字段,需要多運行幾個select也不會對速度有太大影響,所以通過一些work around實現了需求。

Step 1:用locate進行簡單的搜索
Locate可以判斷子串是否在子亂
有兩個column,一個name,一個description.
所以可以用LOCATE>0去判斷是否關鍵字在其中出現了。
其實就是
SELECT * FROM table WHERE LOCATE(key, 'name')>0 OR LOCATE(key, 'description);
這樣,我們就簡單實現了對某個key在兩個域的搜索

Step 2:搜索多個關鍵字
通常,搜索都是有多個關鍵字,所以我們需要對每個關鍵字,執行下Step1的查詢。(當然,也可以合成一個,這裡偷懶每次只查詢1個關鍵字)
然後,我們再將每次查詢出的數組都合並,這樣就得到了一個最終的集合。

php代碼如下:

  1. function selectlocate($tarcols,$skey){
  2. $where ="";
  3. $connector = " ";
  4. global $count;
  5. foreach($tarcols as $tarcol ){
  6. $where .= $connector;
  7. $where .= "LOCATE('$skey', $tarcol) != 0 ";
  8. if($connector == " "){
  9. $connector = " OR ";
  10. }
  11. }

  12. $sql = "SELECT * FROM pets_table WHERE $where";
  13. $result = mysql_query($sql);
  14. $ret = Array();
  15. while($item = mysql_fetch_array($result, MYSQL_ASSOC)){
  16. $count ++;
  17. $ret[] = $item;
  18. }
  19. return $ret;
  20. }
Step 3:匹配的權重
上面Step2的結果,其實是無序的。通常,如果我們搜索一個字段:
1.如果這個字段和關鍵字完全相同,那麼一般來講,可能這個結果應該是相關度最高的
2.如果他只是其其中出現了一次,相關度就最低。
3.如果他出現的次數比在其他row中出現的次數高,那麼他的相關度就比2中的結果高
所以,搜索的時候依據這個順序考慮權重,
a.如果完全相等,權重為1000
b.如果出現1次,權重為10,出現n次
c.權重為n*10
每次搜索出來的結果附加上權重----》然後合並相同項----》並把權重累加
最後按權重排序,即可得到一個有排序的搜索結果。

以下是兩種1關鍵字對應1個字段(上面的代碼是1關鍵字多個字段)查詢的代碼(不包含合並兩個數組的代碼,相關的代碼在Step4中),只需遍歷每個關鍵字和字段,就能完成搜索

  1. $count = 0;
  2. function selectequal($col,$skey){
  3. $connector = " ";
  4. global $count;
  5. $sql = "SELECT * FROM pets_table WHERE LOWER($col)=LOWER('$skey')";
  6. $result = mysql_query($sql);
  7. $ret = Array();
  8. while($item = mysql_fetch_array($result, MYSQL_ASSOC)){
  9. $count ++;
  10. $item["weight"] = 1000;
  11. $ret[] = $item;
  12. }
  13. return $ret;
  14. }
  15. function selectlocate($col,$skey){
  16. global $count;
  17. $sql = "SELECT *,(LENGTH(description) - LENGTH(REPLACE(description, '$skey', '')))/LENGTH('$skey') *10 as weight FROM pets_table WHERE LOCATE(LOWER('$skey'),LOWER($col))>0";
  18. $result = mysql_query($sql);
  19. $ret = Array();
  20. while($item = mysql_fetch_array($result, MYSQL_ASSOC)){
  21. $count ++;
  22. $ret[] = $item;
  23. }
  24. return $ret;
  25. }



Step 4: 字段的權重
在我的需求中,顯然name這個字段比description更重要,所以在匹配時,對name字段的結果應該有所傾斜,所以,又可以增加一個對字段的權重系數。

1.如果是在name域的匹配,設系數為10;
2.如果是在description匹配,設系數為1;

將Step 3每次計算得出的權重,再乘上這個系數,就可以得到一個新的,更有效的權重值。
最後按權重排序,即可得到一個最有相關度排序的搜索結果

其他的細節:
如果一個關鍵字已經滿足了equal條件,那麼再使用locate條件的時候會依然返回一個結果,所以在使用locate條件的時候,過濾掉equal的情況

  1. <?php
  2. $count = 0;
  3. function selectequal($col,$val,$skey){
  4. $connector = " ";
  5. global $count;
  6. $sql = "SELECT * FROM pets_table WHERE LOWER($col)=LOWER('$skey')";
  7. $result = mysql_query($sql);
  8. $ret = Array();
  9. while($item = mysql_fetch_array($result, MYSQL_ASSOC)){
  10. $count ++;
  11. $item["weight"] = 1000*$val;
  12. $ret[] = $item;
  13. }
  14. return $ret;
  15. }
  16. function selectlocate($col,$val,$skey){
  17. global $count;
  18. $sql = "SELECT *,(LENGTH(description) - LENGTH(REPLACE(description, '$skey', '')))/LENGTH('$skey') *10*$val as weight FROM pets_table WHERE LOCATE(LOWER('$skey'),LOWER($col))>0 AND LOWER($col)!=LOWER('$skey')";
  19. $result = mysql_query($sql);
  20. $ret = Array();
  21. while($item = mysql_fetch_array($result, MYSQL_ASSOC)){
  22. $count ++;
  23. $ret[] = $item;
  24. }
  25. return $ret;
  26. }
  27. function cleanarr($arr){
  28. global $count;
  29. $tmp = Array();
  30. $tmpall = Array();
  31. foreach($arr as $item){
  32. if(array_key_exists($item['uid'], $tmp)){
  33. $tmp[$item['uid']]+=$item["weight"];
  34. }
  35. else{
  36. $tmp[$item['uid']] = $item["weight"];
  37. $tmpall[$item['uid']] = $item;
  38. }
  39. }

  40. //sort by weight in descending order
  41. arsort($tmp);

  42. $ret = Array();

  43. //rebuildthe return arary
  44. $count = 0;
  45. foreach($tmp as $k=>$v){
  46. $count++;
  47. $tmpall[$k]['weight']=$v;
  48. $ret[]=$tmpall[$k];
  49. }
  50. return $ret;
  51. }

  52. require_once("consvr.php");


  53. $colshash = array("name"=>10,"description"=>1);
  54. $ret = Array();
  55. $keywords=explode(" ", $keywords);
  56. $cols = array_keys($colshash);
  57. foreach($keywords as $keyword){
  58. foreach($colshash as $col=>$val){
  59. $ret = array_merge($ret,selectequal($col,$val, $keyword));
  60. $ret = array_merge($ret,selectlocate($col,$val, $keyword));
  61. }

  62. }
  63. $ret = cleanarr($ret);
  64. $ret = array('msg' => "Success", 'count'=>$count,'children' => $ret, 'query'=>"COMPLEX:NOT READABLE");
  65. echo json_encode($ret);
  66. mysql_close();

  67. ?>











  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved