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

小型 Lucene.NET 演示应用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (55投票s)

2013年6月21日

CPOL

6分钟阅读

viewsIcon

172828

downloadIcon

8993

一个小型的演示应用,展示如何使用 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();
    }


    ....
    ....
    ....
    ....
    ....
}

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

  1. 创建新的 Lucene 索引目录。
  2. 创建 Lucene 写入器。
  3. 为我们的源对象创建一个新的 Lucene 文档。
  4. 将字段添加到 Lucene 文档中。
  5. 将 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 存储的所有索引数据,如果你愿意,还可以运行搜索。

这是一个很好的工具,值得一看。

 

就这些

总之,我目前就说这么多。我还有几篇文章写好了,只是需要整理一下,最近我一直很难找到时间。我想我会抽时间完成的。总之,一如既往,如果你喜欢这篇文章,非常欢迎投票/评论。

© . All rights reserved.