基于Lucene的文件检索

    技术2022-05-11  102

         基于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


    最新回复(0)