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

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

作者: 呦丶耍脾气 | 来源:发表于2017-01-06 10:29 被阅读880次

    前瞻

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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/rxptbttx.html