Lucene的系统架构(索引方式)
Lucene是一个完全使用Java SDK开发的全文检索工具,没用使用到第三方的java开发包。因此,我们使用Lucene时,只需要把lucene-core-2.0.0.jar包引入到工程中就可以了。为了作为后续章节的示例,展示索引文件结构,我们给出一个较有代表性的示例:
程序1.1 EntryLucene.java:使用Lucene的最简单示例应用(标引部分) |
public void buildIndex(String indexDir) throws IOException{
IndexWriter writer =
new IndexWriter(indexDir,
new StandardAnalyzer(), true); ① 创建Lucene索引
writer.setUseCompoundFile(false); ② 非复合式索引模式
writer.setMergeFactor(2); ③ 合并频率的大小设置为2
Document docOne = new Document(); ④ 建立Document对象
docOne.add(new Field("Title1",
"term1 term2 term3",
Field.Store.YES,
Field.Index.TOKENIZED)); ⑤ 索引文件内容
writer.addDocument(docOne);
Document docTwo = new Document();
docTwo.add(new Field("Title1",
"term2 term3 term2",
Field.Store.YES,
Field.Index.TOKENIZED));
writer.addDocument(docTwo);
Document docThree = new Document();
docThree.add(new Field("Title1",
"term3 term3",
Field.Store.YES,
Field.Index.TOKENIZED));
writer.addDocument(docThree); ⑥将Document对象加到索引中去
Document docFour = new Document();
docFour.add(new Field("Title2",
"Hello Lucene!",
Field.Store.YES,
Field.Index.UN_TOKENIZED));
writer.addDocument(docFour);
Document docFive = new Document();
docFive.add(new Field("Title2",
"I like Lucene!",
Field.Store.NO,
Field.Index.UN_TOKENIZED,
Field.TermVector.WITH_POSITIONS_OFFSETS));
writer.addDocument(docFive);
Document docSix = new Document();
docSix.add(new Field("Title3",
"Do you want use it?",
Field.Store.NO,
Field.Index.TOKENIZED,
Field.TermVector.YES));
docSix.add(new Field("docName",
"docSix",
Field.Store.YES,
Field.Index.UN_TOKENIZED));
writer.addDocument(docSix);
writer.close();⑦ 关闭索引,完成索引建立过程
}
这个示例中,有几处需要特别指出的地方:
② Lucene的索引形式有2种:“复合式索引”和“非复合式索引”。通过IndexWriter类的setUseCompoundFile(boolean)方法进行设置。本例设为“False”表示不使用复合索引(为了展示Lucene具体的索引文件)。建立后的索引文件请参见图1.2;若设为“True”或者默认情况下(不调用该方法),表示开启复合索引功能,建立后的索引文件请参见图1.3。使用复合式索引的优点是可以减少文件的IO操作(《lucence in action》一书由介绍)。
③ 内存中的Segments数量达到指定的数量时(Lucene默认值为10),会把这些数量的Segments合并成一个Segment。
⑦ 执行关闭索引的close()方法时,真正完成了索引文件写入磁盘的操作。
执行上面的代码,能索引目录下看到相关的字典,频率,位置等文件,如图1.2所示:
图1.2 Lucene建立索引的文件
如果采用复合索引形式的话,图1.2中的那些文件名中带_N的文件就会聚合成一个.cfs文件。下图就是采用复合索引模式执行的结果:
图1.3 Lucene建立复合式索引后的文件
示例中采用非复合式索引形式,下面对各个文件分别做个简单介绍:
表1.1 索引文件含义
索引文件 |
索引文件含义 |
.f(n) |
规格化文件。 |
.fdt |
包含各个域数据(field的特性)信息 |
.fdx |
它是指向.fdt文件的指针。填写的是.fdt文件中每个文档的填写field信息的起始位置 |
.fnm |
各个域的名字信息。 |
.frq |
词元(term)的频率信息 |
.prx |
term在文档中的位置信息 |
.tis |
包含term数据信息,指向位置文件与频率文件的指针。 |
.tii |
是.tis文件的快表,可以快速定位.tis文件中term数据信息。 |
.tvd |
保存有document信息,用词元向量(TermVector)方式保存field的信息,同它包含一个指针表,表内的指针指向.tvf文件中的field信息。 |
.tvf |
以TermVector方式保存field数据信息。 |
.tvx |
是.tvd的索引文件,保存了指向.tvd指针信息。 |
deletable |
包含要删除的文档信息 |
Segments_N与segments.gen |
保存了相关段的信息。 |
生成的这些索引文件,它们之间是什么关系呢?从文件后缀名可以大概知道,这些文件常常是一组一组出现,如.fdx和.fdt形成一组,.fdx文件是.fdt文件的索引快表;.tis和.tii又形成一组,.tii文件是.tis文件的索引快表。图1.4大致给出了这些具体文件之间的关系:
图1.4 各个索引文件之间的关系
使用Lucene如何搜索呢?来看下面的示例。
我们先看下面的展示Lucene查询功能代码,详见程序1.2:
程序1.2 EntryLucene.java:使用Lucene的最简单示例应用(查询部分) |
public void doSearcher(String docName,
String keyword, String indexDir) {
Query query = null; QueryParser queryParser = null;
try {
Directory dir =
FSDirectory.getDirectory(indexDir,false);
IndexSearcher searcher =
new IndexSearcher(dir);① 打开索引
queryParser = new QueryParser(docName,
new StandardAnalyzer());
query = queryParser.parse(keyword);②对Keyword解析
Hits hits = searcher.search(query);③检索索引
printResult(hits, keyword);
} catch (Exception e) {
e.printStackTrace();
}
}
private void printResult(Hits hits, String key)
throws IOException {
System.out.println("搜索结果: ");
if (hits.length() != 0) {
System.out.println("搜索 '" + key + "' 一共找到 "
+ hits.length() + " 个文档!");
System.out.print("您要查的内容可以在下列文档中查找到:");
for (int i = 0; i < hits.length(); i++) {
System.out.print(" '"
图1.5 运行结果
程序1.2已经给我们展示了一个完整的搜索功能的例子。这个过程有点儿类似JDBC的查询过程:创建应用与数据库的连接,然后编写查询语句,通过查询对象搜索相关数据库表,把查询结果放到一个结果集对象里。Lucene的搜索也需要类似JDBC的几个对象:Searcher、FSDirectory、Query、StandardAnalyzer和Hits。首先通过Searcher和FSDirectory打开已经建立的索引(文件),然后编写查询语句,即创建一个Query对象,并通过Searcher进行搜索,最后把查询出来的内容,放到Hits这个容器里。如此完成一个Lucene的查询过程。
从图1.5中可以看出,Hits会要查询的内容出现在哪几个文档里。例如,根据例子1.1提供的例子,“term2”这个关键字曾经在“docOne”、“docTwo”这2个文档中出现过。
1.2.2 Lucene采用的索引结构
对比全文扫描,签名文件,后缀树等等索引结构,Lucene为提供准确快速的查询, Lucene采用倒排文件作为它的索引结构。这在后续章节有详细的说明。
1.2.3 Lucene软件包架构
Lucene选择面向对象的方法将搜索引擎的架构模块化,可以选择多种语言来实现这个搜索引擎而不必改变整体架构。
我们给出它的体系结构图。请参考图1.6:
图1.6 Lucene软件架构
各个模块是使用符合MDA规则的,方便用户选择有效的接口,也为用户定制自己所需的各个模块提供高可扩展性。各个模块之间保持固定的协议,将实现方法隐藏起来。
从图1.6我们可以了解到,Lucene一共分为5个模块,分别是:Corpus(语料库)、Analysis(解析)、Index(索引)、Storage(存储)、Search(搜索)。每一模块从逻辑上又可以再划分为2部分:交互协议部分与具体实现部分。
Corpus(语料库)中是需要被解析的文档。语料库为Analysis模块提供了访问协议,Analysis通过该协议得到料库的内容。
Analysis(解析)模块为搜索Search(模块)和Index(索引)模块提供了相同的解析过程(交互协议相同)。Lucene建立索引过程中,需要对被索引的文件进行分析;对于搜索过程而言,用户输入的查询条件,也需要通过解析(Analysis),通过相同的解析过程后,用户的查询信息才能和从文本解析出的信息相匹配,才能返回给用户正确的结果。
Index(索引)模块提供2种访问协议。一种为搜索提供访问,另一种协议为维护索引提供服务。使用Lucene创建索引时,需要对文本内容建立索引;而对索引维护也可以通过该接口访问索引,更新索引信息,优化索引。
Storage(存储)模块也提供了2种保存索引方式,一种将索引信息存放在内存中,一种针对索引信息存放在物理磁盘中。RAM存储接口可以批量地索引文件,加快标引速度。
Search(搜索)模块是用户和Lucene交互的一个窗口。用户输入的内容都通过该模块进入到Lucene的内部,然后通过Search模块,最后返回给用户相关信息,完成查询工作。
![](http://www.google.cn/intl/zh-CN/images/logo_cn.gif)