如何实现全文检索功能之搜索之路

开发 开发工具
张大胖学校的图书馆网站上线了,张大胖去浏览了一番,发现竟然没有按照关键字搜索图书的功能,大为吃惊之余,又隐约觉得机会来了: 我刚学完Web开发,其中也有数据库相关的知识,也许我可以实现这个功能啊!

 引子

时间回到20年前。 

张大胖学校的图书馆网站上线了,张大胖去浏览了一番,发现竟然没有按照关键字搜索图书的功能,大为吃惊之余,又隐约觉得机会来了: 我刚学完Web开发,其中也有数据库相关的知识,也许我可以实现这个功能啊!

想到此处,张大胖赶紧查找联系方式,抱着试试看的态度给图书馆的管理员发了一封电子邮件,表达了能实现这个搜索功能的信心和决心。 

[[241027]]

没想到真的收到回信了,管理员王老师让他在周四的下午2点半到图书馆3楼307聊一聊。 

周四下午,张大胖沐浴更衣以后,如约而至。 经过一番寒暄和介绍,王老师正式进入主题:你打算怎么实现这个全文检索功能啊? 

张大胖说:“我可以用SQL的 Like来实现。”

王老师笑了:“数据量小的时候Like还凑合,数据量一大,效率就非常低了。”

张大胖有点蒙:“那怎么办?”

王老师说:“得用inverted index才行。”

张大胖知道什么是index, 但是从来没有听说过inverted index,这是什么鬼?

王老师看到他迷茫的脸色,就知道这小子虽然勇气可嘉,但是技术还是有所欠缺,鼓励到:“这样吧,你回去研究研究inverted index, 然后再来实现这个全文检索功能。”

Inverted Index

张大胖灰溜溜地回到宿舍,赶紧拨号上网去查这个inverted index。 

张大胖发现有很多人把他翻译成倒排索引,听起来虽然有点古怪,但实际上是一个非常简单的概念。 比如说有两篇文档:

文档1的内容:A computer is a device that can execute operations

文档2的内容:Early computers are big devices

把这两篇文章中的单词都抽取出来,并且记录下这些单词出现在哪个文档中,这就形成了一个简单粗糙的倒排索引。

通过这个“倒排索引”,只要给出一个单词,就可以迅速地定位到它在哪个文档中。 用来做全文搜素非常合适。

但是上面的倒排索引有点“粗糙”,还可以在“精化”一下。

1. 对于 a, is , to ,that ,can ,the,are 这些词对搜索来说没什么意义,用户几乎不会用他们搜索,可以过滤掉。  

2.  用户搜索的时候虽然输入了computer,但是也希望搜出computers出来,所以需要把复数单词,过去式单词等做还原。 

3. 用户虽然输入了device , 但是也希望搜索出Device出来,所以需要把大写都改成小写。

经过这番转换,文档1和文档2的关键字变成了这样:

文档1:[computer] [device] [execute] [operation]

文档2:[early] [computer] [big] [device]

相应地,倒排索引变成了这样:

当用户想搜索device 的时候,我们就可以告诉它,在文档1和文档2中都有。

为了更好的统计和高亮显示搜索结果,还可以记录关键词在文章中出现的次数和位置(例如:是第几个单词)

架构

明白了inverted index是怎么回事儿, 张大胖觉得只要把那些书籍的标题,介绍,作者等信息给提取出来,形成inverted index,不就可以做关键字检索了吗?

可是转眼一想,这个需求应该是个通用的需求,不仅是图书馆有,很多互联网应用,例如网上商城也需要, 我完全可以设计一个类库出来,让大家去使用啊。

张大胖迅速画出一个架构图出来:

看看,不管你是什么类型的文档,HTML, PDF, Word,甚至是数据库的数据,只要能从中抽取出文本,就可以当作我的数据源,对文本做了分析以后,就可以存入索引库。 

用户可以发出各种查询,不仅仅是单个关键字,还支持关键字的组合: keyword1 AND keyword2等,对索引库进行搜索以后,把结果返回给用户。 

“中间用虚线框起来的部分就是我应该实现的类库!” 张大胖对这个架构很满意。 

抽象

接下来就要考虑这个类库对外提供的API了,这是个很烦人的事情。 张大胖的脑海中不由地想起来好基友Bill的谆谆教导:“软件设计就是一个不断抽象的过程。”, 关键是要抽象啊! 

可是这个系统似乎有点复杂,张大胖绞尽脑汁想了两天也没有头绪,对于一个刚学会Web开发的同学,立刻就要设计类库,确实是有点勉为其难。 

没办法,张大胖只好致电Bill前来帮忙。 

Bill对张大胖的痛苦表示了亲切的慰问, 又对架构图表示了适度的赞赏。 他说: “既然有HTML,PDF, Word文档, 很自然,你可以做一个Document的抽象!”

“好像不行吧? 如果数据源是数据库,怎么变成Document? ”

“很简单, 比如一行数据就可以映射到一个Document。 当然,这个转化必须得有程序员来完成。程序员决定什么东西是Document。” Bill说。

“Document中有什么东西? ”

“嗯,Document可以有很多属性,每个属性都有名称和值, 我可以把属性叫做Field。比如一个HTML文档,可能有path, title,content等多个Field,其中path可以唯一地标志这个文档, title和content需要做进行分词,形成倒排索引,让用户搜索。” Bill回答。

(一个包含多个Field的Document)

“用户创建了Document和Field以后,就可以进行分析了原始的内容划分成一个个Term,” 张大胖开始开窍,“这样,我可以定义一个Analyzer的抽象类,让别的程序可以扩展它。”

“对,分析的结果可以加入索引库,这个操作可以让一个单独的类,比如IndexWriter类来完成。” Bill 说。

“可是IndexWriter如何知道索引应该存在什么地方? 内存? 文件系统?”

“那就再来一个抽象的概念:Directory,表示索引文件的存储。”

张大胖怕忘记,赶紧画类图:

 

“看起来还是挺漂亮的嘛!” 张大胖说。  

“好的设计一般都比较漂亮。” Bill 来了一句至理名言。

“对于对于用户搜索来说,肯定得有个叫做Query的东西,用来表达用户的搜索要求。当然,这个Query也应该允许扩展。 ” 张大胖尝试着抽象。

“但是这些类使用起来还是比较麻烦, 最好是支持用户输入字符串来表达搜索的意图:(computer or phone) and price”  Bill还是经验老道,“必须得有个解析器来完成从字符串到Query的转化,然后我们让IndexSearcher 来接收Query,就可以实现搜索的功能了。 ”

张大胖很兴奋,一个复杂的系统,就这么被搞定了,虽然其中有很多细节没有覆盖,但大的方向已经确定, 细节在具体的开发中来处理吧。

他摩拳擦掌,准备大干一场,把这个设计给实现了。 

可是Bill在网上搜索了一会儿,泼了一盆冷水:“大胖,好像有个叫做Lucene的开源系统,和我们的设计很像啊,功能更加强大,要不你就用它这个实现吧。”

张大胖赶紧跑到电脑前去看,果然,这个叫Lucene的家伙实现得非常完善,还有很多高级的功能,比如仅仅是“相似度”这个功能,非得对搜索有深入研究才行,自己是望尘莫及。 

他叹了一口气说:“好吧,我去图书馆找王老师,就用这个Lucene吧!”

三年以后

张大胖已经变成了利用Lucene做搜索的高手,各种细节和最佳实践尽在掌握。随着互联网应用的爆炸式增长,搜索变成了一个常见的需求,他甚至在业余时间专门去给人做Lucene的咨询,赚了不少外快。

但是做得多了,张大胖也觉得很烦,这个Lucene用起来实在是太“低级”,很多人向他抱怨: 我就想搜索一下我的商品描述,现在还得理解什么“Directory”,"Analyzer","Query",实在是太复杂了! 还有我们的数据越来越多,索引也越来占的空间也越来越大,怎么才能实现分布式的存储啊? 

这样的抱怨听多了,张大胖心想:是时候对Lucene做一层封装,提供一个简单的API了......

后记:接下来还可以讲一下Elastic Search ,也不知道大家是否感兴趣,再加上这篇文章已经很长了,就停笔了。 如果各位感兴趣的,留言告诉我一下,我会继续写下去。

【本文为51CTO专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2009-02-25 13:41:49

全文搜索内置函数MySQL

2009-02-25 13:59:57

布尔全文搜索全文搜索内置函数

2010-11-10 14:18:15

SQL SERVER全

2009-03-19 10:24:27

全文检索文本定位Oracle

2009-08-14 11:24:10

MySQL全文检索MySQL Like索

2015-04-03 11:23:11

JavaScript全文搜索相关度评分

2010-05-04 12:18:43

Oracle Text

2010-10-20 10:55:54

SQL Server全

2009-07-06 18:18:41

SQL Server全

2022-11-15 18:31:37

React

2012-07-31 10:37:31

Lucene.net

2011-08-30 12:34:18

Oracle C#Oracle Text

2010-07-21 09:58:21

SQL Server全

2015-07-13 11:39:25

SphinxSQL

2023-08-02 08:02:30

Redis数据原生方法

2020-06-10 08:23:44

JavaScript开发Web

2010-11-25 14:56:46

MySQL全文查询

2022-03-22 15:20:32

微信全文搜索

2010-06-17 10:56:57

SQL Server数

2010-07-09 10:36:22

SQL Server
点赞
收藏

51CTO技术栈公众号