快捷搜索:

使用 Apache Lucene 搜索文本

简介

Lucene 是一个开源、高度可扩展的搜索引擎库,可以从 Apache Software Foundation 获取。您可以将 Lucene 用于商业和开源利用法度榜样。Lucene 强大年夜的 API 主要关注文本索引和搜索。它可以用于为各类利用法度榜样构建搜索功能,比如电子邮件客户端、邮件列表、Web 搜索、数据库搜索等等。Wikipedia、TheServerSide、jGuru 和 LinkedIn 等网站都应用了 Lucene。

Lucene 还为 Eclipse IDE、Nutch(闻名的开源 Web 搜索引擎)以及 IBM®、AOL 和 Hewlett-Packard 等公司供给搜索功能。Lucene 已经兼容许多其他编程说话,包括 Perl、Python、C++ 和 .NET。到 2009 年 7 月 30 日止,用于 Java™ 编程说话的最新版 Lucene 为 V2.4.1。

Lucene 功能浩繁:

拥有强大年夜、准确、有效的搜索算法。

谋略每个文档匹配给定查询的分数,并根据分数返回最相关的文档。

支持许多强大年夜的查询类型,比如 PhraseQuery、WildcardQuery、RangeQuery、FuzzyQuery、BooleanQuery 等。

支持解析人们输入的富厚查询表达式。

容许用户应用定制排序、过滤和查询表达式解析扩展搜索行径。

应用基于文件的锁定机制保护并发索引改动。

容许同时搜索和体例索引。

应用 Lucene 构建利用法度榜样

如图 1 所示,应用 Lucene 构建功能周全的搜索利用法度榜样主要涉及体例数据索引、搜索数据和显示搜索结果几个方面。

图 1. 应用 Lucene 构建利用法度榜样的步骤

本文从应用 Lucene V2.4.1 和 Java 技巧开拓的样例利用法度榜样中遴选了一些代码片段。示例利用法度榜样为存储在属性文件中一组电子邮件文档体例索引,并展示了若何应用 Lucene 的查询 API 搜索索引。该示例还让您认识基础的索引操作。

为数据体例索引

Lucene 容许您为任何文本款式的数据体例索引。Lucene 可以用于险些任何数据源以及从中提取的文本信息。您可以应用 Lucene 体例索引并搜索 HTML 文档、Microsoft® Word 文档、PDF 文件中存储的数据。体例数据索引的第一步是让数据变成一个简单的文本款式。您可以应用定制解析器和数据转换器实现这一点。

体例索引的历程

体例索引 是将文本数据转换为有利于快速搜索的款式。这类似于书籍后面的索引:为您指出主题在书中呈现的位置。

Lucene 将输入数据存储在名为逆序 索引的数据布局中, 该数据布局以索引文件集的形式存储在文件系统或内存中。大年夜部分 Web 搜索引擎都应用逆序索引。它容许用户履行快速关键字查询,查找匹配给定查询的文档。在将文本数据添加到索引前,由阐发法度榜样(应用阐发历程)进行处置惩罚。

阐发

阐发 是将文本数据转换为搜索基础单位(称为项(term))的历程。在阐发历程中,文本数据将经历多项操作:提取单词、移除通用单词、轻忽标点符号、将单词变为词根形式、将单词变成小写等等。阐发历程发生在体例索引和查询解析之前。阐发将文本数据转换为标记,这些标记将作为项添加到 Lucene 索引中。

Lucene 有多种内置阐发法度榜样,比如 SimpleAnalyzer、StandardAnalyzer、StopAnalyzer、SnowballAnalyzer 等。它们在标记文本和利用过滤器的要领上有所差别。由于阐发在体例索引之前移除单词,它削减了索引的大年夜小,然则晦气用正确的查询历程。您可以应用 Lucene 供给的基础构建块创建定制阐发法度榜样,以自己的要领节制阐发历程。表 1 展示了一些内置阐发法度榜样及其处置惩罚数据的要领。

表 1. Lucene 的内置阐发法度榜样

阐发法度榜样

对文本数据的操作

WhitespaceAnalyzer

分化空缺处的标记

SimpleAnalyzer

分化非字母字符的文本,并将文本转为小写形式

StopAnalyzer

移除虚字(stop word)—— 对检索无用的字,并将文本转为小写形式

StandardAnalyzer

根据一种繁杂语法(识别电子邮件地址、缩写、中文、日文、韩翰墨符、字母数字等等)标记文本

将文本转为小写形式

移除虚字

核心索引体例类

Directory 表示索引文件存储位置的抽象类。有两个常用的子类:

FSDirectory — 在实际文件系统中存储索引的 Directory 实现。该类对付大年夜型索引异常有用。

RAMDirectory — 在内存中存储所有索引的实现。该类适用于较小的索引,可以完备加载到内存中,在利用法度榜样终止之后销毁。因为索引保存在内存中,以是速率相对较快。

Analyzer 正如上文所述,阐发法度榜样认真处置惩罚文本数据并将其转换为标记存储在索引中。在体例索引前,IndexWriter 接管用于标记数据的阐发法度榜样。要为文本体例索引,您应该应用适用于该文本说话的阐发法度榜样。

默认阐发法度榜样适用于英语。在 Lucene 沙盒中还有其他阐发法度榜样,包括用于中文、日文和韩文的阐发法度榜样。

IndexDeletionPolicy 该接口用来实现从索引目录中定制删除逾期提交的策略。默认删除策略是 KeepOnlyLastCommitDeletionPolicy,该策略仅保留近来的提交,并在完成一些提交之后急速移除所有之前的提交。

IndexWriter 创建或掩护索引的类。它的构造函数接管布尔值,确定是否创建新索引,或者打开现有索引。它供给在索引中添加、删除和更新文档的措施。

对索引所做的变动最初缓存在内存中,并周期性转储到索引目录。IndexWriter 公开了几个节制若何在内存中缓存索引并写入磁盘的字段。对索引的变动对付 IndexReader 弗成见,除非调用 IndexWriter 的提交或关闭措施。IndexWriter 创建一个目录锁定文件,以经由过程同步索引更新保护索引不受破坏。IndexWriter 容许用户指定可选索引删除策略。

列表 1. 应用 Lucene IndexWriter

//Create instance of Directory where index files will be stored

Directory fsDirectory =FSDirectory.getDirectory(indexDirectory);

/* Create instance of analyzer, which will be used to tokenize

the input data */

Analyzer standardAnalyzer = new StandardAnalyzer();

//Create a new index

boolean create = true;

//Create the instance of deletion policy

IndexDeletionPolicy deletionPolicy = new KeepOnlyLastCommitDeletionPolicy();

indexWriter =new IndexWriter(fsDirectory,standardAnalyzer,create,

deletionPolicy,IndexWriter.MaxFieldLength.UNLIMITED);

将数据添加到索引

将文本数据添加到索引涉及到两个类。

Field 表示搜索中查询或检索的数据片。Field 类封装一个字段名称及其值。Lucene 供给了一些选项来指定字段是否必要体例索引或阐发,以及值是否必要存储。这些选项可以在创建字段实例时通报。下表展示了 Field 元数据选项的具体信息。

表 2. Field 元数据选项的具体信息

选项

描述

Field.Store.Yes

用于存储字段值。适用于显示搜索结果的字段 — 例如,文件路径和 URL。

Field.Store.No

没有存储字段值 — 例如,电子邮件消息正文。

Field.Index.No

适用于未搜索的字段 — 仅用于存储字段,比如文件路径。

Field.Index.ANALYZED

用于字段索引和阐发 — 例如,电子邮件消息正文和标题。

Field.Index.NOT_ANALYZED

用于体例索引但不阐发的字段。它在整体中保留字段的原值 — 例如,日期和小我名称。

Document 是一个字段聚拢。Lucene 也支持推进文档和字段,这在给某些索引数据付与紧张性时异常有用。给文本文件体例索引包括将文本数据封装在字段中、创建文档、添补字段,应用 IndexWriter 向索引添加文档。

列表 2 展示向索引添加数据的示例。

列表 2. 向索引添加数据

/*Step 1. Prepare the data for indexing. Extract the data. */

String sender = properties.getProperty("sender");

String date = properties.getProperty("date");

String subject = properties.getProperty("subject");

String message = properties.getProperty("message");

String emaildoc = file.getAbsolutePath();

/* Step 2. Wrap the data in the Fields and add them to a Document */

Field senderField =

new Field("sender",sender,Field.Store.YES,Field.Index.NOT_ANALYZED);

Field emaildatefield =

new Field("date",date,Field.Store.NO,Field.Index.NOT_ANALYZED);

Field subjectField =

new Field("subject",subject,Field.Store.YES,Field.Index.ANALYZED);

Field messagefield =

new Field("message",message,Field.Store.NO,Field.Index.ANALYZED);

Field emailDocField =

new Field("emailDoc",emaildoc,Field.Store.YES,

Field.Index.NO);

Document doc = new Document();

// Add these fields to a Lucene Document

doc.add(senderField);

doc.add(emaildatefield);

doc.add(subjectField);

doc.add(messagefield);

doc.add(emailDocField);

//Step 3: Add this document to Lucene Index.

indexWriter.addDocument(doc);

搜索索引数据

搜索是在索引中查找单词并查找包孕这些单词的文档的历程。应用 Lucene 的搜索 API 构建的搜索功能异常简单清楚明了。本小节评论争论 Lucene 搜索 API 的主要类。

Searcher

Searcher 是一个抽象基类,包孕各类超负荷搜索措施。IndexSearcher 是一个常用的子类,容许在给定的目录中存储搜索索引。Search 措施返回一个根据谋略分数排序的文档聚拢。Lucene 为每个匹配给定查询的文档谋略分数。IndexSearcher 是线程安然的;一个实例可以供多个线程并发应用。

Term

Term 是搜索的基础单位。它由两部分组成:单词文本和呈现该文本的字段的名称。Term 工具也涉及索引体例,然则可以在 Lucene 内部创建。

Query 和子类

Query 是一个用于查询的抽象基类。搜索指定单词或词组涉及到在项中包装它们,将项添加到查询工具,将查询工具通报到 IndexSearcher 的搜索措施。

Lucene 包孕各类类型的详细查询实现,比如 TermQuery、BooleanQuery、PhraseQuery、PrefixQuery、RangeQuery、MultiTermQuery、FilteredQuery、SpanQuery 等。以下部分评论争论 Lucene 查询 API 的主查询类。

TermQuery 搜索索引最基础的查询类型。可以应用单个项构建 TermQuery。项值应该区分大年夜小写,但也并非全是如斯。留意,通报的搜索项应该与文档阐发获得的项同等,由于阐发法度榜样在构建索引之前对原文本履行许多操作。

例如,斟酌电子邮件标题 “Job openings for Java Professionals at Bangalore”。假设您应用 StandardAnalyzer 体例索引。现在假如我们应用 TermQuery 搜索 “Java”,它不会返回任何内容,由于本文本应该已经规范化,并经由过程 StandardAnalyzer 转成小写。假如搜索小写单词 “java”,它将返回所有标题字段中包孕该单词的邮件。

列表 3. 应用 TermQuery 搜索

//Search mails having the word "java" in the subject field

Searcher indexSearcher = new IndexSearcher(indexDirectory);

Term term = new Term("subject","java");

Query termQuery = new TermQuery(term);

TopDocs topDocs = indexSearcher.search(termQuery,10);

RangeQuery 您可以应用 RangeQuery 在某个范围内搜索。索引中的所有项都以字典顺序排列。Lucene 的 RangeQuery 容许用户在某个范围内搜索项。该范围可以应用肇端项和终极项(包孕两端或不包孕两端均可)指定。

列表 4. 在某个范围内搜索

/* RangeQuery example:Search mails from 01/06/2009 to 6/06/2009

both inclusive */

Term begin = new Term("date","20090601");

Term end = new Term("date","20090606");

Query query = new RangeQuery(begin, end, true);

PrefixQuery 您可以应用 PrefixQuery 经由过程前缀单词进行搜索,该措施用于构建一个查询,该查询查找包孕以指定单词前缀开始的词汇的文档。

列表 5. 应用 PrefixQuery 搜索

//Search mails having sender field prefixed by the word 'job'

PrefixQuery prefixQuery = new PrefixQuery(new Term("sender","job"));

PrefixQuery query = new PrefixQuery(new Term("sender","job"));

BooleanQuery 您可以应用 BooleanQuery 组合任何数量的查询工具,构建强大年夜的查询。它应用 query 和一个关联查询的子句,唆使查询是应该发生、必须发生照样不得发生。在 BooleanQuery 中,子句的最大年夜数量默认限定为 1,024。您可以调用 setMaxClauseCount 措施设置最大年夜子句数。

列表 6. 应用 BooleanQuery 进行搜索

// Search mails have both 'java' and 'bangalore' in the subject field

Query query1 = new TermQuery(new Term("subject","java"));

Query query2 = new TermQuery(new Term("subject","bangalore"));

BooleanQuery query = new BooleanQuery();

query.add(query1,BooleanClause.Occur.MUST);

query.add(query2,BooleanClause.Occur.MUST);

PhraseQuery 您可以应用 PhraseQuery 进行短语搜索。PhraseQuery 匹配包孕特定单词序列的文档。PhraseQuery 应用索引中存储的项的位置信息。斟酌匹配的项之间的间隔称为 slop。默认环境下,slop 的值为零,这可以经由过程调用 setSlop 措施进行设置。PhraseQuery 还支持多个项短语。

列表 7. 应用 PhraseQuery 进行搜索

/* PhraseQuery example: Search mails that have phrase 'job opening j2ee'

in the subject field.*/

PhraseQuery query = new PhraseQuery();

query.setSlop(1);

query.add(new Term("subject","job"));

query.add(new Term("subject","opening"));

query.add(new Term("subject","j2ee"));

WildcardQuery WildcardQuery 实现通配符搜索查询,这容许您搜索 arch*(可以查找包孕 architect、architecture 等)之类的单词。应用两个标准通配符:

* 表示零个以上

? 表示一个以上

假如应用以通配符查询开始的模式进行搜索,则可能会引起机能的低落,由于这必要查询索引中的所有项以查找匹配文档。

列表 8. 应用 WildcardQuery 进行搜索

//Search for 'arch*' to find e-mail messages that have word 'architect' in the subject

field./

Query query = new WildcardQuery(new Term("subject","arch*"));

FuzzyQuery 您可以应用 FuzzyQuery 搜索类似项,该类匹配类似于指定单词的单词。类似度丈量基于 Levenshtein(编辑间隔)算法进行。在列表 9 中,FuzzyQuery 用于查找与拼错的单词 “admnistrtor” 最靠近的项,只管这个差错单词没有索引。

列表 9. 应用 FuzzyQuery 进行搜索

/* Search for emails that have word similar to 'admnistrtor' in the

subject field. Note we have misspelled admnistrtor here.*/

Query query = new FuzzyQuery(new Term("subject", "admnistrtor"));

QueryParser QueryParser 对付解析人工输入的查询字符异常有用。您可以应用它将用户输入的查询表达式解析为 Lucene 查询工具,这些工具可以通报到 IndexSearcher 的搜索措施。它可以解析富厚的查询表达式。 QueryParser 内部将人们输入的查询字符串转换为一个详细的查询子类。您必要应用反斜杠(\)将 *、? 等特殊字符进行转义。您可以应用运算符 AND、OR 和 NOT 构建文本布尔值查询。

列表 10. 搜索人工输入的查询表达式

QueryParser queryParser = new QueryParser("subject",new StandardAnalyzer());

// Search for emails that contain the words 'job openings' and '.net' and 'pune'

Query query = queryParser.parse("job openings AND .net AND pune");

显示搜索结果

IndexSearcher 返回一组对分级搜索结果(如匹配给定查询的文档)的引用。您可以应用 IndexSearcher 的搜索措施确定必要检索的最优先搜索结果数量。可以在此根基上构建定制分页。您可以添加定制 Web 利用法度榜样或桌面利用法度榜样来显示搜索结果。检索搜索结果涉及的主要类包括 ScoreDoc 和 TopDocs。

ScoreDoc 搜索结果中包孕一个指向文档的简单指针。这可以封装文档索引中文档的位置以及 Lucene 谋略的分数。 TopDocs 封装搜索结果以及 ScoreDoc 的总数。

以下代码片段展示了若何检索搜索结果中包孕的文档。

列表 11. 展示搜索结果

/* First parameter is the query to be executed and

second parameter indicates the no of search results to fetch */

TopDocs topDocs = indexSearcher.search(query,20);

System.out.println("Total hits "+topDocs.totalHits);

// Get an array of references to matched documents

ScoreDoc[] scoreDosArray = topDocs.scoreDocs;

for(ScoreDoc scoredoc: scoreDosArray){

//Retrieve the matched document and show relevant details

Document doc = indexSearcher.doc(scoredoc.doc);

System.out.println("\nSender: "+doc.getField("sender").stringValue());

System.out.println("Subject: "+doc.getField("subject").stringValue());

System.out.println("Email file location: "

+doc.getField("emailDoc").stringValue());

}

基础的索引操作

基础的索引操作包括移除和提升文档。

从索引中移除文档

利用法度榜样经常必要应用最新的数据更新索引并移除较旧的数据。例如,在 Web 搜索引擎中,索引必要按期更新,由于老是必要添加新网页,移除不存在的网页。Lucene 供给了 IndexReader 接口容许您对索引履行这些操作。

IndexReader 是一个供给各类措施造访索引的抽象类。Lucene 内部引用文档时应用文档编号,该编号可以在向索引添加或从中移除文档时变动。文档编号用于造访索引中的文档。IndexReader 不得用于更新目录中的索引,由于已经打开了 IndexWriter。IndexReader 在打开时老是搜索索引的快照。对索引的任何变动都可以看到,直到再次打开 IndexReader。应用 Lucene 从新打开它们的 IndexReader 可以看到最新的索引更新。

列表 12. 从索引中删除文档

// Delete all the mails from the index received in May 2009.

IndexReader indexReader = IndexReader.open(indexDirectory);

indexReader.deleteDocuments(new Term("month","05"));

//close associate index files and save deletions to disk

indexReader.close();

提升文档和字段

无意偶尔您必要给某些索引数据更高的紧张级别。您可以经由过程设置文档或字段的提升因子实现这一点。默认环境下,所有文档和字段的默认提升因子都是 1.0。

列表 13. 提升字段

if(subject.toLowerCase().indexOf("pune") != -1){

// Display search results that contain pune in their subject first by setting boost factor

subjectField.setBoost(2.2F);

}

//Display search results that contain 'job' in their sender email address

if(sender.toLowerCase().indexOf("job")!=-1){

luceneDocument.setBoost(2.1F);

}

扩展搜索

Lucene 供给一个称为排序 的高档功能。您可以根据唆使文档在索引中相对位置的字段对搜索结果进行排序。用于排序的字段必须体例索引但不得标记。搜索字段中可以放入 4 种可能的项值:整数值、long 值、浮点值和字符串。

还可以经由过程索引顺序排序搜索结果。Lucene 经由过程低落相关度(比如默认的谋略分数)对结果排序。排序的顺序是可以变动的。

列表 14. 排序搜索结果

/* Search mails having the word 'job' in subject and return results

sorted by sender's email in descending order.

*/

SortField sortField = new SortField("sender", true);

Sort sortBySender = new Sort(sortField);

WildcardQuery query = new WildcardQuery(new Term("subject","job*"));

TopFieldDocs topFieldDocs =

indexSearcher.search(query,null,20,sortBySender);

//Sorting by index order

topFieldDocs = indexSearcher.search(query,null,20,Sort.INDEXORDER);

Filtering 是限定搜索空间,只容许某个文档子集作为搜索范围的历程。您可以应用该功能实现对搜索结果进行再次搜索,或者在搜索结果上实现安然性。Lucene 带有各类内置的过滤器,比如 BooleanFilter、CachingWrapperFilter、ChainedFilter、DuplicateFilter、PrefixFilter、QueryWrapperFilter、RangeFilter、RemoteCachingWrapperFilter、SpanFilter 等。Filter 可以通报到 IndexSearcher 的搜索措施,以过滤匹配筛选标准的筛选文档。

列表 15. 筛选搜索结果

/*Filter the results to show only mails that have sender field

prefixed with 'jobs' */

Term prefix = new Term("sender","jobs");

Filter prefixFilter = new PrefixFilter(prefix);

WildcardQuery query = new WildcardQuery(new Term("subject","job*"));

indexSearcher.search(query,prefixFilter,20);

停止语

Lucene 是来自 Apache 的一个异常盛行的开源搜索库, 它为利用法度榜样供给了强大年夜的索引体例和搜索功能。它供给了一个简单易用的 API,只必要轻细懂得索引体例和搜索的道理即可应用。在本文中,您进修了 Lucene 架构及其核心 API。

Lucene 为许多有名网站和组织供给了各类强大年夜的搜索功能。它还兼容许多其他编程说话。Lucene 有一个生动的大年夜型技巧用户社区。假如您必要一些易用、可扩展以及高机能的开源搜索库,Apache Lucene 是一个极佳的选择。

下载

描述

名字

大年夜小

下载措施

Lucene 代码示例

os-apache-lucenesearch-SampleApplication.zip

755KB

HTTP

关于作者

Amol Sonawane 是一名高档软件工程师,拥有班加罗尔国际信息技巧学院的钻研生学位。他为许多领域设计和开拓软件,比如供应链治理、企业利用法度榜样集成和商业智能。他可以应用 J2EE 技巧和框架(Struts 和 Spring)开拓利用法度榜样。他与妻子 Anuja 栖身在印度普内。除了编程,他还爱好照相、下棋和灯谜。

您可能还会对下面的文章感兴趣: