【转】全文检索Lucene说明书

来源:互联网 发布:机械运动动画制作软件 编辑:程序博客网 时间:2024/05/21 09:16

本文来自CSDN博客,出处:http://blog.csdn.net/huangxc/archive/2008/03/19/2197962.aspx

一般人们谈到搜索肯定想到的是 Google、Baidu、Yahoo 差点的也有搜狗、搜猫什么的,没想到在偶有生之年还要研究一下这门技术,现在就慢慢的学吧

全文检索Lucene说明书

1引言
1.1编写目的
主要是整体介绍Lucene的原理和架构,理解Lucene是什么、能够做什么,为什么我们要选用Lucene来做全文检索

1.2背景
Lucene最初是由Doug Cutting开发的,可以通过SourceForge下载到。作为服务器端的开源java产品于2001年9月份加入了apache软件基金会的jakarta家族。从那时发布的每一个版本,这个项目都吸引了越来越多的用户和开发者。2002年11月Lucene Version1.2发布了,现在已经升级到2.3.1版本。Lucene以其开放源代码的特性、优异的索引结构、良好的系统架构获得了越来越多的应用,比较著名的有

Jive:WEB论坛系统

Eyebrows:邮件列表HTML归档/浏览/查询系统, 已经成为目前APACHE项目的主要邮件列表归档系统

Cocoon:基于XML的web发布框架,全文检索部分使用了Lucene

Eclipse:基于Java的开放开发平台,帮助部分的全文索引使用了Lucene

等等正在使用,在众多的检索引擎中脱颖而出成为业界最流行的全文检索工具

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

Lucene:它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能的一个基于Java编程的工具包

Nutch(网络爬虫):用Lucene工具实现的一个开源全文检索项目

Cygwin:是一个用于在Windows上模拟Linux环境的软件

1.4参考资料
a.  从LUCENE.COM.CN 等网站相关文档整理

b.  对Lucene的理解和认识

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/

可以访问这个项目

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


                          (图一)

3.1 Lucene功能
Lucence可以把文本格式的数据建立索引并能够提供搜索返回搜索结果,它不关心数据的来源、格式甚至语言,只要你能够将它转换成文本数据,这就意味着你可建立索引并搜索存放于文件中的数据。不管在远程的服务器上的web页面,存于本地文件的文档或者数据库数据,不管是简单的文本文件,微软Word文档,html或pdf文件或者其他能够提取出文本信息的格式都可以建立索引并搜索返回结果

3.2 Lucene索引和搜索
    Lucene索引的机制架构图 如(图二):

   

                               (图二)

    从上图中我们可以看出数据源的格式有多种多样,html、pdf、Word、txt等等可以经过相应的解析器(parser)转换成txt数据,然后通过适合的分析器(Lucene Analyzer)而生成索引文件(Index Files)

   建立索引的过程就是把数据源处理成非常容易查询的索引文件的过程,把文本转化为一个可以让你快速搜索的格式,除去缓慢的顺序地扫描过程。这个转化过程称为索引,Lucene采用的是倒排文件索引结构

1)     倒排文件索引结构和生成算法
文章1的内容为:Tom lives in Guangzhou,I live in Guangzhou too.

文章2的内容为:He once lived in Shanghai.

由于Lucene是基于关键词来索引和查询,首先要从文章中找出关键词,其中一些不代表概念的词、标点符号、可以过滤掉。比如英语中的 is、the、no等中文中的 “的”、 “地”、 “得”、 “着”、 “了”、“过”等。live,lives,lived等这类词都还原为live。大小写都统一也就是不区分大写小。由Lucene Analyzer来处理,经过处理后

文章1的所有关键词为:[tom] [live] [guangzhou] [i] [live] [guangzhou]

文章2的所有关键词为:[he] [live] [shanghai]

这样有了关键词就可以建立倒排索引了,上面的关系是文章号对关键字,倒过来就是关键字对文章号即:

关键词        文章号
guangzhou         1
he                2
i                 1
live               1,2    
shanghai           2
tom               1

通常仅知道关键词在哪些文章中出现还不够,我们还需要知道关键词在文章中出现次数和出现的位置,通常有两种位置:a)字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快);b)关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组(phase)查询快),lucene中记录的就是这种位置。加上“出现频率”和“出现位置”信息后,我们的索引结构变为:

关键词      文章号[出现频率]     出现位置
guangzhou    1[2]                3,6
he           2[1]                1
i            1[1]                 4
live          1[2],2[1]             2,5,2
shanghai      2[1]                 3
tom          1[1]                 1

以live为例,1[2],2[1] 说明文章1出现过2次,文章2出现过1次。在文章1中出现的位置分别是第2和第5的位置,文章2中出现的位置是第2的位置

以上就是lucene索引结构中最核心的部分。我们注意到关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二元搜索算法快速定位关键词

实现时 lucene将上面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。

下面我们可以通过对该索引的查询来解释一下为什么要建立索引。

假设要查询单词 “live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。而用普通的顺序匹配算法,不建索引,而是对所有文章的内容进行字符串匹配,这个过程将会相当缓慢,当文章数目很大时,时间往往是无法忍受的。

 

2)     创建索引
        对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。分别对这五个类进行介绍

        Document:描述文档,文档可以是html,txt等等,它由一个或者多个Field组成。可以把Document看成记录,Field看成字段

        Field:文档的属性。Lucene提供了四中不同类型的字段来表示,分别如下:

               Index.NO, Index. TOKENIZED, Index. UN_TOKENIZED,

Index. NO_NORMS

        Directory:这是一个抽象类,它代表Lucene索引的位置,目前有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置

        IndexWriter:是在索引过程中的核心组件,Lucene 用来创建索引的一个核心的类,作用是把一个个的 Document 对象加到索引中去

        Analyzer:在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工

                  作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。

                  针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后

                  的内容交给 IndexWriter 来建立索引

       下面是一个建立索引的例子

                                public void CreateIndexTest() throws IOException, ClassNotFoundException, SQLException{

                                               //首先建立一个存放索引的目录

                                               FSDirectory fsd = FSDirectory.getDirectory("C://indexDir");

                                               //中文解析器

                                               MIK_CAnalyzer analyzer = new MIK_CAnalyzer();

 

                                               //运用IndexWriter把一个个的 Document 对象加到索引中来

                                               //fsd:存放索引的目录,analyzer:分析器, true:是否新建索引

                                               IndexWriter iWriter = new IndexWriter (fsd, analyzer, true);

 

                                               //从数据库中查询出数据,并加到文档中

                                               String query = "  select * from tb_pu_user t where rownum < 2000 ";

                                               Connection conn = new DBUtil().getConnection();

                                               Statement st = conn.createStatement();

                                               ResultSet rs = st.executeQuery(query);

 

                                               while(rs.next()){

                                                        Document document = new Document();

                                                        document.add(new Field("N_ORDERID",String.valueOf(rs.getLong("N_ORDERID")),Field.Store.YES,Field.Index.TOKENIZED));

                                                        document.add(new Field("VCODE",rs.getString("V_CODE"),Field.Store.YES,Field.Index.TOKENIZED));

                                                        document.add(new Field("N_MONEY",String.valueOf(rs.getLong("N_MONEY")),Field.Store.YES,Field.Index.NO));

 

                                                        iWriter.addDocument(document);

                                               }

 

                                               iWriter.optimize();

                                               iWriter.close();

                                        }

          上面的例子是为数据库中tb_pu_user表建立索引

3)     搜索索引
建立了索引后,就可以搜索了,搜索时用到的分词器要跟建索引时用到的分词器一样,否则就不能够正确搜索,在搜索时候用到五个基本的类分别是:

Query:是一个抽象类,它的实现类有TermQuery, BooleanQuery, PrefixQuery这个类的目的就是把用户输入的查询词封装成Lucence能够识别的Query类

Term:是搜索的基本单位,一个Term对象有两个String类型的域组成。生成一个Term对象可以有如下一条语句来完成:Term term = new Term("fieldName",”queryWord”); 其中第一个参数代表了要在文档的哪一个Field上进行查找,第二个参数代表了要查询的关键词

TermQuery:是抽象类Query的一个子类,它同时也是Lucene支持的最为基本的一个查询类。生成一个TermQuery对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参数,那就是一个Term对象

IndexSearcher:是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个IndexSearcher的实例在一个索引上进行操作

Hits:是用来保存搜索的结果的,相当于数据库查询中的ResultSet数据结果集

QueryParser:查询解析器,推荐使用TermQuery来解决

下面是在一个已经建立好的索引中搜索的例子

    public void serach() throws CorruptIndexException, IOException, ParseException{

         //在建立好索引的目录里面搜索

         IndexSearcher searcher = new IndexSearcher("C://indexDir");

         QueryParser queryParser = new QueryParser("VCODE", new MIK_CAnalyzer());

 

         Query query = queryParser.parse(“code888”);

         Hits hits = searcher.search(query);

 

        for(int i=0; i<hits.length(); i++ ){

            Document d = hits.doc(i);

            String VCODE = d.get("VCODE");

        }

    }

在上面的搜索中如果有要搜索的东西发生了变化,这个时候必须要重新生成索引

4)     提高索引的性能
在索引的过程中瓶颈是什么呢?不是查询而是不断生成索引时候往磁盘上写索引文件的过程。为解决这个问题Lucene特别在内存中持有一块缓冲区,IndexWriter 类中有三个参数来调整内存缓冲区的大小和往磁盘写索引文件的频率

1)        合并因子(mergeFactor)

决定一个索引块能够可以存放多少文档以及把磁盘上的索引块合并成大索引块的频率,这个参数在硬件满足的话可以尽量大些

2)        最小合并文档数

这个参数也能够影响索引性能,意思为内存中的文档数至少达到多少才能将它们写回磁盘,如果硬件满足的话可以尽量大些,因为在访问内存比访问磁盘要快很多

3)        最大合并文档数

这个参数决定了一个索引块中的最大的文档数。它的默认值是 Integer.MAX_VALUE,将这个参数设置为比较大的值可以提高索引效率和检索速度,由于该参数的默认值是整型的最大值,所以我们一般不需要改动这个参数

下面是一个性能测试对照表


5)     Lucene 中文分词的解决方案
在Lucene包中有ChineseAnalyzer,StandardAnalyzer两个分词类,都可以解决中文的分词,但采用单字分词法,所以就容易产生歧义,冗余过多,相应得效率也会大大降低,现在可以采用IKAnalyzer来实现中文分词,其基于lucene2.0版本API开发,实现了以词典分词为基础的 正反向全切分以及正反向最大匹配切分两种算法,是Lucene Analyzer接口的实现,如果要增加新的搜索词语只需要在wordbase.dic文件中添加即可

用IKAnalyzer分词“湖南省林业厅组织专家调查出,石燕湖有异常运输迹象。随后石燕湖老板承认了此事”的效果如下:

0~3 :湖南省

2~6 :省林业厅

6~8 :组织

8~10 :专家

10~12 :调查

11~13 :查出

14~16 :石燕

16~17 :湖

17~19 :有异

18~20 :异常

20~22 :运输

22~24 :迹象

25~27 :随后

27~29 :石燕

29~31 :湖老

30~32 :老板

32~34 :承认

33~35 :认了

35~37 :此事

从上面看出,分词后的词语足够满足我们搜索了

6)     Lucene 分页显示的解决方案
搜索出来的结果是一个Hits集,可以把Hits结果集生成一个List对象,这样用Pager-taglib等分页标签即可

 

3.3 nutch简介
       Nutch是Lucene的一个实现,是一个应用程序,主要是用来从网上抓取网页,并为这些网页建立索引,然后通过searcher查询出结果并返回搜索结果

Nutch主要分为两个部分:

爬虫crawler:主要用于从网络上抓取网页并为这些网页建立索引。

查询searcher:主要利用这些索引检索用户的查找关键词来产生查找结果。两者之间的接口是索引,所以除去索引部分,两者之间的耦合度很低。

在这里我们主要用到通过nutch来抓取网页和建立索引,所以我们主要介绍Cygwin

来运行nutch,并把nutch中建立索引时的分词法修改为IKAnalyzer分词法

1)Cygwin介绍
          由于nutch是在unix中运行的,而Cygwin是一个用于在Windows上模拟Linux环境的软件。它可以作为那些虚拟机软件的一个部分替代品。所以在Windows环境下运行nutch就可以安装一个Cygwin来执行nutch程序,

Cygwin具体从那里下载,怎么安装、怎么用可以从网上找到许多的资料,这里就不用仔细的介绍了

2)修改nutch的分词方法
      Nutch采用的是NutchDocumentAnalyzer.java分词法,也是采用单字分词原理,因为单字分词容易产生歧义,冗余过多,相应得效率也会大大降低所以我们要修改分词方法采用词典二分法即采用IKAnalyzer来实现中文分词,在程序中只要修改NutchDocumentAnalyzer.

Java即可,修改方法如下

  public TokenStream tokenStream(String fieldName, Reader reader) {

    Analyzer analyzer;

    if ("anchor".equals(fieldName))

      analyzer = ANCHOR_ANALYZER;

    else

      analyzer = CONTENT_ANALYZER;

 

    //update huangxc by 2008-03-25 主要是修改目的:分词方法采用二分法替换原来的单字分词法

    analyzer = new MIK_CAnalyzer();

    return analyzer.tokenStream(fieldName, reader);

  

    //原来的,现在把它屏蔽掉

    //return analyzer.tokenStream(fieldName, reader);

  }

  

   这样建立索引是用IKAnalyzer分词,在搜索索引的时候必须还是用IKAnalyzer做分词,查询出来的结果才正确

 

     

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/huangxc/archive/2008/03/19/2197962.aspx