DBS

.NET~~勇往直前!后来者居上!!

导航

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大致给出了这些具体文件之间的关系:

第一章-demo结果图图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:

arch

图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模块,最后返回给用户相关信息,完成查询工作。

posted on 2008-11-19 17:33  zhangjpb  阅读(1466)  评论(1编辑  收藏  举报