Lucene.NET 是Lucene搜索引擎库的.NET移植版本,使用C#编写,面向.NET运行时用户。基本概念是,构建一个.NET对象的索引,这些对象存储在一个特殊的Lucene文档中,这些文档具有可搜索的字段。然后,可以运行查询来检索这些存储的文档,并将它们重新水合回.NET对象。
索引构建阶段是将.NET对象创建为每个对象的文档,并添加某些字段(不必为所有.NET对象的属性存储/添加字段,这取决于决定),然后将这些文档保存到磁盘上的物理目录中,稍后将搜索这些文档。
查询数据显然是想要使用Lucene.NET的主要原因之一,它拥有良好的查询设施,这一点应该不会让感到惊讶。Lucene.NET使用的查询语法的一个很好的资源可以在这里找到:
一些简单的例子可能是:
Lucene.NET中有很多不同的分析器类型,例如:
选择正确的分析器取决于想要实现的目标,以及要求。
本节将讨论附带的演示应用程序,并提供足够的信息,以便开始构建自己的Lucene.NET支持的搜索,如果希望在自己的应用程序中使用它。
演示应用程序非常简单,以下是它所做的:
认为最好的例子是看看示例。所以,这是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();
}
}
认为这段代码相当简单易懂,基本上就是这样做的:
如果处理的是大量数据,可能想要创建静态的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();
}
}
说实话,这没什么大不了的,认为代码解释了所有需要知道的东西。