apache lucene_使用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构建应用程序的步骤
apache lucene_使用Apache Lucene搜索文本_第1张图片

本文使用来自使用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的内置分析仪
分析仪 对文本数据进行的操作
空白分析仪 在空白处分割标记
简单分析器 用非字母字符分隔文本,并将文本小写
停止分析器 删除停用词(对搜索无用)并将文本小写
标准分析仪 根据复杂的语法对文本进行标记,该语法可以识别:电子邮件地址; 首字母缩写词 中文,日文和韩文字符; 字母数字 和更多
将文字小写
删除停用词

核心索引类

Directory
表示索引文件存储位置的抽象类。 通常主要使用两个子类:
  • FSDirectoryDirectory的实现,在实际的文件系统中存储索引。 这对于大索引很有用。
  • RAMDirectory —将所有索引存储在内存中的实现。 这适用于较小的索引,这些索引可以完全加载到内存中,并在应用程序终止时销毁。 由于索引保存在内存中,因此速度相对较快。
Analyzer
如上所述,分析器负责预处理文本数据并将其转换为存储在索引中的令牌。 IndexWriter接受用于对数据建立索引之前对其进行标记化的分析器。 要正确索引文本,您应该使用适合需要索引的文本语言的分析器。

默认分析器适用于英语。 Lucene沙箱中还有其他几种分析器,包括中文,日文和韩文的分析器。

IndexDeletionPolicy
用于实现自定义从索引目录中删除过时提交的策略的接口。 默认的删除策略是KeepOnlyLastCommitDeletionPolicy ,该策略仅保留最新的提交,并在完成新提交后立即删除所有先前的提交。
IndexWriter
创建或维护索引的类。 它的构造函数接受一个布尔值,该布尔值确定是创建新索引还是打开现有索引。 它提供了添加,删除或更新索引中文档的方法。

最初对索引所做的更改将存储在内存中,并定期刷新到索引目录中。 IndexWriter公开了几个字段,这些字段控制索引在内存中的缓冲方式以及如何将其写入磁盘。 除非调用IndexWriter的commit或close方法,否则对IndexReader不到对索引所做的更改。 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.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是一个抽象基类,具有各种重载的搜索方法。 IndexSearcher是一个常用的子类,它允许搜索存储在给定目录中的索引。 Search方法返回按计算分数排序的文档的有序集合。 Lucene计算与给定查询匹配的每个文档的分数。 IndexSearcher是线程安全的; 一个实例可以由多个线程同时使用。

术语

术语是搜索的最基本单位。 它由两个元素组成:单词的文本和出现该文本的字段的名称。 术语对象也参与索引编制,但它们是由Lucene内部创建的。

查询和子类

QueryQuery的抽象基类。 搜索指定的单词或短语包括将它们包装在一个术语中,将这些术语添加到查询对象中,然后将此查询对象传递给IndexSearcher的search方法。

Lucene带有各种类型的具体查询实现,例如TermQuery,BooleanQuery,PhraseQuery,PrefixQuery,RangeQuery,MultiTermQuery,FilteredQuery,SpanQuery等。下面的部分讨论了Lucene的查询API中的主要查询类。

TermQuery
用于搜索索引的最基本的查询类型。 可以使用单个术语构造TermQuery 术语值应区分大小写,但这并不完全正确。 重要的是要注意,传递给搜索的术语应与文档分析产生的术语一致,因为分析人员在建立索引之前会对原始文​​本执行许多操作。

例如,考虑电子邮件主题“班加罗尔Java专业人员的职位空缺”。 假设您使用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在范围内进行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 。 默认情况下, setSlop的值为零,可以通过调用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实现了WildcardQuery搜索查询,该查询使您可以进行诸如arch *的搜索(让您查找包含架构师,体系结构等的文档)。 使用两种标准的通配符:
  • *零个或多个
  • ? 一个或多个
如果您尝试在通配符查询的开头使用模式进行搜索,则性能可能会下降,因为将查询索引中的所有术语以查找匹配的文档。
清单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搜索相似的词, 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的search方法。 它可以解析丰富的查询表达式。 QueryParser内部将人工输入的查询字符串转换为具体的查询子类之一。 您需要转义*?等特殊字符? 加上反斜杠( \ )。 您可以使用ANDORNOT运算符以文本形式构造布尔查询。
清单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应用程序或桌面应用程序以显示搜索结果。 检索搜索结果涉及的主要类是ScoreDocTopDocs

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搜索引擎,随着添加新的Web页面和不存在的Web页面需要删除,索引需要定期更新。 Lucene提供了IndexReader接口,可让您对索引执行这些操作。

IndexReader是一个抽象类,提供了各种访问索引的方法。 Lucene在内部指的是文档编号随文档添加到索引或从索引删除而更改的文档。 文档编号用于访问索引中的文档。 IndexReader不能用于更新已经打开IndexWriter的目录中的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提供了称为sorting的高级功能。 您可以按指示索引中文档相对位置的字段对搜索结果进行排序。 用于排序的字段必须建立索引,但不能标记化。 可以在排序字段中放入四种可能的术语值:整数,长整数,浮点数或字符串。

搜索结果也可以按索引顺序排序。 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);

筛选是一个限制搜索空间的过程,只允许考虑文档的一个子集作为搜索结果。 您可以使用此功能实现搜索内搜索结果,或在搜索结果之上实现安全性。 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是一个不错的选择。


翻译自: https://www.ibm.com/developerworks/java/library/os-apache-lucenesearch/index.html

你可能感兴趣的:(apache lucene_使用Apache Lucene搜索文本)