thinkphp利用中文分词和全文索引技术实现搜索

前瞻

以前搜索功能一直使用的是like模糊查询,这种虽然操作简便,但是效果不好,需要搜索整个库,不如使用全文索引便捷,本文档是利用中文分词加上mysql的全文索引实现搜索功能。使用的框架是thinkphp3.2

思路

  1. 当在插入和修改文章的成功时,将文章用于客户搜索的字段利用中文分词插件进行分割,分割成用空格间隔词语的格式插入到索引表(我这里只是将title分割了),删除后也要把对应的索引表数据删除
  2. 当用户输入查询内容时再用中文分词分割成词语,利用mysql的全文索引去查询出数据
  3. 使用字符串替换函数,将存在的词语高亮显示

实现

  • 数据库表


  • 插入文章时代码(修改和删除不写了,一个道理)
public function add(){
...
...
//文章插入成功是走,$data为插入文章的id,这块需要注意,对应search表的art_id字段
                if($data)
                {
                    $title = $_POST['atitle'];//文章标题
                    $cont=isWhat($title);//自己封装的函数,判断是纯英文还是包含中文,下面有展示
                    //vendor("Fenci.segment");//引入词典
                    require_once "/Fenci/segment.php";//文件放在根目录
                    $se= new \Fenci\Segment();//实例化词典
                    if($cont==1){
                        $res['title']=$title;
                    }elseif($cont==2){
                        //调用方法
                        $res['title'] = $se->get_keyword($title);
                    }else{
                        //调用方法
                        $res['title'] = $se->get_keyword($title);
                        $words=split_en_str($title,false);
                        $res['title'].=" ".implode(' ',$words);
                    }
                    $res['tables'] = 'article';
                    $res['art_id'] = $data;
                    $search = M('Search');
                    $search->create( $res );
                    if( $search->add() ){
                        $this->success('添加成功!',U('Admin/Article/index'));
                    }else{
                        $this->error('文章插入成功,索引表插入失败!');
                    }
                }
...
...
}
  • 当用户输入搜索关键词提交时的demo
public function searchnews ()
    {
        $title = $_POST['title'];
        $cont=isWhat($title);//自己封装的函数,判断是纯英文还是包含中文,下面有展示  
        require_once "/Fenci/segment.php";//文件放在根目录
        $se= new \Fenci\Segment();//实例化词典
        if($cont==1){
            $seach_cont=$title;
        }elseif($cont==2){
            //调用方法
            $seach_cont = $se->get_keyword($title);
        }else{
            //调用方法
            $seach_cont = $se->get_keyword($title);
            $words=split_en_str($title,false);
            $seach_cont.=" ".implode(' ',$words);
        }
        $search = M('Search');
        //利用全文索引的语句查询
        $sql="select art_id,tables from search where MATCH(title) AGAINST('".$seach_cont."' IN BOOLEAN MODE)";
        $res = $search->query($sql);
        //获取所有匹配的文章,并且高亮显示
        $data = array();//查询的数据
        $seach_arr = explode(' ',$seach_cont);//将字符串转换成数组
        foreach( $res as $v ){
            //这里我是查询所有符合的内容,没有锁定单个表,所有表的主键最好是id,否则需要在索引表再加个字段判断
            $vs = M( $v['tables'] )->find($v['art_id']);
            //高亮显示查询内容
            foreach($seach_arr as $v1){
                //将数据中的关键词高亮显示
                $vs['atitle']=str_replace($v1,"<font color='red'><b>{$v1}</b></font>",$vs['atitle']);
            }
            $data[] = $vs;
        }
        $this->field=$data;
        $this->display($this->tpl.'news_lists1.html');//这块自己改
    }
  • 最后效果



  • 使用到的函数(放在/app/Common/function.php里)

/**
 *
 *判断字符串时全英文,全中文,或者都有
 *@param string $str1 需要检查的字符串
 *@return int 英文->1 中文->2 混合->3
 */
function isWhat($str1){
    $strA= trim($str1);
    $lenA= strlen($strA); //检测字符串实际长度
    $lenB= mb_strlen($strA, "utf-8"); //文件的编码方式要是UTF8
    if($lenA=== $lenB) {
        return"1";//全英文
    }else {
        if($lenA% $lenB== 0) {
            return"2";//全中文
        }else {
            return"3";//中英混合
        }
    }
}
/**
 *
 *匹配英文单词
 *@param string $str 需要匹配的字符
 *@param bool $distinct 是否去除重复值
 *@return array 返回所有单词的索引数组
 */
function split_en_str($str,$distinct=true) {
    preg_match_all('/([a-zA-Z]+)/',$str,$match);
    if ($distinct == true) {
        $match[1] = array_unique($match[1]);
    }
    sort($match[1]);
    return $match[1];
}
  • 小注
    • 分词用到的插件在我网盘,网盘地址:链接:http://pan.baidu.com/s/1gf7LZG3 密码:amqh;拿到之后直接解压到项目根目录,不放在根目录那就自己放,但是需要修改两处第一处是引入分词的路径(所有的引入路径都要改),见图1,第二是Segment.php类里面的引入词库的路径见图2;
      图1

      图2
    • 这个分词的词典词语还是比较少,没有找到更多的词库,谁有更好的词库也可以告诉一下,谢谢!我的代码需要优化的,或者有问题的留言!

推荐阅读更多精彩内容