全文检索引擎lucene

来源:互联网 发布:mac oracle客户端 编辑:程序博客网 时间:2024/05/02 04:24

               

全文检索引擎的开放源代码Lucene

 2010-10-25

Lucene是一个开放源代码、高性能的Java全文检索工具包。

1全文检索引擎lucene

11 Lucene简介

Luceneapache软件基金会jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,它为数据访问和管理提供了简单的函数调用接口,可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。LuceneAPl接口设计得比较通用,输入输出结构都很像数据库的表、记录和字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射至lJLucene的存储结构或接口中。总体上看,可以先把Lucene当成一个支持全文索引的数据库系统。

 

Lucene 的原作者是Doug Cutting,他是一位资深全文索引/检索专家,曾经是V-Twin 搜索引擎[6]的主要

开发者,后在Excite[7]担任高级系统架构设计师,目前从事于一些Internet 底层架构的研究。早先发布

在作者自己的http://www.lucene.com/,后来发布在SourceForge[8]2001 年年底成为apache 软件基金

jakarta 的一个子项目:http://jakarta.apache.org/lucene/

 

Lucene 目前是 Apache Jakarta 家族中的一个开源项目,下载http://lucene.apache.org/

 

 

12 Lucene系统结构

Lucene作为~个优秀的全文检索引擎,其系统结构运用了大量的面向对象的设计思想。首先是定义了一个与平台无关的索引文件格式,其次通过抽象将系统的核心组成部分设

计为抽象类,具体的平台部分设计为抽象类,此外与具体平台相关的部分比如文件存储也封装为类,经过层层的面向对象编程的处理,最终达成了一个低耦合、高效率、容易二次开发

等的检索引擎系统。Lucene系统其结构如图1所示。可以从图1清楚地看到,Lucene系统是由基础结构封装、索引核心、对外接口3大部分组成。其中直接操作索引文件的索引核心又是系统的重点,索引的最后结果就是产生许多的索引文件,这些索引文件构成索引库。

Lucene系统将所有源码分为了7个模块(Java语言中以包即package来表示),各个模块()完成特定的功能。在Lucene系统的这7个包中,核心类包主要有3(orgapache1uceneanalysisorgapache1uceneindexorgapache1ucenesearch)

(1)orgapache1uceneanalysis该模块主要用于切分词。切分词的工作由Analyzer的扩展类来实现,Lucene自带了StandardAnalyzer类,我们可以参照该类的实现写出自己的切分词分析器类,如中文分析器等。

(2)orgapache1uceneindex该模块主要提供库的读写接口。通过该包可以创建库、添加删除记录及读取记录等。

(3)orgapachehcenesearch该模块主要提供了检索接口。通过该包,我们可以输入条件,得到查询结果集,与orgapache1ucenequeryParser包配合还可以自定义查询规则,像google一样支持查询条件问的与、或、非、属于等复合查询。

 

13 Lucene程序运行机制

Lucene系统,其功能强大,实现复杂,但从根本上说,主要包括两个主要功能:一是建立索引库,也就是将待索引的纯文本内容经切分词后的索引入库;二是检索索引库,即根据查询

条件从索引库中找出符合条件的文档。

131建立索引逻辑库

建立索引逻辑库按先后顺序,其查询逻辑如下:

首先,建库者定义入库文档的结构,比如需要把网站内容加载到全文检索库,让用户通过“站内检索”搜索到相关的网页内容。入库文档结构与关系型数据库中表的行结构类似,

每个入库的文档由多个域构成,假设这里需要入库的网站内容包括如下项目:文章标题、作者、发布时间、原文链接、正文内容(一般作为网页快照),那么每个网页的这些项目可以作为文档的域,共有5个域。

  其次,对于文档中需要切分词的域,系统使用语言解析器(hceneanalysis)对其切分词,形成一个个TokenTokenLucene内部所使用的概念,是对传统文字中的词的概念的抽象,

也是Lucene在建立索引时直接处理的最小单位;简单的讲Token就是一个词和所在域值的组合。

  最后,切分后的Token通过索引器(1uceneindex)的处理,最终添加(插入)到索引库中,存储器(1ucenestore)负责数据存储管理,主要包括一些底层的IO操作。

132检索索引逻辑库

建立检索索引逻辑库按先后顺序,查询逻辑如下:

首先,输入查询条件,比如用户希望查询到含有词“china

和“panda”但不含词“cat”的记录,那么输入条件为“china+panda-cat”;查询条件传入搜索器(1ucenesearch),搜索器里有一个查询解析器(incenequeryParser),搜索器调用这个查询解析器来解析查询条件。

其次,查询条件“china+panda-cat"被传到查询解析器中,解析器将对“china+panda-cat"进行分析,首先分析器解析字符串的连接符,即加号和减号,然后调用语言解析器(1uceneanalysis)对每个词进行切词,一般英文将按空格来切词,最后得到的查询条件表示为:“china"ANDpanda"AND NOTcat”。

最后,查询器根据这个条件去检索事先已建立好的索引库,得到查询结果,并返回结果集lucenesearchHitsHits类似于JDBC中的ResultSet

2基于Lucene的搜索引擎设计与实现

下面介绍一个采用Lucene全文检索技术实现的一个校园网站内搜索引擎,通过介绍该搜索引擎的设计与实现来展示如何运用Lucene

21系统设计要求

一个校园网站的搜索引擎应该满足下列要求:①以校园网为搜索目标,用户可以通过该系统检索校园网站上所有静态网页的内容和大多数动态网页的内容,提供基于Web的查

询接口;②具有较高的查询准确率和较快的响应速度。

22系统设计与实现

基于Lucene全文检索搜索引擎的系统结构图如图2所示。

上述系统分两大部分:创建维护索引与检索索引,本文将主要介绍与Lucene相关的设计与实现,由于Lucene不是一个完整的全文检索引擎,而是一个全文检索引擎的工具

包,所以需要利用工具包提供的类,并对其扩展来实现具体的应用。

221建立索引库

建立索引库就是往索引库添加一条条索引记录,Lucene为添加一条索引记录提供了接口,添加索引。

主要用到了“写索引器”、“文档”、“域”这3个类。

要建立索引,首先要构造一个Document文档对象,确定Document的各个域,这类似于关系型数据库中表结构的建立,Document相当于表中的一个记录行,域相当于一行中的列,在

Lucene中针对不同域的属性和数据输出的需求,对域还可以选择不同的索引/存储字段规则,在本系统中,网页标题、内容、URL和建立日期作为Document的域。IndexWriter负责接收新加入的文档,并写入索引库中。在创建“写索引器”IndexWriter时需要指定所使用的语言

分析器,Lucene13版开始支持中文,它通过个包orgapache1uceneanalysisstandard来支持中文,它对汉字采用单字切分,这种切分方法实现简单,但检索准确率不高,对于本系统来讲足够了,当然也可以通过扩展Analyzer类来开发自己的语言所在的目录,IndexSearcher有一个search方法执行索引的检索,这个方法接受Query作为参数,返回HitsHists是一系列排好序的查询结果的集合,集合的元素是Document。通过Documentget方法可以得到与这个文档对应网页的信息,比如:网页标题,URL等。

这些信息经过处理显示给用户。

下面是索引检索的示例代码片断:

……

Searcher searcher=new IndexSearcher(E:/index)

Analyzer analyzer=new SimpleAnalyzer0

Query query=QueryParserparse(1ine,”Fulllndex",analyzer)

Hits hits=searchersearch(query)

for(int i-Oi<hits1ength0i++){

Document doe=hitsdoe(i)

String path=doeget(Ud)

String title=doeget(Title)

String content--doeget(Content)

String pubTime=docget(PubTime)

……

23系统特点

本系统在开放源码工具包Lucene的基础上,根据系统特点和要求,按照Lucene的框架规范,扩展Lucene的功能,将Lucene很好地嵌入到搜索引擎中。系统具有Lucene的技术特点,全文检索效率高,查全率和准确率都达到了设计的要求,系统运行稳定,查询精确,支持跨平台,多用户。

 

 

 

Lucene 作为一个全文检索引擎,其具有如下突出的优点

1)索引文件格式独立于应用平台。Lucene 定义了一套以8 位字节为基础的索引文件格式,使得兼容系

统或者不同平台的应用能够共享建立的索引文件。

2)在传统全文检索引擎的倒排索引的基础上,实现了分块索引,能够针对新的文件建立小文件索引,提

升索引速度。然后通过与原有索引的合并,达到优化的目的。

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

4)设计了独立于语言和文件格式的文本分析接口,索引器通过接受Token 流完成索引文件的创立,用户

扩展新的语言和文件格式,只需要实现文本分析的接口。

5)已经默认实现了一套强大的查询引擎,用户无需自己编写代码即使系统可获得强大的查询能力,Luc

ene 的查询实现中默认实现了布尔操作、模糊查询(Fuzzy Search[11])、分组查询等等。

 

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相互转化

总的来说,这两个基础类包中含有的类都比较简单,通过阅读源代码,可以很容易的理解,因此这里不作

过多的展开。

 

 

Lucence API 介绍

org.apache.Lucene.search 搜索入口

org.apache.Lucene.index  索引入口

org.apache.Lucene.analysis语言分析器

org.apache.Lucene.queryParser查询分析器

org.apache.Lucene.document存储结构

org.apache.Lucene.store  底层IO/存储结构

org.apache.Lucene.util一些公用的数据结构

 

 

全文检索常见问题

   文档域的存储设置

   分词分析器个性化实现

   文档检索高亮显示查询过程

   查询分页、排序

   索引建立过程异常处理

   部分搜索结果搜索不到

索引内容的删除、更新

中文分词处理

文章相关内容计算法

分布式全文检索

不能及时更新索引时与数据库内容一致方法

高级查询:通配符、模糊、间距查询、范围查询、权重查询、组合条件、转义字符

   英文检索:单词缩为词根形式 steamming lemmatization 两个过程需要建立对应的分词器

   Linux Window 平台差异

   Nutch

   不同版本的Lucene之间API有较大差异

   是否要转向Sphinx

 

 

 

 

简单实例(一)

说明一下,这一篇文章的用到的lucene,是用2.0版本的,主要在查询的时候2.0版本的lucene与以前的版本有了一些区别
其实这一些代码都是早几个月写的,自己很懒,所以到今天才写到自己的博客上,高深的文章自己写不了,只能记录下一些简单的记录与点滴,其中的代码算是自娱自乐的,希望高手不要把重构之类的砸下来... 

1、在windows系统下的的C盘,建一个名叫s的文件夹,在该文件夹里面随便建三个txt文件,随便起名啦,就叫"1.txt","2.txt""3.txt" 
其中1.txt的内容如下: 

Java代码 

1.  中华人民共和国  

2.  全国人民  

3.  2006  


"2.txt""3.txt"的内容也可以随便写几写,这里懒写,就复制一个和1.txt文件的内容一样吧 

2、下载lucene包,放在classpath路径中 
建立索引

Java代码 

1.  package lighter.javaeye.com;  

2.    

3.  import java.io.BufferedReader;  

4.  import java.io.File;  

5.  import java.io.FileInputStream;  

6.  import java.io.IOException;  

7.  import java.io.InputStreamReader;  

8.  import java.util.Date;  

9.    

10. import org.apache.lucene.analysis.Analyzer;  

11. import org.apache.lucene.analysis.standard.StandardAnalyzer;  

12. import org.apache.lucene.document.Document;  

13. import org.apache.lucene.document.Field;  

14. import org.apache.lucene.index.IndexWriter;  

15.   

16. /** 

17.  * author lighter date 2006-8-7 

18.  */  

19. public class TextFileIndexer {  

20.     public static void main(String[] args) throws Exception {  

21.         /* 指明要索引文件夹的位置,这里是C盘的S文件夹下 */  

22.         File fileDir = new File("c://s");  

23.   

24.         /* 这里放索引文件的位置 */  

25.         File indexDir = new File("c://index");  

26.         Analyzer luceneAnalyzer = new StandardAnalyzer();  

27.         IndexWriter indexWriter = new IndexWriter(indexDir, luceneAnalyzer,  

28.                 true);  

29.         File[] textFiles = fileDir.listFiles();  

30.         long startTime = new Date().getTime();  

31.           

32.         //增加document到索引去  

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

34.             if (textFiles[i].isFile()  

35.                     && textFiles[i].getName().endsWith(".txt")) {  

36.                 System.out.println("File " + textFiles[i].getCanonicalPath()  

37.                         + "正在被索引....");  

38.                 String temp = FileReaderAll(textFiles[i].getCanonicalPath(),  

39.                         "GBK");  

40.                 System.out.println(temp);  

41.                 Document document = new Document();  

42.                 Field FieldPath = new Field("path", textFiles[i].getPath(),  

43.                         Field.Store.YES, Field.Index.NO);  

44.                 Field FieldBody = new Field("body", temp, Field.Store.YES,  

45.                         Field.Index.TOKENIZED,  

46.                         Field.TermVector.WITH_POSITIONS_OFFSETS);  

47.                 document.add(FieldPath);  

48.                 document.add(FieldBody);  

49.                 indexWriter.addDocument(document);  

50.             }  

51.         }  

52.         //optimize()方法是对索引进行优化  

53.         indexWriter.optimize();  

54.         indexWriter.close();  

55.           

56.         //测试一下索引的时间  

57.         long endTime = new Date().getTime();  

58.         System.out  

59.                 .println("这花费了"  

60.                         + (endTime - startTime)  

61.                         + 毫秒来把文档增加到索引里面去!"  

62.                         + fileDir.getPath());  

63.     }  

64.   

65.     public static String FileReaderAll(String FileName, String charset)  

66.             throws IOException {  

67.         BufferedReader reader = new BufferedReader(new InputStreamReader(  

68.                 new FileInputStream(FileName), charset));  

69.         String line = new String();  

70.         String temp = new String();  

71.           

72.         while ((line = reader.readLine()) != null) {  

73.             temp += line;  

74.         }  

75.         reader.close();  

76.         return temp;  

77.     }  

78. }  



索引的结果: 

Java代码 

1.  File C:/s/1.txt正在被索引....  

2.  中华人民共和国全国人民2006  

3.  File C:/s/2.txt正在被索引....  

4.  中华人民共和国全国人民2006  

5.  File C:/s/3.txt正在被索引....  

6.  中华人民共和国全国人民2006  

7.  这花费了297 毫秒来把文档增加到索引里面去!c:/s  



3、建立了索引之后,查询啦.... 

Java代码 

1.  package lighter.javaeye.com;  

2.    

3.  import java.io.IOException;  

4.    

5.  import org.apache.lucene.analysis.Analyzer;  

6.  import org.apache.lucene.analysis.standard.StandardAnalyzer;  

7.  import org.apache.lucene.queryParser.ParseException;  

8.  import org.apache.lucene.queryParser.QueryParser;  

9.  import org.apache.lucene.search.Hits;  

10. import org.apache.lucene.search.IndexSearcher;  

11. import org.apache.lucene.search.Query;  

12.   

13. public class TestQuery {  

14.     public static void main(String[] args) throws IOException, ParseException {  

15.         Hits hits = null;  

16.         String queryString = "中华";  

17.         Query query = null;  

18.         IndexSearcher searcher = new IndexSearcher("c://index");  

19.   

20.         Analyzer analyzer = new StandardAnalyzer();  

21.         try {  

22.             QueryParser qp = new QueryParser("body", analyzer);  

23.             query = qp.parse(queryString);  

24.         } catch (ParseException e) {  

25.         }  

26.         if (searcher != null) {  

27.             hits = searcher.search(query);  

28.             if (hits.length() > 0) {  

29.                 System.out.println("找到:" + hits.length() + 个结果!");  

30.             }  

31.         }  

32.     }  

33.   

34. }  



其运行结果: 

引用

找到:3 个结果!

简单实例(二)

写文章的时候,感觉比较难写的就是标题,有时候不知道起什么名字好,反正这里写的都是关于lucene的一些简单的实例,就随便起啦

Lucene 其实很简单的,它最主要就是做两件事:建立索引和进行搜索 
来看一些在lucene中使用的术语,这里并不打算作详细的介绍,只是点一下而已----因为这一个世界有一种好东西,叫搜索。 

IndexWriter:lucene中最重要的的类之一,它主要是用来将文档加入索引,同时控制索引过程中的一些参数使用。 

Analyzer:分析器,主要用于分析搜索引擎遇到的各种文本。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。 

Directory:索引存放的位置;lucene提供了两种索引存放的位置,一种是磁盘,一种是内存。一般情况将索引放在磁盘上;相应地lucene提供了FSDirectoryRAMDirectory两个类。 

Document:文档;Document相当于一个要进行索引的单元,任何可以想要被索引的文件都必须转化为Document对象才能进行索引。 

Field:字段。 

IndexSearcher:lucene中最基本的检索工具,所有的检索都会用到IndexSearcher工具

Query:查询,lucene中支持模糊查询,语义查询,短语查询,组合查询等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些类。 

QueryParser: 是一个解析用户输入的工具,可以通过扫描用户输入的字符串,生成Query对象。 

Hits:在搜索完成之后,需要把搜索结果返回并显示给用户,只有这样才算是完成搜索的目的。在lucene中,搜索的结果的集合是用Hits类的实例来表示的。 

上面作了一大堆名词解释,下面就看几个简单的实例吧
1、简单的的StandardAnalyzer测试例子 

Java代码 

1.  package lighter.javaeye.com;  

2.    

3.  import java.io.IOException;  

4.  import java.io.StringReader;  

5.    

6.  import org.apache.lucene.analysis.Analyzer;  

7.  import org.apache.lucene.analysis.Token;  

8.  import org.apache.lucene.analysis.TokenStream;  

9.  import org.apache.lucene.analysis.standard.StandardAnalyzer;  

10.   

11. public class StandardAnalyzerTest   

12. {  

13.     //构造函数,  

14.     public StandardAnalyzerTest()  

15.     {  

16.     }  

17.     public static void main(String[] args)   

18.     {  

19.         //生成一个StandardAnalyzer对象  

20.         Analyzer aAnalyzer = new StandardAnalyzer();  

21.         //测试字符串  

22.         StringReader sr = new StringReader("lighter javaeye com is the are on");  

23.         //生成TokenStream对象  

24.         TokenStream ts = aAnalyzer.tokenStream("name", sr);   

25.         try {  

26.             int i=0;  

27.             Token t = ts.next();  

28.             while(t!=null)  

29.             {  

30.                 //辅助输出时显示行号  

31.                 i++;  

32.                 //输出处理后的字符  

33.                 System.out.println(""+i+":"+t.termText());  

34.                 //取得下一个字符  

35.                 t=ts.next();  

36.             }  

37.         } catch (IOException e) {  

38.             e.printStackTrace();  

39.         }  

40.     }  

41. }  


显示结果: 

引用

1:lighter 
2:javaeye 
3:com


提示一下: 
StandardAnalyzerlucene中内置的"标准分析器",可以做如下功能
1、对原有句子按照空格进行了分词 
2、所有的大写字母都可以能转换为小写的字母 
3、可以去掉一些没有用处的单词,例如"is","the","are"等单词,也删除了所有的标点 
查看一下结果与"new StringReader("lighter javaeyecom is the are on")"作一个比较就清楚明了。 
这里不对其API进行解释了,具体见lucene的官方文档。需要注意一点,这里的代码使用的是lucene2API,与1.43版有一些明显的差别。 

2、看另一个实例,简单地建立索引,进行搜索 

Java代码 

1.  package lighter.javaeye.com;  

2.  import org.apache.lucene.analysis.standard.StandardAnalyzer;  

3.  import org.apache.lucene.document.Document;  

4.  import org.apache.lucene.document.Field;  

5.  import org.apache.lucene.index.IndexWriter;  

6.  import org.apache.lucene.queryParser.QueryParser;  

7.  import org.apache.lucene.search.Hits;  

8.  import org.apache.lucene.search.IndexSearcher;  

9.  import org.apache.lucene.search.Query;  

10. import org.apache.lucene.store.FSDirectory;  

11.   

12. public class FSDirectoryTest {  

13.   

14.     //建立索引的路径  

15.     public static final String path = "c://index2";  

16.   

17.     public static void main(String[] args) throws Exception {  

18.         Document doc1 = new Document();  

19.         doc1.add( new Field("name""lighter javaeye com",Field.Store.YES,Field.Index.TOKENIZED));  

20.   

21.         Document doc2 = new Document();  

22.         doc2.add(new Field("name""lighter blog",Field.Store.YES,Field.Index.TOKENIZED));  

23.   

24.         IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);  

25.         writer.setMaxFieldLength(3);  

26.         writer.addDocument(doc1);  

27.         writer.setMaxFieldLength(3);  

28.         writer.addDocument(doc2);  

29.         writer.close();  

30.   

31.         IndexSearcher searcher = new IndexSearcher(path);  

32.         Hits hits = null;  

33.         Query query = null;  

34.         QueryParser qp = new QueryParser("name",new StandardAnalyzer());  

35.           

36.         query = qp.parse("lighter");  

37.         hits = searcher.search(query);  

38.         System.out.println("查找/"lighter/" " + hits.length() + "个结果");  

39.   

40.         query = qp.parse("javaeye");  

41.         hits = searcher.search(query);  

42.         System.out.println("查找/"javaeye/" " + hits.length() + "个结果");  

43.   

44.     }  

45.   

46. }  


运行结果: 

Java代码 

1.  查找"lighter" 2个结果  

2.  查找"javaeye" 1个结果  



 

原创粉丝点击