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具有许多功能。 它:
如图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显示了一些内置分析仪及其处理数据的方式。
分析仪 | 对文本数据进行的操作 |
---|---|
空白分析仪 | 在空白处分割标记 |
简单分析器 | 用非字母字符分隔文本,并将文本小写 |
停止分析器 | 删除停用词(对搜索无用)并将文本小写 |
标准分析仪 | 根据复杂的语法对文本进行标记,该语法可以识别:电子邮件地址; 首字母缩写词 中文,日文和韩文字符; 字母数字 和更多 将文字小写 删除停用词 |
Directory
FSDirectory
— Directory
的实现,在实际的文件系统中存储索引。 这对于大索引很有用。 RAMDirectory
—将所有索引存储在内存中的实现。 这适用于较小的索引,这些索引可以完全加载到内存中,并在应用程序终止时销毁。 由于索引保存在内存中,因此速度相对较快。 Analyzer
IndexWriter
接受用于对数据建立索引之前对其进行标记化的分析器。
要正确索引文本,您应该使用适合需要索引的文本语言的分析器。
默认分析器适用于英语。 Lucene沙箱中还有其他几种分析器,包括中文,日文和韩文的分析器。
IndexDeletionPolicy
KeepOnlyLastCommitDeletionPolicy
,该策略仅保留最新的提交,并在完成新提交后立即删除所有先前的提交。
IndexWriter
最初对索引所做的更改将存储在内存中,并定期刷新到索引目录中。 IndexWriter
公开了几个字段,这些字段控制索引在内存中的缓冲方式以及如何将其写入磁盘。 除非调用IndexWriter
的commit或close方法,否则对IndexReader
不到对索引所做的更改。 IndexWriter
为目录创建一个锁定文件,以防止索引同时更新导致索引损坏。 IndexWriter
允许用户指定可选的索引删除策略。
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
元数据选项的详细信息。
Field
元数据选项的详细信息 选项 | 描述 |
---|---|
Field.Store.Yes | 用于存储字段的值。 适用于显示搜索结果的字段,例如文件路径和URL。 |
字段存储号 | 字段值未存储-例如,电子邮件正文。 |
字段索引号 | 适用于未搜索的字段-通常与存储的字段一起使用,例如文件路径。 |
字段索引分析 | 用于索引和分析的字段,例如,电子邮件正文和主题。 |
Field.Index.NOT_ANALYZED | 用于索引但未分析的字段。 它完整保留了字段的原始值,例如日期和个人名称。 |
而Document
是字段的集合。 Lucene还支持增强文档和字段,如果要重视某些索引数据,这是一个有用的功能。 为文本文件建立索引包括将文本数据包装在字段中,创建文档,使用字段填充文本,以及使用IndexWriter
将文档添加到索引中。
清单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内部创建的。
Query
是Query
的抽象基类。 搜索指定的单词或短语包括将它们包装在一个术语中,将这些术语添加到查询对象中,然后将此查询对象传递给IndexSearcher
的search方法。
Lucene带有各种类型的具体查询实现,例如TermQuery,BooleanQuery,PhraseQuery,PrefixQuery,RangeQuery,MultiTermQuery,FilteredQuery,SpanQuery等。下面的部分讨论了Lucene的查询API中的主要查询类。
TermQuery
TermQuery
。
术语值应区分大小写,但这并不完全正确。
重要的是要注意,传递给搜索的术语应与文档分析产生的术语一致,因为分析人员在建立索引之前会对原始文本执行许多操作。
例如,考虑电子邮件主题“班加罗尔Java专业人员的职位空缺”。 假设您使用StandardAnalyzer
将其编入索引。 现在,如果我们使用TermQuery
搜索“ Java”,它将不会返回任何内容,因为此文本将被StandardAnalyzer
规范化并以小写形式显示。 如果我们搜索小写的单词“ java”,它将在主题字段中返回所有包含该单词的邮件。
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
允许用户搜索范围内的术语。
可以使用开始项和结束项来指定范围,可以将其包括在内或排除在外。
/* 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
来搜索前缀词,该前缀词用于构造与包含以指定词前缀开头的术语的文档匹配的查询。
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
方法来设置最大类。
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
还支持多个术语短语。
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 *的搜索(让您查找包含架构师,体系结构等的文档)。
使用两种标准的通配符:
*
零个或多个 ?
一个或多个 //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”的紧密匹配,尽管该单词未编入索引。
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
内部将人工输入的查询字符串转换为具体的查询子类之一。
您需要转义*
、 ?
等特殊字符?
加上反斜杠( \
)。
您可以使用AND
, OR
和NOT
运算符以文本形式构造布尔查询。
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
TopDocs
ScoreDoc
数组。
下面的代码段显示了如何检索搜索结果中包含的文档。
/* 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
以查看最新的索引更新很重要。
// 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。
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通过降低相关性(例如默认情况下的计算得分)对结果进行排序。 排序顺序也可以更改。
/* 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
的搜索方法以过滤符合过滤条件的文档。
/*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