mysql5.6 InnoDB 全文索引 FULLTEXT 中文解決方案 base64


mysql5.6 innlDB 在CHAR、VARCHAR、TEXT類型的列上可以定義全文索引,但因為無法中文分詞所以對中文的支持很差,但從MySQL5.7開始,MySQL內置了ngram全文檢索插件,用來支持中文分詞,並且對MyISAM和InnoDB引擎有效。

在沒法升級5.7的情況下,5.6有變通的辦法,就是將整句的中文拆分成單個漢字,並按urlencode、區位碼、base64、拼音等進行編碼使之以"字母+數字"的方式存儲於數據庫中。轉換完達到如下的效果:

存儲的是將漢字編碼后的結果,用空格連起來,這樣就可以使用5.6的全文索引來進行搜索,注意的是要將搜索的內容也先同樣編碼再進行搜索。

下面給出一種基於base64的漢字變換方式

/**
* 關鍵詞整理函數(用作mysql的全文索引制作的搜索)
* 1.將字符串全角轉半角、去空格、大寫轉小寫、分成單個字符並base64編碼、最后用空格連接類,方便mysql索引,做搜索關鍵字
* 2.將數字轉全角做索引區分標識,全角數字為標識專用
**/
function keywords($str){

    // 全角轉半角
    $str = strtr($str,[
        '1' => '1','2' => '2','3' => '3','4' => '4','5' => '5','6' => '6','7' => '7','8' => '8','9' => '9','0' => '0',
        'A' => 'A','B' => 'B','C' => 'C','D' => 'D','E' => 'E','F' => 'F','G' => 'G','H' => 'H','I' => 'I','J' => 'J','K' => 'K','L' => 'L','M' => 'M','N' => 'N','O' => 'O','P' => 'P','Q' => 'Q','R' => 'R','S' => 'S','T' => 'T','U' => 'U','V' => 'V','W' => 'W','X' => 'X','Y' => 'Y','Z' => 'Z',
        'a' => 'a','b' => 'b','c' => 'c','d' => 'd','e' => 'e','f' => 'f','g' => 'g','h' => 'h','i' => 'i','j' => 'j','k' => 'k','l' => 'l','m' => 'm','n' => 'n','o' => 'o','p' => 'p','q' => 'q','r' => 'r','s' => 's','t' => 't','u' => 'u','v' => 'v','w' => 'w','x' => 'x','y' => 'y','z' => 'z',
        '~' => '~','`' => '`','!' => '!','@' => '@','#' => '#','$' => '$','%' => '%','^' => '^','&' => '&','*' => '*','(' => '(',')' => ')','_' => '_','-' => '-','+' => '+','=' => '=',
        '{' => '{','}' => '}','[' => '[',']' => ']','|' => '|','\' => '\\',':' => ':',';' => ';','"' => '"',''' => '\'',
        '<' => '<',',' => ',','>' => '>','.' => '.','?' => '?','/' => '/',' ' => ' '
    ]);
    // 去空格
    $str = str_replace(' ','',$str);
    // 大寫轉小寫
    $str = strtolower($str);
    // 數字統一格式為阿拉伯數字
    $str = strtr($str,['零' => 0,'一' => 1,'二' => 2,'三' => 3,'四' => 4,'五' => 5,'六' => 6,'七' => 7,'八' => 8,'九' => 9]);
    // 分成單個字符並base64編碼
    $str_len = strlen($str);

    // 獲取關鍵字集合
    $arr = [];
    $str_len = mb_strlen($str);
    for($i = 0;$i < $str_len;++ $i){
        $keyword = strtr(base64_encode(mb_substr($str,$i,1)),'+/=','abc');
        if(!in_array($keyword,$arr)){    // 去除重復的關鍵字
            $arr[] = $keyword;
        }
    }
    return $arr;
}

例如將字符串 '小明小紅是朋友' 輸入改函數,返回的結果是數組如下

array(6) {
 [0]=>
 string(4) "5bCP"
 [1]=>
 string(4) "5piO"
 [2]=>
 string(4) "57qi"
 [3]=>
 string(4) "5piv"
 [4]=>
 string(4) "5pyL"
 [5]=>
 string(4) "5YaL"
}

分別對應相應的漢字,注意小明和小紅都有小這個字,所以去掉重復的字,只有六個編碼。

然后用空格將數組連起來

$keywords = implode(' ',keywords($keyword));

將$keywords 存入數據庫。

 

進階:

如果匹配的關鍵詞包含一些常用的字,會出現大量的結果。

例如搜索書名 霸道總裁,可能會出現,裁縫,總經理,這樣的結果

全文搜索是按照相關度從高到底返回的結果,可以只去去前面一些相關度較高的結果。

或者先查詢出相關度最高是多少(相關度是一個數值),然后除以二,限定結果的相關度都大於這個最大相關度的一半。

參考

// 通過最大相關度/2過濾一部分無關結果

//查詢出最大相關度是多少 $score = $this->sql('xs.nh')->query('SELECT MATCH(keywords_base) AGAINST (?) AS score FROM nh ORDER BY score DESC LIMIT 1',[$keywords]); //構造查詢語句 $this->where['MATCH(keywords_base) AGAINST'] = [$keywords,'> '.$score[0]['score'] / 2];

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM