本文主要讨论 Lucene 的系统结构,希望对其结构的初步分析,更深入的了解 Lucene 的运作机制,从而实现对 Lucene 的功能扩展。
1. Lucene 的包结构
Lucene 源码中共包括 7 个子包,每个包完成特定的功能:
Lucene 包结构功能表 | |
包名 |
功能 |
org.apache.lucene.analysis |
语言分析器,主要用于的切词,支持中文主要是扩展此类 |
org.apache.lucene.document |
索引存储时的文档结构管理,类似于关系型数据库的表结构 |
org.apache.lucene.index |
索引管理,包括索引建立、删除等 |
org.apache.lucene.queryParser |
查询分析器,实现查询关键词间的运算,如与、或、非等 |
org.apache.lucene.search |
检索管理,根据查询条件,检索得到结果 |
org.apache.lucene.store |
数据存储管理,主要包括一些底层的 I/O 操作 |
org.apache.lucene.util |
一些公用类 |
2. Lucene 的主要逻辑图
Lucene 功能强大,但从根本上说,主要包括两块:一是文本内容经切词后索引入库;二是根据查询条件返回结果。
以下是上述两大功能逻辑:
查询逻辑
按先后顺序,查询逻辑可分为如下几步:
1. 查询者输入查询条件
条件之间可以通过特定运算符进行运算,比如查询希望查询到与“中国”和“北京”相关的记录,但不希望结果中包括“海淀区中关村”,于是输入条件为“中国 + 北京 - 海淀区中关村”;
2. 查询条件被传达到查询分析器中,分析器将将对“中国 + 北京 - 海淀区中关村”进行分析,首先分析器解析字符串的连接符,即这里的加号和减号,然后对每个词进行切词,一般最小的词元是两个汉字,则中国和北京两个词不必再切分,但对海淀区中关村需要切分,假设根据切词算法,把该词切分为“海淀区”和“中关村”两部分,则最后得到的查询条件可以表示为:“中国” AND “北京” ANDNOT (“海淀区” AND “中关村”) 。
3. 查询器根据这个条件遍历索引树,得到查询结果,并返回结果集,返回的结果集类似于 JDBC 中的 ResultSet 。
4. 将返回的结果集显示在查询结果页面,当点击某一条内容时,可以链接到原始网页,也可以打开全文检索库中存储的网页内容。
这就是查询的逻辑过程,需要说明的是, Lucene 默认只支持英文,为了便于说明问题,以上查询过程采用中文举例,事实上,当 Lucene 被扩充支持中文后就是这么一个查询过程。
入库逻辑
入库将把内容加载到全文检索库中,按顺序,入库逻辑包括如下过程:
1. 入库者定义到库中文档的结构,比如需要把网站内容加载到全文检索库,让用户通过“站内检索”搜索到相关的网页内容。入库文档结构与关系型数据库中的表结构类似,每个入库的文档由多个字段构成,假设这里需要入库的网站内容包括如下字段:文章标题、作者、发布时间、原文链接、正文内容(一般作为网页快照)。
2. 包含 N 个字段的文档( DOCUMENT )在真正入库前需要经过切词(或分词)索引,切词的规则由语言分析器( ANALYZER )完成。
3. 切分后的“单词”被注册到索引树上,供查询时用,另外也需要也其它不需要索引的内容入库,所有这些是文件操作均由 STORAGE 完成。
以上就是记录加载流程,索引树是一种比较复杂的数据存储结构,将在后续章节陆续介绍,这里就不赘述了,需要说明的一点是, Lucene 的索引树结构非常优秀,是 Lucene 的一大特色。
接下来将对 Lucene 的各个子包的结构进行讨论。
3. 语言分析包 org.apache.lucene.analysis
Analyzer 是一个抽象类,司职对文本内容的切分词规则。
切分后返回一个 TokenStream , TokenStream 中有一个非常重要方法 next() ,即取到下一个词。简单点说,通过切词规则,把一篇文章从头到尾分成一个个的词,这就是 org.apache.lucene.analysis 的工作。
对英文而言,其分词规则很简单,因为每个单词间都有一个空格,按空格取单词即可,当然为了提高英文检索的准确度,也可以把一些短语作为一个整体,其间不切分,这就需要一个词库,对德文、俄文也是类似,稍有不同。
对中文而言,文字之间都是相连的,没有空格,但我们同样可以把字切分,即把每个汉字作为一个词切分,这就是所谓的“切字”,但切字方式方式的索引没有意义,准确率太低,要想提高准确度一般都是切词,这就需要一个词库,词库越大准确度将越高,但入库效率越低。
若要支持中文切词,则需要扩展 Analyzer 类,根据词库中的词把文章切分。
简单点说, org.apache.lucene.analysis 就是完成将文章切分词的任务。
4. 文档结构包 org.apache.lucene.document
document 包相对而言比较简单,该包下面就 3 个类, Document 相对于关系型数据库的记录对象,主要负责字段的管理,字段分两种,一是 Field ,即文本型字段,另一个是日期型字段 DateField 。这个包中关键需要理解的是 Field 中字段存储方式的不同,这在上一篇中已列表提到,下面我们可以参见一下其详细的类图:
5. 索引管理包 org.apache.lucene.index
索引包是整个系统核心,全文检索的的根本就为每个切出来的词建索引,查询时就只需要遍历索引,而不需要去正文中遍历,从而极大的提高检索效率,索引建设的质量关键整个系统的质量。 Lucene 的索引树是非常优质高效的,具体的索引树细节,将在后续章节中重要探讨。
在这个包中,主要学习 IndexWriter 和 IndexReader 这个类。
通过上一篇的初步应用可知,全文检索库的初始化和记录加载均需要通过该类来完成。
初始化全文库的语句为:
IndexWriter indexWriter = new IndexWriter(“ 全文库的目录位置 ”, new StandardAnalyzer(), true );
记录加载的语句为: indexWriter.addDocument(doc);
IndexWriter 主要用于写库,当需要读取库内容时,就需要用到 IndexReader 这个类了。
6. 查询分析包 org.apache.lucene.queryParser 和检索包 org.apache.lucene.search
通过查询分析器( queryParser )解析后,将返回一个查询对象( query ),根据查询对象就可进行检索了。上图描述了 query 对象的生成,下图描述了查询结果集( Hits )的生成。
7. 存储包 org.apache.lucene.store
一些底层的文件 I/O 操作。
8. 工具包 org.apache.lucene.util
该包中包括 4 个工具类。
9. 总结
通过对 Lucene 源码包的分析,我们可以初步认识到 Lucene 的核心类包主要有 3 个:
l org.apache.lucene.analysis
l org.apache.lucene.index
l org.apache.lucene.search
其中 org.apache.lucene.analysis 主要用于切分词,切分词的工作由 Analyzer 的扩展类来实现, Lucene 自带了 StandardAnalyzer 类,我们可以参照该写出自己的切词分析器类,如中文分析器等。
org.apache.lucene.index 主要提供库的读写接口,通过该包可以创建库、添加删除记录及读取记录等。
org.apache.lucene.search 主要提供了检索接口,通过该包,我们可以输入条件,得到查询结果集,与 org.apache.lucene. queryParser 包配合还可以自定义的查询规则,像 google 一样支持查询条件间的与、或、非、属于等复合查询。