用MySQL实现中文全文搜索|站内搜索

首先我们首先要了解几个概念

  1. MySQL自带英文的全文搜索功能,需要知道fulltext索引和myisam引擎。
  2. 英文全文搜索是靠单词之间的空格实现的。
  3. 英文全文搜索的三个模式以及权重分析。

什么是索引

这边我先做一个说一下索引有什么用?众所周知,你要执行查询,如果没有索引的话,他的搜索方式是检表,即从前到后依次对表进行遍历,最简单的例子

SELECT * FROM users WHERE age=18

这条语句会检查所有表中的数据,直到表没数据了为止,可想而知,如果是数据量较少,速度几乎是不能察觉出来的,但数据量一旦十万百万等,会有明显的延迟。而如果我们给age建立了索引,那么这这个索引的数据将会是这样的。

index age
18 18
19 19
20 20
18 18

此处index为索引标识符,里面存储的是标识符和数据地址,当我们用上面的SQL语句搜索时,步骤是,搜索索引标识符,通过索引标识符,得到所有age=18的数据地址,然后依次取出来。
这样,便省去了检表的操作。
这样,是不是省去了不少时间。
Fulltext的效果就是这样的。

英文全文搜索的三个模式

  1. 自然搜索模式
    MySQL会把搜索字符串解析成一系列的单词,然后搜索出保函这些单词的所在行。
SELECT * FROM index_article WHERE  MATCH(content) AGAINST('中国 北京')

这条语句意思为:搜索索引表中字段为content中包含关键词'中国'、'北京'的文章。

  1. 布尔搜索模式
    在搜索的字符串中可以存在修饰符
SELECT * FROM index_article WHERE MATCH(content) AGAINST('+北京 -朝阳' IN BOOLEAN MODE)

这条语句的意思为搜索索引表中包含'北京'不包含'朝阳'的文章。

  1. 扩展搜索模式
    两次搜索,第一次搜索是自然搜索,第二次搜索会吧第一次搜索中内容中所有次再次进行匹配,从而达到扩大饭的效果。
SELECT * FROM index_article WHERE  MATCH(content) AGAINST('中国 北京' WITH QUERY EXPANSION)

这句的效果是,当第一次搜索时候,搜索到了一句北京朝阳男性市民,第二次扩展搜索时,会将男性市民加入搜索行列,即所有出现这些关键词的时候,都会被搜索到。

权重,怎么计算?

一条语句搞定,但是中文特殊一点,在源码中我有使用到。

SELECT id, MATCH(content) AGAINST('中国 北京') AS weight FROM index_article

之前写的条件变成了权重显示的字段。这条语句的效果为,所有匹配到这两个关键词的内容,会根据次数,密度,比例等指标计算权重。示例如下:

id weight
1 1.2323232123
2 1.3423423423
3 2.3421231231
4 0.2213123123

好,基本知识普及完成,接下来讲下中文全文搜索的思路以及源码

思路解析

项目:做一个博客,可以进行文章上传、展示、站内全文关键词搜索、搜索关键字提示、搜索权重分析。
项目思路:对文章和标题进行分词操作,分词写入数据库的索引表,原数据写入原表;关键词高亮显示,可以使用字符串替换;权重计算关键词出现的次数。

数据表截图

索引表1.png
索引表2.png
原表1.png
原表2.png

多插入几条数据才能实现

展示页面+搜索

<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="http://www.zh30.com/wp-content/themes/weisayheibai/style.css">
    <style>
        h1 {font-size:32px;}
        html #hm_t_undefined .hm-t-go-top {
            position:fixed;right:2px;bottom:2px;z-index:99998;cursor:pointer;width:40px;height:37px!important;text-align:center;white-space:normal;font-size:14px;line-height:17px;padding-top:3px;color:#fff;background:#404040;
        }
        .entry_post{
            height:100px;overflow: hidden;white-space:wrap;text-overflow:ellipsis;
        }
    </style>
    <script src="../js/jquery.min.js"></script>

</head>
<body>
<div id="page">
    <div id="header">
        <div class="open-follow">
            <table class="rss"><tbody><td>
                <div class="search"> 
                    <form method="GET" id="searchform" action="#"> 
                        <fieldset id="search"><span>
                            <input type="search" name="search" style="height:23px;float:left;">
                            <input type="submit" value="GO" style="background:#555;font-size:18px;color:white;cursor:pointer;">
                        </span></fieldset> 
                    </form>
                </div>
                </td></tr></tbody>
            </table>
        </div>
        <div class="clear"></div> 

        <div id="blogname">
            <a href="http://www.zh30.com/"><h1>个人博客</h1></a>
            <div id="blogtitle">个人网站</div>
        </div>

        <div class="pagemenu">
            <ul id="menu-pages-menu" class="navi">
            <li class="page_item page-item-3"><a href="show.php" title="首页">首页</a></li>
            <li class="page_item page-item-285"><a href="">地图</a></li>
            <li class="page_item page-item-2"><a href="" rel="nofollow">关于</a></li>
            <li class="page_item"><a href="upload.html">发布文章</a></li>
            </ul>
        </div>
    </div>

    <div id="content">
        <div class="content_l">
        <div class="post-2156 post type-post status-publish format-standard hentry category-linux-2 tag-centos tag-fedora tag-git" id="post-2156">
        <div class="left">

        <!-- 文章模块开始-->
        <div class="article">
            <?php
            // // 包涵类文件
            require("../model/model.php");
            require("../model/segment.php");

            $model = new DB();
            $segment = new segment();
            if(!empty($_GET['search'])){
                $keywords = htmlentities($_GET['search']);
                $key = $segment->get_keyword($keywords);

                unset($keywords);// 释放变量

                // 准备sql语句
                $sql = "SELECT fid FROM index_article WHERE MATCH(title,content) AGAINST('".$key."')";
                $rtn = $model->get_all($sql);
                
                unset($sql);// 释放变量

                $keys = explode(' ', $key);// 将关键字分割成数组
                
                $res = [];// 建立空数组存储数据

                for($i=0;$i<count($rtn);$i++){
                    $fid = $rtn[$i]['fid'];

                    $sql = "SELECT * FROM article WHERE id = '".$fid."'";
                    $res[$i] = $model->get_one($sql);

                    unset($sql);// 销毁变量

                    //这边做一个相似度评级
                    $res[$i]['title_num'] = 0;
                    $res[$i]['content_num'] = 0;
                    
                    for($j=0;$j<count($keys);$j++){
                        $res[$i]['title_num'] += substr_count($res[$i]['title'],$keys[$j]);
                        $res[$i]['content_num'] += substr_count($res[$i]['content'],$keys[$j]);
                    }
                    // 计算权重
                    $res[$i]['weight'] = $res[$i]['title_num'] * 90 + $res[$i]['content_num'] * 10;
                }

                // 根据权重新排序数组
                foreach($res as $k=>$v){
                    $weight[$k] = $v['weight']; 
                }
                @array_multisort($weight,SORT_NUMERIC,SORT_DESC,$res);

                // 销毁变量
                unset($rtn);
                unset($weight);

            }else{
                $sql = "SELECT * FROM article";
                $res = $model->get_all($sql);
                unset($sql);
            }
            
            $str = '';
            for($i=0;$i<count($res);$i++){
                $str .= '<div class="article_t">';
                $str .= '<div class="article_b">';
                $str .= '<h2><a href="details.php?id='.$res[$i]['id'].'" title="权重:'.$res[$i]['weight'].'"rel="bookmark">'.$res[$i]['title'].'</a></h2>';
                $str .= '<p class="entry_post">'.$res[$i]['content'].'</p>';
                $str .= '<p class="more_b"><span class="more"><a href="details.php?id='.$res[$i]['id'].'" rel="bookmark nofollow">阅读全文...</a></span></p>';
                $str .= '<div class="clear"></div>';
                $str .= '</div></div>';
            }
            // // $s = '';
            // 替换多个关键字
            for($i=0;$i<count($keys);$i++){
                $str = str_replace($keys[$i],"<font color='red'>".$keys[$i]."</font>",$str);
            }
            echo $str;

            // 释放变量
            unset($keys);
            unset($str);
            
            ?>
        </div>
        <!--分页开始-->
        <div class="navigation">
            <div class="pagination">
                <span class="current">1</span>
                <a href="http://www.zh30.com/page/2" class="inactive">2</a>
                <a href="http://www.zh30.com/page/3" class="inactive">3</a>
                <a href="http://www.zh30.com/page/4" class="inactive">4</a>
                <a href="http://www.zh30.com/page/5" class="inactive">5</a>
                <a href="http://www.zh30.com/page/6" class="inactive">6</a>
                <a href="http://www.zh30.com/page/2" class="page_next">下一页</a>
                <a href="http://www.zh30.com/page/56">最后</a>
            </div>
        </div>
</div>
</body>
</html>

源码百度云盘地址

http://pan.baidu.com/s/1qXQDTUW

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容