基于Lucene的文件检索

     基于Java的全文索引/检索引擎——Lucene

      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很简单。因其代码是开源的可以下载下来对其进一步扩展加工。

    相关链接:

         1、 http://www.dotlucene.net/

         2、http://sourceforge.net

   作者:jinru2560@gmail.com
    QQ: 55854548

阅读更多
个人分类: Visual C#
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭