65.9K
CodeProject 正在变化。 阅读更多。
Home

Lucene.Net 简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (83投票s)

2008年9月29日

CPOL

9分钟阅读

viewsIcon

434522

深入了解创建具有高级搜索功能的快速全文索引。

什么是 Lucene.Net?

Lucene.Net 是一个高性能的信息检索 (IR) 库,也称为搜索引擎库。Lucene.Net 包含强大的 API,用于创建全文索引,并将高级且精确的搜索技术集成到您的程序中。有些人可能会将 Lucene.Net 与现成的应用程序混淆,例如网络搜索/爬虫或文件搜索应用程序,但 Lucene.Net 并非此类应用程序,它是一个框架库。Lucene.Net 提供了一个框架,供您自己实现这些复杂的技术。Lucene.Net 对您可以索引和搜索的内容不做任何区分,这比其他全文索引/搜索实现提供了更大的灵活性;您可以索引任何可以表示为文本的内容。还有一些方法可以让 Lucene.Net 索引 HTML、Office 文档、PDF 文件等。

Lucene.Net 是原始 Lucene 项目的 API 对 API 端口,该项目是用 Java 编写的,甚至单元测试也已移植以保证质量。此外,Lucene.Net 索引与 Lucene 索引完全兼容,两个库可以一起在同一个索引上使用,没有任何问题。许多产品使用 Lucene 和 Lucene.Net 来构建它们的搜索;一些知名的网站包括 WikipediaCNETMonster.comMayo ClinicFedEx 以及 更多。但是,不仅仅是网站使用 Lucene;还有一个名为 Lookout 的产品使用了 Lucene.Net,这是一个用于 Microsoft Outlook 的搜索工具,它使得 Outlook 的集成搜索显得非常缓慢和不准确。

Lucene.Net 目前正在 Apache 软件基金会进行孵化。其源代码存储在一个 subversion 存储库中,可以在 这里 找到。如果您需要下载源代码的帮助,可以使用免费的 TortoiseSVNRapidSVN。Lucene.Net 项目始终欢迎新贡献者。并且,请记住,除了编写代码之外,还有 多种方式 可以为开源项目做出贡献。

创建搜索解决方案

搜索解决方案大致有两个主要部分。索引您希望搜索的内容,以及实际搜索内容。而且,这几乎就是这么简单。在我们拥有索引后,我们将执行搜索。

创建索引所需的内容

让我们看一个创建索引并填充它的例子。

//state the file location of the index
string indexFileLocation = @"C:\Index"; 
Lucene.Net.Store.Directory dir =
    Lucene.Net.Store.FSDirectory.GetDirectory(indexFileLocation, true);

//create an analyzer to process the text
Lucene.Net.Analysis.Analyzer analyzer = new
Lucene.Net.Analysis.Standard.StandardAnalyzer(); 

//create the index writer with the directory and analyzer defined.
Lucene.Net.Index.IndexWriter indexWriter = new
Lucene.Net.Index.IndexWriter(dir, analyzer, 
           /*true to create a new index*/ true); 

//create a document, add in a single field
Lucene.Net.Documents.Document doc = new
Lucene.Net.Documents.Document();

Lucene.Net.Documents.Field fldContent = 
  new Lucene.Net.Documents.Field("content", 
  "The quick brown fox jumps over the lazy dog",
  Lucene.Net.Documents.Field.Store.YES, 


Lucene.Net.Documents.Field.Index.TOKENIZED, 
Lucene.Net.Documents.Field.TermVector.YES);

doc.Add(fldContent);

//write the document to the index
indexWriter.AddDocument(doc);

//optimize and close the writer
indexWriter.Optimize(); 
indexWriter.Close();

好了,不错,让我们看看我们刚刚做了什么。这里使用了五个主要类,它们是 DirectoryAnalyzerIndexWriterDocumentField。我们创建一个 Directory,让 Lucene 知道我们将把索引存储在哪里。Analyzer 用于分析文本。我们有一个 IndexWriter,它使用 DirectoryAnalyzer 来创建和写入索引。然后,我们创建一个新的 Document 对象,并创建一个 Field,其字段名称设置为“content”,值为“The quick brown fox jumps over the lazy dog”。我们将 Field 添加到 Document 中,现在,我们可以使用 IndexWriter 索引新创建的 Document。然后,我们有一个看起来很奇怪的 Optimize 调用(稍后会详细介绍),并在完成后调用 Close 来关闭写入器。我们已成功创建了一个准备好进行搜索的全文索引。首先,让我们稍微详细地介绍一下我们刚刚使用的一些类。

  • Lucene.Net.Store.DirectoryDirectory 是一个基类,用于提供目录的抽象视图。Lucene.Net 提供了两个实现。FSDirectory 使用文件目录来存储索引。RAMDirectory 是一个内存中的目录,您可以使用它来存储索引。您可以继承 Directory 类来实现自己的自定义目录对象来存储索引。
  • Lucene.Net.Analysis.AnalyzerAnalyzer 是一个基类,负责将文本分解为单个单词或词条,并删除任何停用词,或者 Lucene.net 称之为停用词;停用词包括“and”、“a”、“the”等。现在,我们将仅使用 StandardAnalyzer 类,因为它是一个非常好的第一选择。您可以将您自己的停用词列表作为字符串数组传递给 StandardAnalyzer 的构造函数。使用默认构造函数将使用默认的停用词列表。您可以继承 Analyzer 来实现自定义的处理要索引的文档的方式。
  • Lucene.Net.Index.IndexWriterIndexWriter 负责协调 Analyzer 并将结果发送到 Directory 进行存储。在创建索引期间,写入器将在 Directory 中创建一些文件。当我们向索引写入器添加文档时,索引写入器将使用 Analyzer 来分解每个字段,并找到一个位置将索引的文档存储在 Directory 中。在完成一次索引会话后,建议您优化索引,这会压缩索引以获得资源消耗更少的模型。另请注意,不建议您为添加到索引的每个 Document 调用 Optimize,最好在索引会话后只调用一次,如果可能的话。在 IndexWriter 的构造函数结束时,我们将 true 指定为创建新索引。要添加更多文档到索引,您应该将 false 指定在这里,以避免覆盖索引。
  • Lucene.Net.Documents.DocumentDocument 类是由 IndexWriter 索引的内容。您可以将 Document 视为一个您想要检索的实体;一个 Document 可以代表一封电子邮件、一个网页、一个食谱,甚至是一篇 CodeProject 文章。
  • Lucene.Net.Documents.FieldDocument 包含一个 Field 列表,用于描述文档。每个字段都有一个名称和一个值。每个字段的值都包含您希望使其可搜索的文本。字段构造函数的其他部分包含有关如何处理单个字段的说明。Field.Store 说明告诉 IndexWriter 您希望将字段的值存储在索引中,以便稍后检索该值并对其进行操作,例如在搜索结果中向用户显示数据或存储一个标识值,例如此字段文档所代表对象的初识键。
  • 其他说明是 Field.Index 值,它们告诉 IndexWriter 如何(如果根本)索引该字段。可能的值包括 Field.Index.TOKENIZED,这意味着我们希望使用 IndexWriter 提供的 Analyzer 来分解字符串并使其可搜索。另一个选项是 Field.Index.UNTOKENIZED,它仍然会索引该字段,但作为一个整体,并且不会被 Analyzer 分解。存储值和索引值之间的区别在于,当您存储值时,目的是能够从索引中检索回该值。而索引值的目的是使字段的值可搜索。存储一个值但不对其进行索引是完全可以接受的,就像您可能希望存储一个标识值但不想对其进行索引一样,而您可能希望索引一封电子邮件的内容,但实际上不想在搜索结果中显示该内容。另一组说明定义了如何处理 TermVector。将 TermVector 存储在索引中用于 Lucene 的高级功能,该功能不完全匹配搜索查询项,但使用词向量,您将能够检索相关文档 - 例如,关于同一主题的文档。

在对一些文档进行了良好的索引后,我相信我们已经准备好迎接有趣的部分了。

搜索索引所需的内容

让我们看一个执行简单搜索所需的示例。

//state the file location of the index
string indexFileLocation = @"C:\Index";
Lucene.Net.Store.Directory dir =
    Lucene.Net.Store.FSDirectory.GetDirectory(indexFileLocation, true);

//create an index searcher that will perform the search
Lucene.Net.Search.IndexSearcher searcher = new
Lucene.Net.Search.IndexSearcher(dir);

//build a query object
Lucene.Net.Index.Term searchTerm = 
  new Lucene.Net.Index.Term("content", "fox");
Lucene.Net.Search.Query query = new Lucene.Net.Search.TermQuery(searchTerm);

//execute the query
Lucene.Net.Search.Hits hits = searcher.Search(query);

//iterate over the results.
for (int i = 0; i < hits.Length(); i++)
{
    Document doc = hits.Doc(i);
    string contentValue = doc.Get("content");

    Console.WriteLine(contentValue);

}

使用这段简短的代码,我们定义了索引的存储位置,再次通过使用 Directory 类。但是现在,我们有了这个 IndexSearch 对象,它完成了实际搜索的所有繁重工作。要使用 IndexSearcher,我们必须向其传递一个 Query 对象。您从 IndexSearcher 对象调用 Search 方法,并将 Query 对象传递给搜索。它将返回一个 Hits 对象。最后,通过迭代 Hits,我们可以提取与该查询匹配的 Document。在我们获得文档后,我们可以提取在索引该文档时曾存储过的字段的值。让我们更仔细地看看这些类!

  • Lucene.Net.Search.IndexSearcherIndexSearcher 对象再次完成了实际搜索的所有繁重工作。当执行搜索时,它将使用传递给 IndexSearcher 构造函数的 Directory 对象将索引打开为只读文件。IndexSearcher 对象上还有更多方法,提供了其他查询索引的方式。
  • Lucene.Net.Index.TermTerm 是最基本的搜索结构。Term 由两部分组成,您要搜索的字段的名称,以及该字段的值。
  • Lucene.Net.Search.Query – 一个基类,它与 IndexSearcher 一起提供结果。Query 是一个抽象基类。在上面的示例中,我们使用了 TermQuery 对象,该对象对单个 Term 进行查询。还有许多其他创建查询的方式。Query 类的一些实现,除了 TermQuery 之外,还包括 BooleanQueryPhraseQueryPrefixQueryPhrasePrefixQueryRangeQueryFilteredQuerySpanQuery。有了所有这些查询选项,我们就需要一种方法让用户从单个文本框中进行强大的查询。这就是另一个类的作用,那就是 QueryParser。稍后会详细介绍。
  • Lucene.Net.Search.Hits – 这代表搜索返回的文档列表。Hits 对象可以被迭代,并负责从搜索中获取文档。对于大型索引,不建议迭代所有搜索结果。另外,值得注意的是,Hits 对象不会立即加载所有文档,它只加载一部分文档。否则,会导致性能问题。在获得 Hits 对象后,您可以调用 Doc(int index) 方法,该方法将返回与单个命中相关的文档。

正如我之前提到的,Query 类有许多实现,它们在查询中都有其位置。通常,您不会自己创建查询对象,而是让一个强大的解析器使用简单的语法为您构建复杂的查询,就像您搜索 Google 一样。这就是我向您介绍 QueryParser 的地方。QueryParser 实例有一个名为 Parse(string query) 的方法。以下是使用 QueryParser 的小示例。

//create an analyzer to process the text
Lucene.Net.Analysis.Analyzer analyzer = new
Lucene.Net.Analysis.Standard.StandardAnalyzer();

//create the query parser, with the default search feild set to "content"
Lucene.Net.QueryParsers.QueryParser queryParser = new
    Lucene.Net.QueryParsers.QueryParser("content", analyzer);

//parse the query string into a Query object
Lucene.Net.Search.Query
query = queryParser.Parse("fox");

而且,如果您认为所有这些东西都很棒,我们才刚刚触及表面。但是,本文到此就结束了。如果您想了解更多信息,请告诉我,我将开始撰写另一篇关于 Lucene.Net 的文章。

© . All rights reserved.