lucence

本文主要通过三个部分来分别介绍lucene:认识lucene、通过lucene建立索引和对已经建立的索引进行搜索,文章内容大部分参考”IBM developerWorks 中国”,我想这篇文章对于要学习或已经接触过Lucene的朋友是一个比较好的资料。
第一部分Lucene 简介
    Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene 是一个开源、高度可扩展的搜索引擎库,可以从 Apache Software Foundation 获取。您可以将 Lucene 用于商业和开源应用程序。Lucene 强大的 API 主要关注文本索引和搜索。它可以用于为各种应用程序构建搜索功能,目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能、电子邮件客户端、邮件列表、Web 搜索、数据库搜索等等。Wikipedia、TheServerSide、jGuru 和 LinkedIn 等网站都使用了 Lucene。
    Lucene 能够为文本类型的数据建立索引,所以你只要能把你要索引的数据格式转化的文本的,Lucene 就能对你的文档进行索引和搜索。比如你要对一些 HTML 文档,PDF 文档进行索引的话你就首先需要把 HTML 文档和 PDF 文档转化成文本格式的,然后将转化后的内容交给 Lucene 进行索引,然后把创建好的索引文件保存到磁盘或者内存中,最后根据用户输入的查询条件在索引文件上进行查询。不指定要索引的文档的格式也使 Lucene 能够几乎适用于所有的搜索应用程序。到 2009 年 7 月 30 日止,用于 Java™ 编程语言的最新版 Lucene 为 V2.4.1。

Lucene 功能众多:
• 拥有强大、准确、有效的搜索算法。
• 计算每个文档匹配给定查询的分数,并根据分数返回最相关的文档。
• 支持许多强大的查询类型,比如 PhraseQuery、WildcardQuery、RangeQuery、FuzzyQuery、BooleanQuery 等。
• 支持解析人们输入的丰富查询表达式。
• 允许用户使用定制排序、过滤和查询表达式解析扩展搜索行为。
• 使用基于文件的锁定机制保护并发索引修改。
• 允许同时搜索和编制索引。
如图 1 所示,使用 Lucene 构建功能全面的搜索应用程序主要涉及编制数据索引、搜索数据和显示搜索结果几个方面。


索引和搜索
    索引是现代搜索引擎的核心,建立索引的过程就是把源数据处理成非常方便查询的索引文件的过程。为什么索引这么重要呢,试想你现在要在大量的文档中搜索含有某 个关键词的文档,那么如果不建立索引的话你就需要把这些文档顺序的读入内存,然后检查这个文章中是不是含有要查找的关键词,这样的话就会耗费非常多的时 间,想想搜索引擎可是在毫秒级的时间内查找出要搜索的结果的。这就是由于建立了索引的原因,你可以把索引想象成这样一种数据结构,他能够使你快速的随机访 问存储在索引中的关键词,进而找到该关键词所关联的文档。Lucene 采用的是一种称为反向索引(inverted index)的机制。反向索引就是说我们维护了一个词/短语表,对于这个表中的每个词/短语,都有一个链表描述了有哪些文档包含了这个词/短语。这样在用 户输入查询条件的时候,就能非常快的得到搜索结果。
    对文档建立好索引后,就可以在这些索引上面进行搜索了。搜索引擎首先会对搜索的关键词进行解析,然后再在建立好的索引上面进行查找,最终返回和用户输入的关键词相关联的文档。
Lucene 软件包分析
    Lucene 软件包的发布形式是一个 JAR 文件,下面我们分析一下这个 JAR 文件里面的主要的 JAVA 包,做一个初步的了解。
Package: org.apache.lucene.document
    这个包提供了一些为封装要索引的文档所需要的类,比如 Document, Field。这样,每一个文档最终被封装成了一个 Document 对象。
Package: org.apache.lucene.analysis
    这个包主要功能是对文档进行分词,因为文档在建立索引之前必须要进行分词,所以这个包的作用可以看成是为建立索引做准备工作。
Package: org.apache.lucene.index
    这个包提供了一些类来协助创建索引以及对创建好的索引进行更新。这里面有两个基础的类:IndexWriter 和 IndexReader,其中 IndexWriter 是用来创建索引并添加文档到索引中的,IndexReader 是用来删除索引中的文档的。
Package: org.apache.lucene.search
    这个包提供了对在建立好的索引上进行搜索所需要的类。比如 IndexSearcher 和 Hits, IndexSearcher 定义了在指定的索引上进行搜索的方法,Hits 用来保存搜索得到的结果。

第二部分 建立索引
    为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介绍一下这五个类的用途:
Document
Document 是用来描述文档的,这里的文档可以指一个 HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。
Field
Field 表示搜索中查询或检索的数据片。Field 类封装一个字段名称及其值。Lucene 提供了一些选项来指定字段是否需要编制索引或分析,以及值是否需要存储。这些选项可以在创建字段实例时传递。下表展示了 Field 元数据选项的详细信息。
Field 元数据选项的详细信息
选项 描述
Field.Store.Yes 用于存储字段值。适用于显示搜索结果的字段 — 例如,文件路径和 URL。
Field.Store.No 没有存储字段值 — 例如,电子邮件消息正文。
Field.Index.No 适用于未搜索的字段 — 仅用于存储字段,比如文件路径。
Field.Index.ANALYZED 用于字段索引和分析 — 例如,电子邮件消息正文和标题。
Field.Index.NOT_ANALYZED 用于编制索引但不分析的字段。它在整体中保留字段的原值 — 例如,日期和个人名称。

Analyzer
    分析是将文本数据转换为搜索基本单位(称为项(term))的过程。在分析过程中,文本数据将经历多项操作:提取单词、移除通用单词、忽略标点符号、将单词变为词根形式、将单词变成小写等等。分析过程发生在编制索引和查询解析之前。分析将文本数据转换为标记,这些标记将作为项添加到 Lucene 索引中。Lucene 有多种内置分析程序,比如 SimpleAnalyzer、StandardAnalyzer、StopAnalyzer、SnowballAnalyzer 等。它们在标记文本和应用过滤器的方式上有所区别。因为分析在编制索引之前移除单词,它减少了索引的大小,但是不利用精确的查询过程。您可以使用 Lucene 提供的基本构建块创建定制分析程序,以自己的方式控制分析过程。表 1 展示了一些内置分析程序及其处理数据的方式。
Lucene 的内置分析程序
分析程序 对文本数据的操作
WhitespaceAnalyzer 分解空白处的标记
SimpleAnalyzer 分解非字母字符的文本,并将文本转为小写形式
StopAnalyzer 移除虚字(stop word)—— 对检索无用的字,并将文本转为小写形式
StandardAnalyzer 根据一种复杂语法(识别电子邮件地址、缩写、中文、日文、韩文字符、字母数字等等)标记文本
将文本转为小写形式
移除虚字

IndexWriter
    创建或维护索引的类。它的构造函数接收布尔值,确定是否创建新索引,或者打开现有索引。它提供在索引中添加、删除和更新文档的方法。
Directory
    这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现:
1. FSDirectory,它表示一个存储在文件系统中的索引的位置,该类对于大型索引非常有用。
2. RAMDirectory,它表示一个存储在内存当中的索引的位置,该类适用于较小的索引,可以完整加载到内存中,在应用程序终止之后销毁。由于索引保存在内存中,所以速度相对较快。
Java代码
/** 
     * 创建索引 
     */ 
    private static void createIndex(String dir, Analyzer analyzer,   
            List<Document> docList) throws CorruptIndexException,  
            LockObtainFailedException, IOException {  
        IndexWriter writer = new IndexWriter(dir, analyzer,  
                true, IndexWriter.MaxFieldLength.UNLIMITED);  
          
        for(Document doc : docList){  
            writer.addDocument(doc); //添加索引  
        }  
          
        writer.optimize();  //优化索引  
        writer.close();     //索引创建完需要关闭  
    } 

/**
* 创建索引
*/
private static void createIndex(String dir, Analyzer analyzer,
List<Document> docList) throws CorruptIndexException,
LockObtainFailedException, IOException {
IndexWriter writer = new IndexWriter(dir, analyzer,
true, IndexWriter.MaxFieldLength.UNLIMITED);

for(Document doc : docList){
writer.addDocument(doc); //添加索引
}

writer.optimize(); //优化索引
writer.close(); //索引创建完需要关闭
}


第二部分 搜索
    利用Lucene进行搜索就像建立索引一样也是非常方便的。在上一部分中,我们已经为一个目录下的文本文档建立好了索引,现在我们就要在这个索引上进行搜索以找到包含某个关键词或短语的文档。Lucene提供了几个基础的类来完成这个过程,它们分别是呢IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍这几个类的功能。
Searcher
Searcher 是一个抽象基类,包含各种超负荷搜索方法。IndexSearcher 是一个常用的子类,允许在给定的目录中存储搜索索引。Search 方法返回一个根据计算分数排序的文档集合。Lucene 为每个匹配给定查询的文档计算分数。IndexSearcher 是线程安全的;一个实例可以供多个线程并发使用。

Term
    Term 是搜索的基本单位。它由两部分组成:单词文本和出现该文本的字段的名称。Term 对象也涉及索引编制,但是可以在 Lucene 内部创建。

Query 和子类
    Query 是一个用于查询的抽象基类。搜索指定单词或词组涉及到在项中包装它们,将项添加到查询对象,将查询对象传递到 IndexSearcher 的搜索方法。Lucene 包含各种类型的具体查询实现,比如TermQuery、BooleanQuery、PhraseQuery、PrefixQuery、RangeQuery、MultiTermQuery、FilteredQuery、SpanQuery 等。
TermQuery
    搜索索引最基本的查询类型。可以使用单个项构建 TermQuery。项值应该区分大小写,但也并非全是如此。注意,传递的搜索项应该与文档分析得到的项一致,因为分析程序在构建索引之前对原文本执行许多操作。 例如,考虑电子邮件标题 “Job openings for Java Professionals at Bangalore”。假设您使用 StandardAnalyzer 编制索引。现在如果我们使用 TermQuery 搜索 “Java”,它不会返回任何内容,因为本文本应该已经规范化,并通过 StandardAnalyzer 转成小写。如果搜索小写单词 “java”,它将返回所有标题字段中包含该单词的邮件。
Java代码
//搜索标题,不适用于中文,因为使用StandardAnalyzer分析英文字母已经被转换成小写,如果含大写字母将找不到结果  
    Searcher indexSearcher = new IndexSearcher(dir);  
    Term term = new Term("title", search);  
    Query termQuery = new TermQuery(term);     
    TopDocs topDocs = indexSearcher.search(termQuery,10); 

//搜索标题,不适用于中文,因为使用StandardAnalyzer分析英文字母已经被转换成小写,如果含大写字母将找不到结果
Searcher indexSearcher = new IndexSearcher(dir);
Term term = new Term("title", search);
Query termQuery = new TermQuery(term);
TopDocs topDocs = indexSearcher.search(termQuery,10);


RangeQuery
    您可以使用 RangeQuery 在某个范围内搜索。索引中的所有项都以字典顺序排列。Lucene 的 RangeQuery 允许用户在某个范围内搜索项。该范围可以使用起始项和最终项(包含两端或不包含两端均可)指定。
Java代码
Term begin = new Term("date", beginDate);  
    Term end = new Term("date", endDate);  
    Query rangequery = new RangeQuery(begin, end, inclusive);   //true:包含begin和end日期 

Term begin = new Term("date", beginDate);
Term end = new Term("date", endDate);
Query rangequery = new RangeQuery(begin, end, inclusive); //true:包含begin和end日期

PrefixQuery
    您可以使用 PrefixQuery 通过前缀单词进行搜索,该方法用于构建一个查询,该查询查找包含以指定单词前缀开始的词汇的文档。
Java代码
Term term = new Term("title",prefix);  
    //搜索基本单位(称为项(term))的前缀  
Query prefixQuery = new PrefixQuery(term); 

Term term = new Term("title",prefix);
//搜索基本单位(称为项(term))的前缀
Query prefixQuery = new PrefixQuery(term);

BooleanQuery
    您可以使用 BooleanQuery 组合任何数量的查询对象,构建强大的查询。它使用 query 和一个关联查询的子句,指示查询是应该发生、必须发生还是不得发生。在 BooleanQuery 中,子句的最大数量默认限制为 1,024。您可以调用 setMaxClauseCount 方法设置最大子句数。
Java代码
//指定StandardAnalyzer分析器,支持中文  
        QueryParser queryParser1 = new QueryParser("title",getStandardAnalyzer());  
        Query query1 = queryParser1.parse(search);  
        QueryParser queryParser2 = new QueryParser("content",getStandardAnalyzer());  
        Query query2 = queryParser2.parse(search);  
          
        //不支持中文搜索  
//      Query query1 = new TermQuery(new Term("title",search));   
//      Query query2 = new TermQuery(new Term("content",search));  
        BooleanQuery query = new BooleanQuery();  
        query.add(query1,BooleanClause.Occur.MUST);  
    query.add(query2,BooleanClause.Occur.MUST); 

//指定StandardAnalyzer分析器,支持中文
QueryParser queryParser1 = new QueryParser("title",getStandardAnalyzer());
Query query1 = queryParser1.parse(search);
QueryParser queryParser2 = new QueryParser("content",getStandardAnalyzer());
Query query2 = queryParser2.parse(search);

//不支持中文搜索
// Query query1 = new TermQuery(new Term("title",search));
// Query query2 = new TermQuery(new Term("content",search));
BooleanQuery query = new BooleanQuery();
query.add(query1,BooleanClause.Occur.MUST);
query.add(query2,BooleanClause.Occur.MUST);


PhraseQuery
    您可以使用 PhraseQuery 进行短语搜索。PhraseQuery 匹配包含特定单词序列的文档。PhraseQuery 使用索引中存储的项的位置信息。考虑匹配的项之间的距离称为 slop。默认情况下,slop 的值为零,这可以通过调用 setSlop 方法进行设置。PhraseQuery 还支持多个项短语。
Java代码
PhraseQuery query = new PhraseQuery();  
        query.setSlop(1);  
        //不支持中文  
        query.add(new Term("title","new"));  
    query.add(new Term("title","china")); 

PhraseQuery query = new PhraseQuery();
query.setSlop(1);
//不支持中文
query.add(new Term("title","new"));
query.add(new Term("title","china"));

WildcardQuery
    WildcardQuery 实现通配符搜索查询,这允许您搜索 arch*(可以查找包含 architect、architecture 等)之类的单词。使用两个标准通配符:
• * 表示零个以上
• ? 表示一个以上
    如果使用以通配符查询开始的模式进行搜索,则可能会引起性能的降低,因为这需要查询索引中的所有项以查找匹配文档。

FuzzyQuery
    您可以使用 FuzzyQuery 搜索类似项,该类匹配类似于指定单词的单词。类似度测量基于 Levenshtein(编辑距离)算法进行。在列表 9 中,FuzzyQuery 用于查找与拼错的单词 “admnistrtor” 最接近的项,尽管这个错误单词没有索引。

QueryParser
    QueryParser 对于解析人工输入的查询字符非常有用。您可以使用它将用户输入的查询表达式解析为 Lucene 查询对象,这些对象可以传递到 IndexSearcher 的搜索方法。它可以解析丰富的查询表达式。 QueryParser 内部将人们输入的查询字符串转换为一个具体的查询子类。您需要使用反斜杠(\)将 *、? 等特殊字符进行转义。您可以使用运算符 AND、OR 和 NOT 构建文本布尔值查询。

布尔操作符
    大多数的搜索引擎都会提供布尔操作符让用户可以组合查询,典型的布尔操作符有 AND, OR, NOT。Lucene 支持 5 种布尔操作符,分别是 AND, OR, NOT, 加(+), 减(-)。接下来我会讲述每个操作符的用法。
• OR: 如果你要搜索含有字符 A 或者 B 的文档,那么就需要使用 OR 操作符。需要记住的是,如果你只是简单的用空格将两个关键词分割开,其实在搜索的时候搜索引擎会自动在两个关键词之间加上 OR 操作符。例如,“Java OR Lucene” 和 “Java Lucene” 都是搜索含有 Java 或者含有 Lucene 的文档。
• AND: 如果你需要搜索包含一个以上关键词的文档,那么就需要使用 AND 操作符。例如,“Java AND Lucene” 返回所有既包含 Java 又包含 Lucene 的文档。
• NOT: Not 操作符使得包含紧跟在 NOT 后面的关键词的文档不会被返回。例如,如果你想搜索所有含有 Java 但不含有 Lucene 的文档,你可以使用查询语句 “Java NOT Lucene”。但是你不能只对一个搜索词使用这个操作符,比如,查询语句 “NOT Java” 不会返回任何结果。
• 加号(+): 这个操作符的作用和 AND 差不多,但它只对紧跟着它的一个搜索词起作用。例如,如果你想搜索一定包含 Java,但不一定包含 Lucene 的文档,就可以使用查询语句“+Java Lucene”。
• 减号(-): 这个操作符的功能和 NOT 一样,查询语句 “Java -Lucene” 返回所有包含 Java 但不包含 Lucene 的文档。

你可能感兴趣的:(apache,算法,搜索引擎,Lucene,全文检索)