Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。
Lucene的作者:Lucene的贡献者Doug Cutting是一位资深全文索引/检索专家,曾经是V-Twin搜索引擎(Apple的Copland操作系统的成就之一)的主要开发者,后在Excite担任高级系统架构设计师,目前从事于一些INTERNET底层架构的研究。他贡献出的Lucene的目标是为各种中小型应用程序加入全文检索功能。
Lucene的发展历程:早先发布在作者自己的www.lucene.com,后来发布在SourceForge,2001年年底成为APACHE基金会jakarta的一个子项目:http://jakarta.apache.org/lucene/ 已经有很多Java项目都使用了Lucene作为其后台的全文索引引擎。
现在也有很多用.NET做的Lucene,其索引/检索的效果还是不错的。
近期由于做项目的需要,也对Lucene进行了研究,小有一些心得。
其关键步骤为:
一、建立索引
从源数据文件夹中取出所有要进行索引的文件,循环为每个文件建立索引:
注意DLL的引用(using StandardAnalyzer = Lucene.Net.Analysis.Standard.StandardAnalyzer;
using IndexWriter = Lucene.Net.Index.IndexWriter;
using Lucene.Net.Demo;)
IndexWriter writer = new IndexWriter(indexPath, new StandardAnalyzer(), true);//index indexPath 为存放索引文件的路径
IndexDocs(writer, new System.IO.FileInfo(sourcePath)); //索引方法 sourcePath 为存放源数据文件的路径
下面是实现索引的方法:
#region Index Documets
public void IndexDocs(IndexWriter writer, System.IO.FileInfo file)
{
if (System.IO.Directory.Exists(file.FullName))
{
System.String[] files = System.IO.Directory.GetFileSystemEntries(file.FullName);
// an IO error could occur
if (files != null)
{
for (int i = 0; i < files.Length; i++)
{
IndexDocs(writer, new System.IO.FileInfo(files[i]));
}
}
}
else
{
try
{
writer.AddDocument(FileDocument.Document(file));
index++;
this.listBoxControl.Items.Add(file.Name+" "+DateTime.Now.ToString());//recorder successful file
}
// at least on windows, some temporary files raise this exception with an "access denied" message
// checking if the file can be read doesn't help
catch (System.IO.FileNotFoundException fnfe)
{
MessageBox.Show((fnfe.GetType()+fnfe.Message),"错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
}
}
#endregion
在这里要注意的是,由于需求的原因我对源数据文件加过密,所以我把索引的具休法中添加了解密方法。也就是说,在索引文件的时候首先要把源加密文件解密后再进行索引,这样就要以把真正没加密前的信息加到索引里面了。(当然在查到相应文件查看时,还要对文件进行解密的)
修改类FileDocument中的函数:
public static Document Document(System.IO.FileInfo f)
{
// make a new, empty document
Document doc = new Document();
// Add the path of the file as a Field named "path". Use a Text Field, so
// that the index stores the path, and so that the path is searchable
doc.Add(Field.Text("path", f.FullName));
// Add the last modified date of the file a Field named "modified". Use a
// Keyword Field, so that it's searchable, but so that no attempt is made
// to tokenize the Field into words.
doc.Add(Field.Keyword("modified", DateField.TimeToString(((f.LastWriteTime.Ticks - 621355968000000000) / 10000))));
// Add the contents of the file a Field named "contents". Use a Text
// Field, specifying a Reader, so that the text of the file is tokenized.
// ?? why doesn't FileReader work here ??
//read file from source
FileStream fs = new FileStream(f.FullName,FileMode.Open);
StreamReader sr = new StreamReader(fs,Encoding.Default);
string filetemp = sr.ReadToEnd();
sr.Close();
fs.Close();
//open pass //此处为自己写的加密/解密方法
Cryptography op = new Cryptography();
filetemp = op.DesDecrypt(filetemp,"xxxxxxxxx");
// Save a copy
string tempFile = @"c:/temp.htm";
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
try
{
using (StreamWriter sw = new StreamWriter(tempFile,false,Encoding.UTF8))
{
sw.Write(filetemp);
}
}
catch
{
throw new IOException();
}
System.IO.FileStream is_Renamed = new System.IO.FileStream(tempFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
System.IO.StreamReader reader = new System.IO.StreamReader(new System.IO.StreamReader(is_Renamed, System.Text.Encoding.Default).BaseStream, new System.IO.StreamReader(is_Renamed, System.Text.Encoding.Default).CurrentEncoding);
doc.Add(Field.Text("contents", reader));
// return the document
return doc;
}
此处是建了临时文件,因为法在执行时一直在使用reader,所以不能在当前这个方法为执行删除临时文件,我把它加到了调用索引方法的主函数中。(也就是说要在所有索引建完之后去删除昨临时文件)到此索引就告一段落。
二、文件检索
在这里文件检索就相对比较简单一些,直接利用建好的索引文件进行检索就可以了。同时也要注意命名空间的引用:using Analyzer = Lucene.Net.Analysis.Analyzer;
using StandardAnalyzer = Lucene.Net.Analysis.Standard.StandardAnalyzer;
using Document = Lucene.Net.Documents.Document;
using QueryParser = Lucene.Net.QueryParsers.QueryParser;
using Hits = Lucene.Net.Search.Hits;
using IndexSearcher = Lucene.Net.Search.IndexSearcher;
using Query = Lucene.Net.Search.Query;
using Searcher = Lucene.Net.Search.Searcher;
下面为具体的检索方法:
private void Search()
{
try
{
DateTime starttime = DateTime.Now; //begin analyse file
string analysepath = Application.StartupPath+"//"+analyseConfigPath; //index file config xml path
string analysefolder = xo.ReadFileConfig(analysepath); //index file folder path
string indexPath = Application.StartupPath+"//"+analysefolder; //index folder path
Searcher searcher = new IndexSearcher(indexPath);
Analyzer analyzer = new StandardAnalyzer();
System.String line = this.txtWord.Text.Trim();
if(listSearchResult.Items.Count > 0)
listSearchResult.Items.Clear();
if (line.Length == 0)
return;;
Query query = QueryParser.Parse(line, "contents", analyzer);
this.listSearchResult.Items.Add("查找关键词: " + query.ToString("contents"));
Hits hits = searcher.Search(query);
this.listSearchResult.Items.Add("找到相关文件约 "+hits.Length()+" 篇");
int HITS_PER_PAGE = 10;
for (int start = 0; start < hits.Length(); start += HITS_PER_PAGE)
{
int end = System.Math.Min(hits.Length(), start + HITS_PER_PAGE);
for (int i = start; i < end; i++)
{
Document doc = hits.Doc(i);
System.String path = doc.Get("path");
if (path != null)
{
this.listSearchResult.Items.Add(i + ". " + path);
}
else
{
System.String url = doc.Get("url");
if (url != null)
{
this.listSearchResult.Items.Add(i + ". " + url);
this.listSearchResult.Items.Add(" - " + doc.Get("title"));
}
else
{
this.listSearchResult.Items.Add(i + ". " + "No path nor URL for this document");
}
}
}
}
searcher.Close();
DateTime endtime = System.DateTime.Now;
TimeSpan time = endtime - starttime;
double timestr = time.TotalSeconds;
this.laFileNum.Text = "找到相关文件约"+hits.Length().ToString()+"篇,用时"+timestr.ToString()+"秒 ";
}
catch //(System.Exception ecp)
{
// MessageBox.Show((ecp.GetType()+ecp.Message),"错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
// return;
}
}
可以把检索到的文件以列表的形式显示出来,选择自己需要的文件进行查看。下面的处理可以根据自己的具休情况来处理。
通过以上的应用,可以看到,其实使用lucene很简单。因其代码是开源的可以下载下来对其进一步扩展加工。
相关链接:
作者:jinru2560@gmail.com
QQ: 55854548