小型 Lucene.NET 演示应用






4.97/5 (55投票s)
一个小型的演示应用,展示如何使用 Lucene.NET 进行存储/搜索
演示项目 : Lucene_VS2012_Demo_App.zip
目录
引言
我刚刚辞职,即将开始一份新工作,但在我离开之前,我的团队中的另一位同事负责编写一些使用 .NET 的 Lucene 搜索引擎的东西。我们需要搜索 30 万个左右的对象,而且似乎相当快。我们是在响应用户输入字符的操作,即使它要经过大量的 Rx 和大量的不同应用程序层,最后命中 Lucene 搜索结果,但也没有注意到任何延迟。
这引起了我的兴趣,我决定尝试一下 Lucene,看看是否能想出一个简单的演示可以分享。
所以,这就是我所做的,也是我的成果。
Lucene .NET 基础知识
Lucene.Net 是 Lucene 搜索引擎库的一个端口,用 C# 编写,面向 .NET 运行时用户。
总体的想法是,你构建一个 .NET 对象的索引,这些对象存储在一个特殊的 Lucene 文档中,具有可搜索的字段。然后,你可以针对这些存储的文档运行查询,并将它们重新水合为 .NET 对象。
构建索引(文档)
索引构建阶段是你获取 .NET 对象,为每个对象创建一个文档,并添加某些字段(你不必存储/添加 .NET 对象的所有属性的字段,这取决于你的决定),然后将这些文档保存到磁盘上的一个物理目录,以后将在此目录中进行搜索。
查询数据
查询数据显然是你想要使用 Lucene .NET 的主要原因之一,因此它具有良好的查询功能并不奇怪。
我认为 Lucene .NET 使用的查询语法的一个不错的资源可以在这里找到。
http://www.lucenetutorial.com/lucene-query-syntax.html
一些简单的例子可能包括:
title:foo | : 在 title 字段中搜索单词 "foo"。 |
title:"foo bar" | : 在 title 字段中搜索短语 "foo bar"。 |
title:"foo bar" AND body:"quick fox" | : 在 title 字段中搜索短语 "foo bar" **并且** 在 body 字段中搜索短语 "quick fox"。 |
(title:"foo bar" AND body:"quick fox") OR title:fox | : 搜索 title 字段中的短语 "foo bar" **并且** body 字段中的短语 "quick fox",**或者** 搜索 title 字段中的单词 "fox"。 |
title:foo -title:bar | : 在 title 字段中搜索单词 "foo" **但不是** "bar"。 |
分析器类型
Lucene.NET 中实际上有很多不同的分析器类型,例如(还有很多,这里只列出几个):
- SimpleAnalyzer
- StandardAnalyzer
- StopAnalyzer
- WhiteSpaceAnalyzer
选择正确的分析器取决于你想要实现的目标以及你的需求。
演示应用程序
本节将介绍随附的演示应用,并应提供足够的信息,以便在你希望在自己的应用程序中使用它时,开始构建你自己的基于 Lucene.NET 的搜索。
演示应用做什么?
这个演示应用确实很简单,它所做的事情如下:
- 有一个静态文本文件(在我这里是一首诗),可以进行索引。
- 启动时,文本文件会被索引并添加到整体 Lucene 索引目录中(在我这里硬编码为 C:\Temp\LuceneIndex)。
- 有一个 UI(我使用了 WPF,但无关紧要),它:
- 允许用户输入一个搜索关键字,用于搜索已索引的 Lucene 数据。
- 显示最初用于创建 Lucene 索引数据的文本文件中的所有行。
- 显示当用户进行搜索时,诗歌中匹配的行。
我认为最好看一个例子。所以,这是 UI 加载时的样子。
然后我们输入一个搜索词,比如单词 "when",我们会看到这个。
演示应用就做到这里,但我觉得这足以演示 Lucene 的工作原理。
存储什么以及如何存储
那么,存储的是什么呢?这非常简单。回想一下我说的我们有一个静态文本文件(一首诗),我们首先使用下面的简单实用类读取该静态文本文件,将其转换为实际的 SampleDataFileRow
对象,然后添加到 Lucene 索引中。
public class SampleDataFileReader : ISampleDataFileReader
{
public IEnumerable<SampleDataFileRow> 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
{
// Note there are many different types of Analyzer that may be used with Lucene, the exact one you use
// will depend on your requirements
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<SampleDataFileRow> 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
{
// Note there are many different types of Analyzer that may be used with Lucene, the exact one you use
// will depend on your requirements
private Analyzer analyzer = new WhitespaceAnalyzer();
private Directory luceneIndexDirectory;
private IndexWriter writer;
private string indexPath = @"c:\temp\LuceneIndex";
public LuceneService()
{
InitialiseLucene();
}
....
....
public IEnumerable<SampleDataFileRow> 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<SampleDataFileRow> results = new List<SampleDataFileRow>();
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();
}
}
说实话,这没什么复杂的,我认为代码已经解释了你需要知道的一切。
Lucene GUI
还有一个用于检查你存储的 Lucene 数据的非常酷的 GUI,它被称为 "Luke.NET",可以在 codeplex 上免费获取,链接如下:
http://luke.codeplex.com/releases/view/82033
运行此工具时,你需要输入 Lucene 索引创建的索引目录的路径。对于这个演示应用,它是:
C:\Temp\LuceneIndex
输入后,点击 "Ok",你将看到一个 UI,允许你检查 Lucene 存储的所有索引数据,如果你愿意,还可以运行搜索。
这是一个很好的工具,值得一看。
就这些
总之,我目前就说这么多。我还有几篇文章写好了,只是需要整理一下,最近我一直很难找到时间。我想我会抽时间完成的。总之,一如既往,如果你喜欢这篇文章,非常欢迎投票/评论。