全文搜索知识库

undefined

最近在快讯的项目是做一个全文搜索的知识库。本来一开始听到这个要求的时候,头皮有点发麻,因为他要求三层类别层级,然后支持全文搜索。全是我未知的领域。

但是事实证明,压力能给你带来很多意想不到的东西。刚开始接到要求,我第一反应就是谈判,减低要求。奈何和我一组的老哥一直点头说没问题。于是,便开始了这个需求的学习。

全文搜索

全文搜索,听起来好像很高大上,很难搞定。但是其实原理只有两个:关联的模糊搜索(高端点可以利用索引)和中文分词。

关联的模糊搜索以及索引

索引这个,奈何我自己的数据库知识不是特别扎实,所以看了挺多资料之后也没特别搞懂。今天在具体实现的时候,发现sequelize在官方文档的实例里用到这样一招,让我茅塞顿开。

1
2
3
4
5
6
7
8
9
10
11
12
13
const result = yield Knowledge.findAll({
where: {
$or: {
title: {
$like: `%${key[0]}%`,
}
},{
content: {
$like: `%${key[0]}%`,
}
},
},
});

大致的意思就是,将满足title列模糊查询或者content列模糊查询得到的结果都提取出来。这不就是全文搜索么!!!只是,索引的使用我猜可能会更高效?或者更便捷?但是我相信原理应该就是如此。

索引

虽然没实践成功,但是可以mark一下。利用的是mysql的FULLTEXT索引

1
2
3
4
5
6
7
8
9
mysql> CREATE TABLE articles (
-> id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
-> title VARCHAR(200),
-> body TEXT,
-> FULLTEXT (title,body)
-> );
mysql> SELECT * FROM articles
-> WHERE MATCH (title,body) AGAINST ('database');

MYSQL全文搜索通过 MATCH() 函数完成。

在全文索引上进行搜索是不区分大小写的。  下面再看如何实现中文全文检索。  fulltext字段是以词语为单位,词语之间需要用空格隔开,而汉语的句子中各个词语之间并不会用空格隔开,因此我们需要对中文进行分词,这也就是为什么上面需要强词用到中文分词扩展模块。  但是尽管对中文进行分词,MYSQL还是不能通过MATCH来实现中文的全文检索,这需要通过一定的方法来进行转换,一个比较简单实用的方法是采用下面这个函数(当然还有更好的),它将中文进行了urlencode转换。

将转换过后的内容保存至事先定义好的fulltext字段。同样,在查询的时候也需要将查询的关键词进行同样方法的转换。

如何使用PHP实现全文检索功能?

中文分词

一开始查的文档,实在有点凶残。一直在教如何建立一个中文分词的算法,看到我头皮发麻。但是这是node啊!不找模块算什么。于是便遇到了它node-segment直接解决了我最麻烦的问题,自动分词。大概的代码贴上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 载入模块
var Segment = require('segment');
// 创建实例
var segment = new Segment();
// 使用默认的识别模块及字典,载入字典文件需要1秒,仅初始化时执行一次即可
segment.useDefault();
// 开始分词
let result = segment.doSegment(text, {
simple: true, //不返回词性
stripPunctuation: true, //去除标点符号
});
// 分词后注意进行数组的去重操作
// 可以减轻数据库的重复操作负担。
uniqueArray(result);

之后剩下的问题就是如何将所有结果组合起来而已了~~~

后记

在写完后,又断断续续地发现仅仅如此处理分词,可能没办法达到全文搜索优化的效果。例如,假设用户要搜索的就是”深圳大学体质测试通知”。而在数据库中正好也躺着这么一个标题命名的文章,本来是100%匹配的结果的。然而,由于我引入的分词模块的影响,这段文字被分成了"深圳""大学""体质""测试""通知"这几段文字,由此可见,它将会匹配到一些莫名其妙的东西然后才匹配到我们想要的结果。这不得不说是十分影响体验的。暂时想到的解决办法就是先将字段分词,分词完毕后再将原字段插入到数组的第一个位置上去unshift()。提高文本的命中率。

还有一个点就是,因地制宜地制定适合的字典。例如’深圳大学’,明显这个字段在深大是不需要分割,或者说不能分割的。然而,因为使用的是通用词典,所以在这些地方会显得特别的鸡肋。想到的解决办法就是记录用户的使用习惯,最后因地制宜地编写更适合的字典文件。例如用户输入’体侧’,我们可以自动补全为’体质测试’。

分词是全文搜索最重要的环节,所以远远不是几行代码就可以处理完毕的。可以更加深入地探究如何提高搜索结果的匹配度。