前瞻
以前搜索功能一直使用的是like模糊查询,这种虽然操作简便,但是效果不好,需要搜索整个库,不如使用全文索引便捷,本文档是利用中文分词加上mysql的全文索引实现搜索功能。使用的框架是thinkphp3.2
思路
- 当在插入和修改文章的成功时,将文章用于客户搜索的字段利用中文分词插件进行分割,分割成用空格间隔词语的格式插入到索引表(我这里只是将title分割了),删除后也要把对应的索引表数据删除
- 当用户输入查询内容时再用中文分词分割成词语,利用mysql的全文索引去查询出数据
- 使用字符串替换函数,将存在的词语高亮显示
实现
-
数据库表
- 插入文章时代码(修改和删除不写了,一个道理)
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;
- 这个分词的词典词语还是比较少,没有找到更多的词库,谁有更好的词库也可以告诉一下,谢谢!我的代码需要优化的,或者有问题的留言!
- 分词用到的插件在我网盘,网盘地址:链接:http://pan.baidu.com/s/1gf7LZG3 密码:amqh;拿到之后直接解压到项目根目录,不放在根目录那就自己放,但是需要修改两处第一处是引入分词的路径(所有的引入路径都要改),见图1,第二是Segment.php类里面的引入词库的路径见图2;