Lucene 基础理论

1. 全文检索系统与Lucene简介

1.1 什么是全文检索与全文检索系统

全文检索是指计算机索引程序经过扫描文章中的每个词,对每个词创建一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先创建的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程相似于经过字典中的检索字表查字的过程。java

全文检索的方法主要分为按字检索和按词检索两种。按字检索是指对于文章中的每个字都创建索引,检索时将词分解为字的组合。对于各类不一样的语言而言,字有不一样的含义,好比英文中字与词其实是合一的,而中文中字与词有很大分别。按词检索指对文章中的词,即语义单位创建索引,检索时按词检索,而且能够处理同义项等。英文等西方文字因为按照空白切分词,所以实现上与按字处理相似,添加同义处理也很容易。中文等东方文字则须要切分字词,以达到按词索引的目的,关于这方面的问题,是当前全文检索技术尤为是中文全文检索技术中的难点,在此不作详述。程序员

全文检索系统是按照全文检索理论创建起来的用于提供全文检索服务的软件系统。通常来讲,全文检索须要具有创建索引和提供查询的基本功能,此外现代的全文检索系统还须要具备方便的用户接口、面向WWW[1]的开发接口、二次应用开发接口等等。功能上,全文检索系统核心具备创建索引、处理查询返回结果集、增长索引、优化索引结构等等功能,外围则由各类不一样应用具备的功能组成。结构上,全文检索系统核心具备索引引擎、查询引擎、文本分析引擎、对外接口等等,加上各类外围应用系统等等共同构成了全文检索系统。图1.1展现了上述全文检索系统的结构与功能。算法

clip_image002

在上图中,咱们看到:全文检索系统中最为关键的部分是全文检索引擎,各类应用程序都须要创建在这个引擎之上。一个全文检索应用的优异程度,根本上由全文检索引擎来决定。所以提高全文检索引擎的效率便是咱们提高全文检索应用的根本。另外一个方面,一个优异的全文检索引擎,在作到效率优化的同时,还须要具备开放的体系结构,以方便程序员对整个系统进行优化改造,或者是添加原有系统没有的功能。好比在当今多语言处理的环境下,有时须要给全文检索系统添加处理某种语言或者文本格式的功能,好比在英文系统中添加中文处理功能,在纯文本系统中添加XML或者HTML格式的文本处理功能,系统的开放性和扩充性就十分的重要。apache

1.2 什么是Lucene

Lucene是apache软件基金会jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础创建起完整的全文检索引擎。编程

Lucene的原做者是Doug Cutting,他是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些Internet底层架构的研究。早先发布在做者本身的http://www.lucene.com/,后来发布在SourceForge,2001年年末成为apache软件基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/数组

1.3 Lucene的应用、特色及优点

做为一个开放源代码项目,Lucene从问世以后,引起了开放源代码社群的巨大反响,程序员们不只使用它构建具体的全文检索应用,并且将之集成到各类系统软件中去,以及构建Web应用,甚至某些商业软件也采用了Lucene做为其内部全文检索子系统的核心。apache软件基金会的网站使用了Lucene做为全文检索的引擎,IBM的开源软件eclipse的2.1版本中也采用了Lucene做为帮助子系统的全文索引引擎,相应的IBM的商业软件Web Sphere中也采用了Lucene。Lucene以其开放源代码的特性、优异的索引结构、良好的系统架构得到了愈来愈多的应用。服务器

Lucene做为一个全文检索引擎,其具备以下突出的优势:网络

(1)索引文件格式独立于应用平台。Lucene定义了一套以8位字节为基础的索引文件格式,使得兼容系统或者不一样平台的应用可以共享创建的索引文件。数据结构

(2)在传统全文检索引擎的倒排索引的基础上,实现了分块索引,可以针对新的文件创建小文件索引,提高索引速度。而后经过与原有索引的合并,达到优化的目的。架构

(3)优秀的面向对象的系统架构,使得对于Lucene扩展的学习难度下降,方便扩充新功能。

(4)设计了独立于语言和文件格式的文本分析接口,索引器经过接受Token流完成索引文件的创立,用户扩展新的语言和文件格式,只须要实现文本分析的接口。

(5)已经默认实现了一套强大的查询引擎,用户无需本身编写代码即便系统可得到强大的查询能力,Lucene的查询实现中默认实现了布尔操做、模糊查询(Fuzzy Search)、分组查询等等。

面对已经存在的商业全文检索引擎,Lucene也具备至关的优点:

首先,它的开发源代码发行方式(遵照Apache Software License),在此基础上程序员不只仅能够充分的利用Lucene所提供的强大功能,并且能够深刻细致的学习到全文检索引擎制做技术和面相对象编程的实践,进而在此基础上根据应用的实际状况编写出更好的更适合当前应用的全文检索引擎。在这一点上,商业软件的灵活性远远不及Lucene。其次,Lucene秉承了开放源代码一向的架构优良的优点,设计了一个合理而极具扩充能力的面向对象架构,程序员能够在Lucene的基础上扩充各类功能,好比扩充中文处理能力,从文本扩充到HTML、PDF等等文本格式的处理,编写这些扩展的功能不只仅不复杂,并且因为Lucene恰当合理的对系统设备作了程序上的抽象,扩展的功能也能轻易的达到跨平台的能力。最后,转移到apache软件基金会后,借助于apache软件基金会的网络平台,程序员能够方便的和开发者、其它程序员交流,促成资源的共享,甚至直接得到已经编写完备的扩充功能。最后,虽然Lucene使用Java语言写成,可是开放源代码社区的程序员正在不懈的将之使用各类传统语言实现(例如.net framework),在遵照Lucene索引文件格式的基础上,使得Lucene可以运行在各类各样的平台上,系统管理员能够根据当前的平台适合的语言来合理的选。

 

2. Lucene系统结构分析

2.1 系统结构组织

Lucene做为一个优秀的全文检索引擎,其系统结构具备强烈的面向对象特征。首先是定义了一个与平台无关的索引文件格式,其次经过抽象将系统的核心组成部分设计为抽象类,具体的平台实现部分设计为抽象类的实现,此外与具体平台相关的部分好比文件存储也封装为类,通过层层的面向对象式的处理,最终达成了一个低耦合高效率,容易二次开发的检索引擎系统。

如下将讨论Lucene系统的结构组织,并给出系统结构与源码组织图:

clip_image004

从图中咱们清楚的看到,Lucene的系统由基础结构封装、索引核心、对外接口三大部分组成。其中直接操做索引文件的索引核心又是系统的重点。Lucene的将全部源码分为了7个模块(在java语言中以包即package来表示),各个模块所属的系统部分也如上图所示。须要说明的是org.apache.lucene.queryPaser是作为org.apache.lucene.search的语法解析器存在,不被系统以外实际调用,所以这里没有看成对外接口看待,而是将之独立出来。

从面象对象的观点来考察,Lucene应用了最基本的一条程序设计准则:引入额外的抽象层以下降耦合性。首先,引入对索引文件的操做org.apache.lucene.store的封装,而后将索引部分的实现创建在(org.apache.lucene.index)其之上,完成对索引核心的抽象。在索引核心的基础上开始设计对外的接口org.apache.lucene.search与org.apache.lucene.analysis。在每个局部细节上,好比某些经常使用的数据结构与算法上,Lucene也充分的应用了这一条准则。在高度的面向对象理论的支撑下,使得Lucene的实现容易理解,易于扩展。

Lucene在系统结构上的另外一个特色表现为其引入了传统的客户端服务器结构之外的的应用结构。Lucene能够做为一个运行库被包含进入应用自己中去,而不是作为一个单独的索引服务器存在。这天然和Lucene开放源代码的特征分不开,可是也体现了Lucene在编写上的原本意图:提供一个全文索引引擎的架构,而不是实现。

2.2 数据流分析

了解数据流分析的重要性:

理解Lucene系统结构的另外一个方式是去探讨其中数据流的走向,并以此摸清楚Lucene系统内部的调用时序。在此基础上,咱们可以更加深刻的理解Lucene的系统结构组织,以方便之后在Lucene系统上的开发工做。这部分的分析,是深刻Lucene系统的钥匙,也是进行重写的基础。

Lucene系统中的主要的数据流以及它们之间的关系图:

clip_image006

图2.2很好的代表了Lucene在内部的数据流组织状况,而且沿着数据流的方向咱们也能够对与Lucene内部的执行时序有一个清楚的了解。如今将图中的涉及到的流的类型与各个逻辑对应系统的相关部分的关系说明一下。

图中共存在4种数据流,分别是文本流、token流、字节流与查询语句对象流。文本流表示了对于索引目标和交互控制的抽象,即用文本流表示了将要索引的文件,用文本流向用户输出信息;在实际的实现中,Lucene中的文本流采用了UCS-2做为编码,以达到适应多种语言文字的处理的目的。Token流是Lucene内部所使用的概念,是对传统文字中的词的概念的抽象,也是Lucene在创建索引时直接处理的最小单位;简单的讲Token就是一个词和所在域值的组合,后面在叙述文件格式时也将继续涉及到token,这里不详细展开。字节流则是对文件抽象的直接操做的体现,经过固定长度的字节(Lucene定义为8比特位长,后面文件格式将详细叙述)流的处理,将文件操做解脱出来,也作到了与平台文件系统的无关性。查询语句对象流则是仅仅在查询语句解析时用到的概念,它对查询语句抽象,经过类的继承结构反映查询语句的结构,将之传送到查找逻辑来进行查找的操做。

图中的涉及到了多种逻辑,基本上直接对应于系统某一模块,可是也有跨模块调用的问题发生,这是由于Lucene的重用程度很是好,所以不少实现直接调用了之前的工做成果,这在某种程度上实际上是增强了模块耦合性,可是也是为了不系统的过于庞大和没必要要的重复设计的一种折衷体现。词法分析逻辑对应于org.apache.lucene.analysis部分。查询语句语法分析逻辑对应于org.apache.lucene.queryParser部分,而且调用了org.apache.lucene.analysis的代码。查询结束以后向评分排序逻辑输出token流,继而由评分排序逻辑处理以后给出文本流的结果,这一部分的实现也包含在了org.apache.lucene.search中。索引构建逻辑对应于org.apache.lucene.index部分。索引查找逻辑则主要是org.apache.lucene.search,可是也大量的使用了org.apache.lucene.index部分的代码和接口定义。存储抽象对应于org.apache.lucene.store。没有提到的模块则是作为系统公共基础设施存在。

2.3 基于Lucene的应用开发

首先,咱们须要的是按照目标语言的词法结构来构建相应的词法分析逻辑,实现Lucene在org.apache.lucene.analysis中定义的接口,为Lucene提供目标系统所使用的语言处理能力。Lucene默认的已经实现了英文和德文的简单词法分析逻辑(按照空格分词,并去除经常使用的语法词,如英语中的is,am,are等等)。在这里,主要须要参考实现的接口在org.apache.lucene.analysis中的Analyzer.java和Tokenizer.java中定义,Lucene提供了不少英文规范的实现样本,也能够作为实现时候的参考资料。其次,须要按照被索引的文件的格式来提供相应的文本分析逻辑,这里是指除开词法分析以外的部分,好比HTML文件,一般须要把其中的内容按照所属于域分门别类加入索引,这就须要从org.apache.lucene.document中定义的类document继承,定义本身的HTMLDocument类,而后就能够将之交给org.apache.lucene.index模块来写入索引文件。完成了这两步以后,Lucene全文检索引擎就基本上完备了。这个过程能够用下图表示:

clip_image008

下面是使用java语言开发,Lucene系统可以方便的嵌入到整个系统中去,做为一个API集来调用。这个过程十分简单,如下即是一个示例程序,配合注释理解起来很容易。

clip_image009

2.4 Lucene索引文件格式

首先在Lucene的文件格式中,以字节为基础,定义了以下的数据类型:

 3.1 Lucene文件格式中定义的数据类型

数据类型

所占字节长度(字节)

说明

Byte

1

基本数据类型,其余数据类型以此为基础定义

UInt32

4

32位无符号整数,高位优先

UInt64

8

64位无符号整数,高位优先

VInt

不定,最少1字节

动态长度整数,每字节的最高位代表还剩多少字节,每字节的低七位代表整数的值,高位优先。能够认为值能够为无限大。其示例以下

字节1

字节2

字节3

0

00000000

   

1

00000001

   

2

00000010

   

127

01111111

   

128

10000000

00000001

 

129

10000001

00000001

 

130

10000010

00000001

 

16383

10000000

10000000

00000001

16384

10000001

10000000

00000001

16385

10000010

10000000

00000001

 

Chars

不定,最少1字节

采用UTF-8编码[20]的Unicode字符序列

String

不定,最少2字节

由VInt和Chars组成的字符串类型,VInt表示Chars的长度,Chars则表示了String的值

以上的数据类型就是Lucene索引文件格式中用到的所有数据类型,因为它们都以字节为基础定义而来,所以保证了是平台无关,这也是Lucene索引文件格式平台无关的主要缘由。接下来咱们看看Lucene索引文件的概念组成和结构组成。

clip_image010

以上就是Lucene的索引文件的概念结构。Lucene索引index由若干段(segment)组成,每一段由若干的文档(document)组成,每个文档由若干的域(field)组成,每个域由若干的项(term)组成。项是最小的索引概念单位,它直接表明了一个字符串以及其在文件中的位置、出现次数等信息。域是一个关联的元组,由一个域名和一个域值组成,域名是一个字串,域值是一个项,好比将“标题”和实际标题的项组成的域。文档是提取了某个文件中的全部信息以后的结果,这些组成了段,或者称为一个子索引。子索引能够组合为索引,也能够合并为一个新的包含了全部合并项内部元素的子索引。咱们能够清楚的看出,Lucene的索引结构在概念上即为传统的倒排索引结构。

从概念上映射到结构中,索引被处理为一个目录(文件夹),其中含有的全部文件即为其内容,这些文件按照所属的段不一样分组存放,同组的文件拥有相同的文件名,不一样的扩展名。此外还有三个文件,分别用来保存全部的段的记录、保存已删除文件的记录和控制读写的同步,它们分别是segments,deletable和lock文件,都没有扩展名。每一个段包含一组文件,它们的文件扩展名不一样,可是文件名均为记录在文件segments中段的名字。让咱们看以下的结构图3.2:

clip_image012

每一个段的文件中,主要记录了两大类的信息:域集合与项集合。这两个集合中所含有的文件在图3.2中均有代表。因为索引信息是静态存储的,域集合与项集合中的文件组采用了一种相似的存储办法:一个小型的索引文件,运行时载入内存;一个对应于索引文件的实际信息文件,能够按照索引中指示的偏移量随机访问;索引文件与信息文件在记录的排列顺序上存在隐式的对应关系,即索引文件中按照“索引项一、索引项2…”排列,则信息文件则也按照“信息项一、信息项2…”排列。好比在图3.2所示文件中,segment1.fdx与segment1.fdt之间,segment1.tii与segment1.tis、segment1.prx、segment1.frq之间,都存在这样的组织关系。而域集合与项集合之间则经过域的在域记录文件(好比segment1.fnm)中所记录的域记录号维持对应关系,在图3.2中segment1.fdx与segment1.tii中就是经过这种方式保持联系。这样,域集合和项集合不只仅联系起来,并且其中的文件之间也相互联系起来。此外,标准化因子文件和被删除文档文件则提供了一些程序内部的辅助设施(标准化因子用在评分排序机制中,被删除文档是一种伪删除手段)。这样,整个段的索引信息就经过这些文档有机的组成。

 

2.5 一些公用的基础类

基础结构封装,或者基础类,由org.apache.lucene.util和org.apache.lucene.document两个包组成,前者定义了一些常量和优化过的经常使用的数据结构和算法,后者则是对于文档(document)和域(field)概念的一个类定义。如下咱们用列表的方式来分析这些封装类,指出其要点;

 

 3.2 基础类包org.apache.lucene.util

 

说明

Arrays

一个关于数组的排序方法的静态类,提供了优化的基于快排序的排序方法sort

BitVector

C/C++语言中位域的java实现品,可是加入了序列化能力

Constants

常量静态类,定义了一些常量

PriorityQueue

一个优先队列的抽象类,用于后面实现各类具体的优先队列,提供常数时间内的最小元素访问能力,内部实现机制是哈析表和堆排序算法

 

 3.3 基础类包org.apache.lucene.document

 

说明

Document

是文档概念的一个实现类,每一个文档包含了一个域表(fieldList),并提供了一些实用的方法,好比多种添加域的方法、返回域表的迭代器的方法

Field

是域概念的一个实现类,每一个域包含了一个域名和一个值,以及一些相关的属性

DateField

提供了一些辅助方法的静态类,这些方法将java中Date和Time数据类型和String相互转化

 

2.6 存储抽象

org.apache.lucene.store包:存储抽象是惟一可以直接对索引文件存取的包,所以其主要目的是抽象出和平台文件系统无关的存储抽象,提供诸如目录服务(增、删文件)、输入流和输出流。在分析其实现以前,首先咱们看一下UML图;

clip_image013

 3.3 存储抽象实现UML图(一)

clip_image014

 3.4 存储抽象实现UML图(二)

clip_image015

 3.4 存储抽象实现UML图(三)

图3.2到3.4展现了整个org.apache.lucene.store中主要的继承体系。共有三个抽象类定义:Directory、InputStream和OutputStrem,构成了一个完整的基于抽象文件系统的存取体系结构,在此基础上,实做出了两个实现品:(FSDirectory,FSInputStream,FSOutputStream)和(RAMDirectory,RAMInputStream和RAMOutputStream)。前者是以实际的文件系统作为基础实现的,后者则是创建在内存中的虚拟文件系统。前者主要用来永久的保存索引文件,后者的做用则在于索引操做时是在内存中创建小的索引,而后一次性的输出合并到文件中去,这一点咱们在后面的索引逻辑部分可以看到。此外,还定以了org.apache.lucene.store.lock和org.apache.lucene.store.with两个辅助内部实现的类用在实现Directory方法的makeLock的时候,以在锁定索引读写以前来让客户程序作一些准备工做。

(FSDirectory,FSInputStream,FSOutputStream)的内部实现依托于java语言中的io类库,只是简单的作了一个外部逻辑的包装。这固然要归功于java语言所提供的跨平台特性,同时也带了一些隐患:文件存取的效率提高须要依耐于文件类库的优化。若是须要继续优化文件存取的效率,应该还提供一个文件与目录的抽象,以根据各类文件系统或者文件类型来提供一个优化的机会。固然,这是应用开发者所不须要关系的问题。

(RAMDirectory,RAMInputStream和RAMOutputStream)的内部实现就比较直接了,直接采用了虚拟的文件RAMFile类(定义于文件RAMDirectory.java中)来表示文件,目录则看做一个String与RAMFile对应的关联数组。RAMFile中采用数组来表示文件的存储空间。在此的基础上,完成各项操做的实现,就造成了基于内存的虚拟文件系统。由于在实际使用时,并不会牵涉到很大字节数量的文件,所以这种设计是简单直接的,也是高效率的。

 

 

3. Lucene索引构建逻辑模块分析

3.1对象体系与UML图

1. 项(Term

项(Term):包括概念所实际涉及的类、永久化类。项(Term)所表示的是一个字符串,它拥有域、频数和位置信息等等属性。所以,Lucene中设计了两个类来表示这个概念,以下图

clip_image016

 4.1 UML图(-)

 

上图中,有意的突出了类Term和TermInfo中的数据成员,由于它反映了对于项(Term)这个概念的具体表示。同时上图中也同时列出了用于永久化项(Term)的代理类TermInfosWriter和TermInfosReader,它们完成永久化的功能,须要注意的是,TermInfosReader内部使用了数组indexTerms和indexInfos来存储一系列项;而TermInfosWriter则是一个相似于链表的结构,经过一个other指向下一个TermInfosWriter,每个TermInfosWriter只负责自己那个lastTerm和lastTi的永久化工做。这是一个设计上的技巧,经过批量读取(或者称为缓冲的方式)来得到读入时候的效率优化;而经过一个链表式的、各负其责的方式,来得到写出时候的设计简化。

项(term)这部分的设计中,还有一些重要的接口和类:

clip_image017

图 4.2 UML图(二)

 

图4.2中,咱们看到三个类:TermEnum、TermDocs与TermPositions,第一个是抽象类,后两个都是接口。TermEnum的设计主要用在后面Segment和Document等等的实现中,以提供枚举其中每个项(Term)的能力。TermDocs是一个接口,用来继承以提供返回<document, frequency>值对的能力,经过这个接口就能够得到某个项(Term)在某个文档中出现的频数。TermPositions则是在TermDocs上的扩展,将项(Term)在文档中的位置信息也表示出来。TermDocs(TermPositions)接口的使用方式相似于java中的Enumration接口,即经过next方法跳转,经过doc,freq等方法得到当前的属性值。

2. 域(Field

因为Field的基本概念在org.apache.lucene.document中已经作了定义,所以在这部分主要是针对项文件(.fnm文件、.fdx文件、.fdt文件)所须要的信息再来设计一些类。

clip_image018

图 4.3 UML图(三)

 

图 4.3中展现的,就是表示与域(Field)所关联的属性信息的类。其中isIndexed表示的这个域的值是否被索引过,即值是否被分词而后索引;另外两个属性所表示的意思则很明显:一个是域的名字,一个是域的编号。

关于域表和存取逻辑的UML图:

clip_image019

FieldInfos即为域表的概念表示,内部采用了冗余的方式以获取在经过域的编号访问或者经过域的名字来访问时候的高效率。FieldsReader与FieldsWriter则分别是写出和读入的代理类。在功能和实现上,这两个类都比较简单。

3. 文档(document)

文档(document)一样也是在org.apache.lucene.document中定义过的结构。因为对于这部分比较重要,咱们也来看看其UML图:

clip_image020

图 4.5 UML图(五)

在图4.5中咱们看到,Document的设计基本上沿用了链表的处理方法。左边的Document类做为一个数据外包类,用来提供对于内部结构DocumentFieldList的增长删除访问操做等等。DocumentFieldList才是实际上的数据存储单位,它用了链表的处理方法,直接指向一个当前的Field对象和下一个DocumentFieldList对象,这个与前面的相似。为了可以逐个访问链表中的节点,还设计了DocumentFieldEnumeration枚举类。

clip_image021

图 4.6 UML图(六)

 

 

实际上定义于org.apache.lucene.index中的有关于Document的就是永久化的代理类。在图4.6中给出了其UML图。须要说明的是为何没有出现读入的方法:这个方法已经隐含在图4.5中Document类中的add方法中了,结合图2.4中的程序代码段,咱们就可以清楚的理解这种设计。

4. 段(segment

段(Segment)这一部分设计的比较特殊,在实现简单的对象结构之上,还特地的设计了用于段之间合并的类。接下来,咱们仍然采起对照UML分析的方式逐个叙述。接下来咱们看Lucene中如何表示段这个概念。

clip_image022

图 4.7 UML图(七)

Lucene定义了一个类SegmentInfo用来表示每个段(Segment)的信息,包括名字(name)、含有的文档的数目(docCount)和段所位于的目录的位置(dir)。根据索引文件中的段的意义,有了这三点,就能惟一肯定一个段了。SegmentInfos这个类则是用来表示一个段的链表(从标准的java.util.Vector继承而来),实际上,也就是索引(index)的意思了。须要注意的是,这里并无在SegmentInfo中安插一个文档(document)的链表。这样作的缘由牵涉到Lucene内部对于文档(至关于一个被索引文件)的处理;Lucene内部采用了赋予文档编号,给域赋值的方式来处理文档,即加入的文档顺次编号,之后用文档号表示文档,而路径信息,文件名字等等在之后索引查找须要的属性,都做为域存储下来;所以SegmentInfo中并无另外存储一个文档(document)的链表,对于这些的写出和读入,则交给了永久化的代理类来作。

clip_image023

图 4.8 UML图(八)

图4.8给出了负责段(segment)的读入操做的代理类,而负责段(segment)的写出操做也一样没有定义,这些操做都直接实如今了类IndexWriter类中。段的操做一样采用了以前的数组或者说是缓冲的处理方式。

针对前面项(term)那部分定义的几个接口,段(segment)这部分也须要作相应的接口实现,由于提供直接遍历访问段中的各个项的能力对于检索来讲,无疑是十分重要的。即这部分的设计,实际上都是在为了检索在服务。

clip_image024

图 4.9 UML图(九)

clip_image025

图 4.10 UML图(十)

图4.9和图4.10分别展现了前面项(term)那里定义的接口是如何在这里经过继承实现的。Lucene在处理这部分的时候,也是分红两部分(Segment与Segments开头的类)来实现,并且很合理的运用了数组的技法,以及注意了继承重用。可是细化到局部,终归是比较简单的按照语义来得到结果而已了。

Lucene为了兼顾创建索引时的效率和读取索引查找的速度,引入了分小段创建索引的方式,即每一次批量创建索引时,先在内存中的虚拟文件系统中为每个文档单独创建一个段,而后在输出的时候将这些段合并以后输出成为索引文件,这时仅仅存在一个段。屡次创建的索引后,若是想优化索引文件,也可采起合并段的方法,将索引中的段合并成为一个段。咱们来看一下在IndexWriter类中相应的方法的实现,来了解一下这中创建索引的实现。

clip_image026

 

在mergeSegments函数中,将用到几个重要的类结构,它们记录了合并时候的一些重要信息,完成合并时候的工做。接下来,咱们来看这几个类的UML图:

clip_image027

图 4.12 UML图(十一)

 

从图4.12中,咱们看到Lucene设计一个类SegmentMergeInfo用来保存每个被合并的段的信息,也保存可以访问其内部的接口句柄,也就是说合并时的操做使用这个类做为对被合并的段的操做代理。类SegmentMergeQueue则设计为org.apache.lucene.util.PriorityQueue的子类,作为SegmentMergeInfo的容器类,并且附带可以自动排序。SegmentMerger是主要进行操做的类,主要完成合并各个数据项的问题。

5. IndexReader类与IndexWirter类

最后剩下的,就是整个索引逻辑部分的使用接口类了。外界经过这两个类以及文档(document)类的构造函数调用之,好比图2.4中的代码示例所示。下面咱们来看一下这部分最后两个类的UML图:

clip_image028

图 4.13 UML图(十二)

 

IndexWriter的设计与IndexReader的设计很不相同,前者是一个实现类,然后者是一个抽象类,带有没有实现的接口。IndexWriter的主要做用就是接收新加入的文档(document),而后在内部为之生成相应的小段,最后再合并并向索引文件中输出,图4.11中已经给出了一些实现的代码。因为Lucene在面向对象上封装的努力,经过各个构造函数就已经完成了对于各个概念的构造过程,剩下部分的代码主要是依据各个数组或者是链表中的信息,逐个逐个的将信息写出到相应的文件中去了。IndexReader部分则只是作了接口设计,没有具体的实现,这个和本部分所完成的主要功能有关:索引构建逻辑。设计这个抽象类的目的是,预先完成一些函数,为之后的检索(search)部分的各类形式的IndexReader铺平道路,也是利用了在同一个包内能够方便访问其它类的保护变量这个java语言的限制。

3.2 数据流逻辑

从宏观上明白一个系统的设计,理清楚其中的运行规律,最好的方式应该是经过数据流图。在分析了各个位于索引构建逻辑部分的类的设计以后,咱们接下来就经过分析数据流图的方式来总结一下。可是因为以前提到的缘由:索引读入部分在这一部分并无彻底实现,因此咱们在数据流图中主要给出的是索引构建的数据流图。

clip_image030

对于图4.14中所描述的内容,结合Lucene源代码中的一些文件看,可以加深理解。准备阶段能够参考demo文件夹中的org.apache.lucene.demo.IndexFiles类和java文件夹中的org.apache.lucene.document文件包。索引构建阶段的主要源码位于java文件夹中org.apache.lucene.index.IndexWriter类,所以这部分能够结合这个类的实现来看。至于内存文件系统,比较复杂,可是这时的逻辑相对简单,所以也不难理解。

上面的数据流图十分清楚的勾画除了整个索引构建逻辑这部分的设计:经过层层嵌套的类结构,在构建时候即分步骤有计划的生成了索引结构,将之存储到内存中的文件系统中,而后经过对内存中的文件系统优化合并输出到实际的文件系统中。

相关文章
相关标签/搜索