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

其他链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值