Lucene.NET 搜索引擎入门指南

Lucene.NET 基础

Lucene.NET 是Lucene搜索引擎库的.NET移植版本,使用C#编写,面向.NET运行时用户。基本概念是,构建一个.NET对象的索引,这些对象存储在一个特殊的Lucene文档中,这些文档具有可搜索的字段。然后,可以运行查询来检索这些存储的文档,并将它们重新水合回.NET对象。

构建索引(文档)

索引构建阶段是将.NET对象创建为每个对象的文档,并添加某些字段(不必为所有.NET对象的属性存储/添加字段,这取决于决定),然后将这些文档保存到磁盘上的物理目录中,稍后将搜索这些文档。

查询数据

查询数据显然是想要使用Lucene.NET的主要原因之一,它拥有良好的查询设施,这一点应该不会让感到惊讶。Lucene.NET使用的查询语法的一个很好的资源可以在这里找到:

一些简单的例子可能是:

  • title:foo - 在标题字段中搜索单词“foo”。
  • title:"foo bar" - 在标题字段中搜索短语“foo bar”。
  • title:"foo bar" AND body:"quick fox" - 在标题字段中搜索短语“foo bar”,并且在正文字段中搜索短语“quick fox”。
  • (title:"foo bar" AND body:"quick fox") OR title:fox - 在标题字段中搜索短语“foo bar”并且正文字段中搜索短语“quick fox”,或者在标题字段中搜索单词“fox”。
  • title:foo -title:bar - 在标题字段中搜索单词“foo”,但不包括“bar”。

分析器类型

Lucene.NET中有很多不同的分析器类型,例如:

  • SimpleAnalyzer
  • StandardAnalyzer
  • StopAnalyzer
  • WhiteSpaceAnalyzer

选择正确的分析器取决于想要实现的目标,以及要求。

演示应用

本节将讨论附带的演示应用程序,并提供足够的信息,以便开始构建自己的Lucene.NET支持的搜索,如果希望在自己的应用程序中使用它。

演示应用程序非常简单,以下是它所做的:

  • 有一个静态文本文件(在例子中是一首诗),可以索引。
  • 在启动时,文本文件被索引并添加到总体Lucene索引目录中(在例子中是硬编码为C:\Temp\LuceneIndex)。
  • 有一个用户界面(使用了WPF,但这并不重要),允许用户输入用于搜索索引的Lucene数据的搜索关键字。
  • 将显示原始用于创建Lucene索引数据的文本文件的所有行。
  • 当用户进行搜索时,将显示诗中的匹配行。

认为最好的例子是看看示例。所以,这是UI在首次加载时的样子:

然后输入一个搜索词,比如“when”,会看到这样的结果:

这就是演示所做的全部,但认为这足以演示Lucene的工作原理。

存储的内容和方式

那么存储了什么内容呢?这很简单,回想一下说过有一个静态文本文件(一首诗),从使用一个简单的实用程序类开始读取这个静态文本文件,如下所示,将其转换为可以添加到Lucene索引中的SampleDataFileRow对象:

public class SampleDataFileReader : ISampleDataFileReader { public IEnumerable ReadAllRows() { FileInfo assFile = new FileInfo(Assembly.GetExecutingAssembly().Location); string file = string.Format("{0}\\Lucene\\SampleDataFile.txt", assFile.Directory.FullName); string[] lines = File.ReadAllLines(file); for (int i = 0; i < lines.Length; i++) { yield return new SampleDataFileRow { LineNumber = i + 1, LineText = lines[i] }; } } }

SampleDataFileRow对象如下所示:

public class SampleDataFileRow { public int LineNumber { get; set; } public string LineText { get; set; } public float Score { get; set; } }

然后构建Lucene索引,如下所示:

public class LuceneService : ILuceneService { private Analyzer analyzer = new WhitespaceAnalyzer(); private Directory luceneIndexDirectory; private IndexWriter writer; private string indexPath = @"c:\temp\LuceneIndex"; public LuceneService() { InitialiseLucene(); } private void InitialiseLucene() { if (System.IO.Directory.Exists(indexPath)) { System.IO.Directory.Delete(indexPath, true); } luceneIndexDirectory = FSDirectory.GetDirectory(indexPath); writer = new IndexWriter(luceneIndexDirectory, analyzer, true); } public void BuildIndex(IEnumerable dataToIndex) { foreach (var sampleDataFileRow in dataToIndex) { Document doc = new Document(); doc.Add(new Field("LineNumber", sampleDataFileRow.LineNumber.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED)); doc.Add(new Field("LineText", sampleDataFileRow.LineText, Field.Store.YES, Field.Index.TOKENIZED)); writer.AddDocument(doc); } writer.Optimize(); writer.Flush(); writer.Close(); luceneIndexDirectory.Close(); } }

认为这段代码相当简单易懂,基本上就是这样做的:

  • 创建新的Lucene索引目录。
  • 创建一个Lucene写入器。
  • 为源对象创建一个新的Lucene文档。
  • 将字段添加到Lucene文档。
  • 将Lucene文档写入磁盘。

如果处理的是大量数据,可能想要创建静态的Field字段并重用它们,而不是每次重建索引时都创建新的。显然,对于这个演示,Lucene索引每运行一次应用程序就只创建一次,但在生产应用程序中,可能会每5分钟构建一次索引,或者类似的东西,在这种情况下,建议通过创建静态字段来重用Field对象。

搜索和方式

在搜索索引数据方面,这真的很简单,所需要做的就像这样:

public class LuceneService : ILuceneService { private Analyzer analyzer = new WhitespaceAnalyzer(); private Directory luceneIndexDirectory; private IndexWriter writer; private string indexPath = @"c:\temp\LuceneIndex"; public LuceneService() { InitialiseLucene(); } public IEnumerable Search(string searchTerm) { IndexSearcher searcher = new IndexSearcher(luceneIndexDirectory); QueryParser parser = new QueryParser("LineText", analyzer); Query query = parser.Parse(searchTerm); Hits hitsFound = searcher.Search(query); List results = new List(); SampleDataFileRow sampleDataFileRow = null; for (int i = 0; i < hitsFound.Length(); i++) { sampleDataFileRow = new SampleDataFileRow(); Document doc = hitsFound.Doc(i); sampleDataFileRow.LineNumber = int.Parse(doc.Get("LineNumber")); sampleDataFileRow.LineText = doc.Get("LineText"); float score = hitsFound.Score(i); sampleDataFileRow.Score = score; results.Add(sampleDataFileRow); } return results.OrderByDescending(x => x.Score).ToList(); } }

说实话,这没什么大不了的,认为代码解释了所有需要知道的东西。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485