本文要介绍的是利用Lucene.NET写的一个简单的文档搜索器。实现最基本的索引和搜索功能。编写这个小工具的初衷之一是因为我收集的专业资料文档越来越多,有时想要在计算机中找到自己想要的文档却不知道自己之前把那些资料放到了什么地方,而且现在的硬盘越来越大,目录结构也越来越复杂,常常要花很多时间去查找自己想要的资料。有了这个搜索工具,只要定期重新建立索引,就可以方便快速地找到自己想要的东西,其次是因为正好这段时间想花时间了解一下Lucene.NET这个开源库学习一下搜索引擎的原理。下面是这个工具的截图:
图一:索引界面
在索引界面可以选择索引保存的位置、添加要索引的目录以及要进行索引的文件类型。这些参数设置好后就可以进行索引了。索引的保存目录一般不用频繁地修改,因为在搜索的时候要引用索引信息,如果频繁地修改索引信息的保存位置,可能会影响搜索结果,导致搜索结果不完整。索引文件类型可以通过菜单添加,不过现在这个工具还比较简单,没有把这些设置数据保存到配置文件中,所以添加了文件类型以后只能在不关闭程序的前提下使用,重新启动工具后又会回到默认状态。当然这些细节都在不断的改进之中,在使用过程中完善吧。索引的过程使用了BackgroundWorker以多线程方式来处理,因为索引处理可能会比较消耗时间,使用多线程可以提高用户体验并且可以提供一个进度条来提示索引进程。另外在加载索引路径的时候因为要搜索所有子目录的文件,这也会是一个比较耗时的操作。因此我在这里使用了泛型委托进行异步方法调用,其目的同样是为了提升用户体验。使界面不至于停滞。下面的代码是索引过程的主要代码:
IndexWriter idxWriter = new IndexWriter( this .toolStripTextBox2.Text, analyzer, true );
StringBuilder sb = new StringBuilder();
int percent = 0 ;
for ( int i = 0 ; i < listBox1.Items.Count; i ++ )
{
string path = listBox1.Items[i].ToString();
string [] files = Directory.GetFiles(path, " *.* " , SearchOption.AllDirectories);
if (files.Length > 0 )
{
for ( int j = 0 ; j < files.Length; j ++ )
{
FileInfo fi = new FileInfo(files[j]);
if (listBox2.Items.Contains(fi.Extension))
{
sb.AppendFormat(files[j] + " 已经被索引

Document doc = new Document();
Field title = new Field( " title " , fi.FullName, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
Field content = new Field( " content " , File.ReadAllText(files[j]), Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
doc.Add(title);
doc.Add(content);
idxWriter.AddDocument(doc);
percent = ( int )(((step + 1.0 ) / totalProgress) * 100 );
if (percent < 0 )
{
percent = 0 ;
}
else if (percent > 100 )
{
percent = 100 ;
}
// 通知Worker更新进度
backgroundWorker1.ReportProgress(percent, sb.ToString());
step ++ ;
}
}
}
}
idxWriter.Optimize();
idxWriter.Close();
我针对每篇文档的标题和内容进行了索引,并且将分析器应用到了标题和内容上。当然如果想要控制索引信息量的大小可以不对标题索引或者减少待索引的文件正文。
图二:搜索界面
图二是进行搜索的界面,这个界面比较简单,只要在上面的文本框中输入关键词回车就开始进行搜索了,搜索的结果会显示在下面的列表框中,每一行就是一条搜索结果。当双击某一条结果的时候会使用适当的应用程序打开文件。下面是搜索功能的主要代码:
Query query = null ;
IndexSearcher searcher = new IndexSearcher(Program.Parameters[ " INDEX_LOCATION " ].ToString());
Analyzer analyzer = new StandardAnalyzer();
try
{
QueryParser qp = new QueryParser( " content " , analyzer);
query = qp.Parse( this .textBox1.Text);
}
catch (Exception)
{
throw ;
}
if (searcher != null )
{
hits = searcher.Search(query);
if (hits.Length() > 0 )
{
lblStatus.Text = " 搜索完毕,搜索结果数量: " + hits.Length().ToString();
}
for ( int i = 0 ; i < hits.Length(); i ++ )
{
Document currDoc = hits.Doc(i);
listBox1.Items.Add( string .Format( " [{0}]:{1}{2} " ,
(i + 1 ).ToString(), currDoc.GetField( " title " ).StringValue().Replace( @" \\ " , @" \ " ), Environment.NewLine));
}
}
现在这个小工具的实现还比较简单,主要就是完成文件索引和搜索并且根据搜索结果直接打开文件进行浏览,因为其中使用的是Lucene.NET的StandardAnalyzer,这个类在分析文档的时候只是简单地通过字词间的空格来分析文本同时也会过滤英文停止词(这个分析器主要还是针对英文处理的)。它在搜索功能方面还有很大的局限。为了提高这个工具的实用性,还需要把StandardAnalyzer更新为可以进行中文分词的分析器,以及其它一些影响实用性的细节。