MySQL中文全文检索解决方案

PHP+MySQL构架的网站中,大数据量的全文检索一般都会用到MySQLFULLTEXT全文索引,通过SELECT...MATCH...AGAINST语句来进行查找。

迄今为止,MySQL对中文全文索引无法正确支持,MySQL是不会识别中文词语的。参照MySQL识别英文单词机制,要建立中文全文索引,暂时的解决方案只有手动将中文分词(以空格的形式将中文词语分开),来将中文转换成MySQL认识的语言。

 

如今网上对于中文分词的解决方案有很多,有基于MySQL插件的,有谈论算法思想的。基于插件(如海量科技的MySQL--LinuxX86-Chinese+hightman开发的mysql--ft-hightman)的方式主要通过对MySQL数据库安装一个别人提供好的插件,在建FULLTEXT索引的字段时后面加上WITH PARSER ×××(大多都是这样)的形式。而基于算法思想的则大部分工作都要自己完成,但他们的大体思想都差不多:

1. 对插入的要建全文索引的中文数据进行分词;

2. 将原始数据和分词后的数据都存入数据库中,并以某种方式建立联系;

3. 在存储分词数据的字段上建立FULLTEXT索引;

4. 查询时以SELECT...MATCH...AGAINST的方式在分词字段上搜索,将搜到的行通过前面建立的联系找到原始数据行并返回。

 

而我们在讨论解决方案时,考虑到使用开源插件的话可控性比价差,而且插件会对MySQL做一些改变,我们决定将分词存储的工作自己写代码完成,这样虽然工作量加大,但以后的维护成本却降低了很多。下面我们来看下大体实现。

 

 

一、首先,先建立数据库

要注意的是只有MyISAM表类型才能支持FULLTEXTMyISAMInnoDB各有优劣,我们决定将原始数据与索引分表存储,原始数据存入InnoDB表,同时建立MyISAM表存入作为检索的字段和用于关联的字段id。这里我建立了两张表questionsquestions_idx

其中,titledetail是要建立全文索引的字段,而id则是建立两张表的联系。值得注意的是,MyISAM是不支持事务和外键的,因此对于两张表数据的同步还要靠额外代码逻辑来实现。

还有就是索引字段有可能需要比原始数据更大的空间,这里我分配了2倍(这个是我随意想的,有可能需要更多)。

 

二、接着,讨论中文分词

网上流传的分词方法有很多,主要有基于算法的(比如二元分词算法,字节交叉切分算法)和基于词库的。基于算法是不必要维护词库的,而词库法则必须维护词库,有可能跟不上词汇的发展。实际上现在很多著名的搜索引擎都使用了多种分词的办法,比如正向最大匹配+逆向最大匹配,基于统计学的新词识别,自动维护词库等技术

我们采用的是基于词库的,并且使用了hightmanscwsphp扩展模块方式。参考http://www.ftphp.com/scws/ 。这个开源分词系统这里不多说,总之利用的是词库来分词,而且最新版的是支持自定义词库的,这对于我们的内部网站来说,词库的维护问题变得简单了,因为新增词汇不会像外部网站那么大,也不需要维护太多。

 

下面定义了类CWS,方法get_idx将输入中文数据,输出分词并编码后的数据:

 

class CWS {
  //对输入字符串使用scws进行分词,去重复项,进行urlencode编码
  public static function get_idx($input) {
    //--------分词-----------
    $so = scws_new();
    $so->set_charset('utf8');
    $so->set_ignore(true);
    $output = '';
    $so->send_text($input);
    while ($tmp = $so->get_result()) {
      foreach ($tmp as $item) {
        $output .= $item['word'] . ' ';
      }
    }
    $so->close();
    //--------编码-----------
    $data = array_filter(explode(" ",$output)); //删除数组空项
    $data = array_flip(array_flip($data));      //删除重复项
    //对分词结果进行urlcode编码
    foreach ($data as $ss) {
      if (strlen($ss) > 1) {
        $data_code .= str_replace('%','',urlencode($ss)) . ' ';
      }
    }
    return $data_code;
  }
}

 

对于scws的那段代码请参照scws使用手册。

而对于为什么要进行编码,网上大都解释是:MySQL系统自变量规定了全文检索被编入索引单词的最小长度和最大长度(ft_min_word_lenft_max_word_len),默认的最小值为4个字符,默认的最大值取决于使用的 MySQL 版本。 参考http://dev.mysql.com/doc/refman/4.1/en/server-system-variables.html#sysvar_ft_min_word_len 

为了不改变这个默认值同时也是兼考虑这个值对于英文的意义,则需要通过编码将中文词变长。

 

而对于编码,网上流传的方式也有很多,比如base64编码、urlencode编码等,甚至还有汉字转拼音。这里我尝试了urlencode编码,需要注意的是urlencode会产生很多’%’,这在MySQL中是通配符,要去掉。

 

三、插入数据

插入数据的时候我们就要调用以上的函数了:

public function add($title, $detail, $askerid) {
  $date = NOW;
  $sql = 'INSERT INTO questions ' .
      '(title, detail, askerid, date) ' .
      'values ' .
      "('$title', '$detail', $askerid, '$date')";
  $result = $this->db->query($sql);
  $id = $this->db->lastId();
  //scws分词存储
  $title_idx = CWS::get_idx($title);
  //使用strip_tags函数过滤掉富文本编辑器产生的标签
  $detail_idx = CWS::get_idx(strip_tags($detail));
  $sql = 'INSERT INTO questions_idx ' .
      '(id, title, detail) ' .
      'values ' .
      "($id, '$title_idx', '$detail_idx')";
  $this->db->query($sql);
  return $result;
}

 

四、搜索数据

搜索的时候,从输入框获取问题,当然如果你输入的是关键词,那就直接搜就是了,但我们是要用户输入的一个完整的问题,因此也要分词,否则MySQL还是检索不到:

public function search($word, $limit) {
    $word = CWS::get_idx($word);
    $sql = "SELECT A.title, A.detail, askerid, date " .
           "FROM questions as A, questions_idx as B " .
           "WHERE A.id = B.id " .
           "AND MATCH (B.title, B.detail) AGAINST ('$word')";
    $result = $this->db->getAll($sql, $limit);
    return $result;
}     

转载于:https://www.cnblogs.com/tanxin/archive/2012/11/05/2754645.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值