天天看點

Lucene.net 實作全文搜尋

Lucene.net 實作全文搜尋

忙了幾天終于實作一個簡單的全文搜尋在此回顧總結一下

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

本文介紹一下Lucene.Net 是什麼?Lucene.Net 能作什麼?以及怎麼做的問題?最後給出 Lucene.Net 實作全文搜尋的一個示例

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

1、Lucene.Net 是什麼?

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Lucene.net 起初是一個開源項目然後轉向商業化,也在Lucene.net 2.0已經釋出,不過是要money D ,Lucene.net的命運有點類似于FreeTextBox ,它在 1.6.5 版本之後釋出的 2.0 開始了商業路線,2.0 提供了 DLL 方式的免費版本,源代碼版本則必須購買商業的許可 licence;不過它留下了 1.6.5 版本的源代碼,還是可以看到大部分的内部細節,但 2.0 版本中添加的對 Mozilla 浏覽器的支援部分隻有通過它生成的 HTML 和 JavaScript 腳本去窺測。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Lucene 是 Java 世界中常用的索引 API,使用它提供的方法可以為文本資料建立索引,并提供檢索。(參考:NLucene 和 Lucene .NET)NLucene 是第一個的 .net 移植,也是一個有 .net 風格的版本,使用 .net 的命名規範和類庫設計。不過 NLucene 項目的 leader 由于精力原因,隻釋出了 1.2beta 版本。Lucene.NET 項目出現後,NLucene 就沒有新的計劃了。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Lucene.NET 當初号稱要做 up-to-date 的 .net Lucene 移植,它隻在命名方面采納了 .net 的建議,主要目标傾向于和 Java Lucene 相容:一個是索引格式相容,達到可以共同工作的目的;一個是命名接近(隻相差很少,比如大小寫等),目的是可以友善開發者使用 Java Lucene 相關的代碼和資料。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

不知什麼時候 Lucene.NET 項目已經放棄了開源計劃,轉向了商業。它居然把 SourceForge 上已經開源的檔案也删除了。與此同時,SourceForge 上又出現了 dotLucene 項目,出于對 Lucene.NET 的抗議,dotLucene 幾乎将 Lucene.NET 的代碼原封不動放在上面作為他們的起點。(https://sourceforge.net/forum/forum.php?thread_id=1153933&forum_id=408004)。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

說白了Lucene.Net就是是一個資訊檢索的函數庫(Library),利用它你可以為你的應用加上索引和搜尋的功能. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Lucene的使用者不必深入了解有關全文檢索的知識,僅僅學會使用庫中的幾個類,知道怎麼調用Library中的函數,就可以為你的應用實作全文檢索的功能. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

不過千萬别期望Lucene是一個象google和百度那樣的搜尋引擎,它僅僅是一個工具,一個Library.你也可以把它了解為一個将索引,搜尋功能封裝的很好的一套簡單易用的API.利用這套API你可以做很多有關搜尋的事情,而且很友善,它可以滿足你對一個應用做簡單的全文搜尋,作為應用的開發者(非專業搜尋引擎開發者)來說,它的功能足以滿足你。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

2、Lucene.Net 可以作什麼?

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Lucene可以對任何的資料做索引和搜尋. Lucene不管資料源是什麼格式,隻要它能被轉化為文字的形式,就可以被Lucene所分析利用.也就是說不管是MS word, Html ,pdf還是其他什麼形式的檔案隻要你可以從中抽取出文字形式的内容就可以被Lucene所用.你就可以用Lucene對它們進行索引以及搜尋.

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

3、使用 Lucene.Net 怎麼做?

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

簡單的歸結為:建立索引,和使用索引,其中建立索引就是将要搜尋的資料源的那些資訊作為我們的關鍵資訊來存儲或者是分析,為搜尋留下标記就象Word裡面建立目錄(個人了解),使用索引就是在搜尋的時候根據索引的資訊來分析資料源将我們需要的資訊提取出來。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

具體請看一下示例:

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

建立索引的類

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

public class IntranetIndexer

Lucene.net 實作全文搜尋

    {

Lucene.net 實作全文搜尋

        /**/////索引寫入器

Lucene.net 實作全文搜尋

        private IndexWriter writer;

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        //要寫入索引的檔案的根目錄

Lucene.net 實作全文搜尋

        private string docRootDirectory;

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        //要比對的檔案格式

Lucene.net 實作全文搜尋

        private string[] pattern;

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /**//// <summary>

Lucene.net 實作全文搜尋

        /// 初始化一個索引寫入器writer,directory為建立索引的目錄,true代表如果不存在索引檔案将重新建立索引檔案,如果已經存在索引檔案将覆寫索引檔案

Lucene.net 實作全文搜尋

        /// </summary>

Lucene.net 實作全文搜尋

        /// <param name="directory">傳入的要建立索引的目錄,注意是字元串值,如果目錄不存在,他将會被自動建立</param>

Lucene.net 實作全文搜尋

        public IntranetIndexer(string directory)

Lucene.net 實作全文搜尋

        {

Lucene.net 實作全文搜尋

            writer = new IndexWriter(directory, new StandardAnalyzer(), true);

Lucene.net 實作全文搜尋

            writer.SetUseCompoundFile(true);

Lucene.net 實作全文搜尋

        }

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        public void AddDirectory(DirectoryInfo directory, string [] pattern)

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            this.docRootDirectory = directory.FullName;

Lucene.net 實作全文搜尋

            this.pattern = pattern;

Lucene.net 實作全文搜尋

            addSubDirectory(directory);

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        private void addSubDirectory(DirectoryInfo directory)

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            for(int i=0;i<pattern .Length ;i++)

Lucene.net 實作全文搜尋

            {

Lucene.net 實作全文搜尋

                foreach (FileInfo fi in directory.GetFiles(pattern[i]))

Lucene.net 實作全文搜尋

                {

Lucene.net 實作全文搜尋

                    AddHtmlDocument(fi.FullName);

Lucene.net 實作全文搜尋

                }

Lucene.net 實作全文搜尋

            }

Lucene.net 實作全文搜尋

            foreach (DirectoryInfo di in directory.GetDirectories())

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                addSubDirectory(di);

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        public void AddHtmlDocument(string path)

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            string exname=Path.GetExtension (path);

Lucene.net 實作全文搜尋

            Document doc = new Document();    

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            string html;            

Lucene.net 實作全文搜尋

            if(exname.ToLower ()==".html" ||exname .ToLower ()==".htm"||exname .ToLower ()==".txt")

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                using(StreamReader sr=new StreamReader (path,System .Text .Encoding .Default ))

Lucene.net 實作全文搜尋

                { 

Lucene.net 實作全文搜尋

                     html = sr.ReadToEnd();

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            else

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Unicode  ))

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                    html = sr.ReadToEnd();

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            int relativePathStartsAt = this.docRootDirectory.EndsWith("\\") ? this.docRootDirectory.Length : this.docRootDirectory.Length + 1;

Lucene.net 實作全文搜尋

            string relativePath = path.Substring(relativePathStartsAt);

Lucene.net 實作全文搜尋

            string title=Path.GetFileName(path);

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            //判斷若是網頁則去标簽否則不用

Lucene.net 實作全文搜尋

            if(exname.ToLower ()==".html" ||exname .ToLower ()==".htm")

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                doc.Add(Field.UnStored("text", parseHtml(html)));

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

                doc.Add (Field .UnStored ("text",html));

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            doc.Add(Field.Keyword("path", relativePath));

Lucene.net 實作全文搜尋

            //doc.Add(Field.Text("title", getTitle(html)));

Lucene.net 實作全文搜尋

            doc.Add (Field .Text ("title",title));

Lucene.net 實作全文搜尋

            writer.AddDocument(doc);

Lucene.net 實作全文搜尋

        }  

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /// 去除網頁中的标簽

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /// <param name="html">網頁</param>

Lucene.net 實作全文搜尋

        /// <returns>傳回去除後的網頁文本</returns>

Lucene.net 實作全文搜尋

        private string parseHtml(string html)

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            string temp = Regex.Replace(html, "<[^>]*>", "");

Lucene.net 實作全文搜尋

            return temp.Replace("&nbsp;", " ");

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /// 擷取網頁标題

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /// <param name="html"></param>

Lucene.net 實作全文搜尋

        /// <returns></returns>

Lucene.net 實作全文搜尋

        private string getTitle(string html)

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            Match m = Regex.Match(html, "<title>(.*)</title>");

Lucene.net 實作全文搜尋

            if (m.Groups.Count == 2)

Lucene.net 實作全文搜尋

                return m.Groups[1].Value;

Lucene.net 實作全文搜尋

            return "文檔标題未知";

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        /// 優化索引并關閉寫入器

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

        public void Close()

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

            writer.Optimize();

Lucene.net 實作全文搜尋

            writer.Close();

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    } 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

首先建立Document對象,然後為Document對象添加一些屬性Field.你可以把Document對象看成是虛拟檔案,将來将從此擷取資訊.而Field則看成是描述此虛拟檔案的中繼資料(metadata).其中Field包括四個類型: Keywork 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

 該類型的資料将不被分析,而會被索引并儲存儲存在索引中. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

UnIndexed 

Lucene.net 實作全文搜尋

 該類型的資料不會被分析也不會被索引,但是會儲存在索引. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

UnStored 

Lucene.net 實作全文搜尋

 和UnIndexed剛好相反,被分析被索引,但是不被儲存. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

Text 

Lucene.net 實作全文搜尋

 和UnStrored類似.如果值的類型為string還會被儲存.如果值的類型為Reader就不會被儲存和UnStored一樣. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

最後将每一個Document添加到索引當中。

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

下面是對索引進行搜尋 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

//建立一個索引器

Lucene.net 實作全文搜尋

   IndexSearcher searcher = new IndexSearcher(indexDirectory); 

Lucene.net 實作全文搜尋

   //解析索引的text字段以便搜尋

Lucene.net 實作全文搜尋

   Query query = QueryParser.Parse(this.Q, "text", new StandardAnalyzer());  

Lucene.net 實作全文搜尋

   //将搜尋結果放在hits中

Lucene.net 實作全文搜尋

   Hits hits = searcher.Search(query);   

Lucene.net 實作全文搜尋

   //統計搜尋的總記錄數

Lucene.net 實作全文搜尋

   this.total = hits.Length();   

Lucene.net 實作全文搜尋

   //高亮顯示

Lucene.net 實作全文搜尋

   QueryHighlightExtractor highlighter = new QueryHighlightExtractor(query, new StandardAnalyzer(), "<font color=red>", "</font>");   

Lucene.net 實作全文搜尋

第一步利用IndexSearcher打開索引檔案用于後面搜尋,其中的參數是索引檔案的路徑.

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

第二步使用QueryParser将可讀性較好的查詢語句(比如查詢的詞lucene ,以及一些進階方式lucene AND .net)轉化為Lucene内部使用的查詢對象.

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

第三步執行搜尋.并将結果傳回到hits集合.需要注意的是Lucene并不是一次将所有的結果放入hits中而是采取一次放一部分的方式.出于空間考慮. 

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

然後将搜尋的結果進行處理并在頁面上顯示出來:

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

for (int i = startAt; i < resultsCount; i++) 

Lucene.net 實作全文搜尋

   {

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    Document doc = hits.Doc(i);   

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    string path = doc.Get("path");   

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    string location =Server.MapPath("documents")+"\\"+path;

Lucene.net 實作全文搜尋

                string exname=Path.GetExtension (path);

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    string plainText ;

Lucene.net 實作全文搜尋

    string str=doc.Get ("title");

Lucene.net 實作全文搜尋

    if(exname==".html" || exname ==".htm" || exname ==".txt")

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

     using (StreamReader sr = new StreamReader(location, System.Text.Encoding.Default))

Lucene.net 實作全文搜尋

     {

Lucene.net 實作全文搜尋

      plainText = parseHtml(sr.ReadToEnd());

Lucene.net 實作全文搜尋

     }

Lucene.net 實作全文搜尋

    }

Lucene.net 實作全文搜尋

    else

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

     using (StreamReader sr = new StreamReader(location, System.Text.Encoding.Unicode ))

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

      plainText = sr.ReadToEnd();

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

    //DataTable 添加行 

Lucene.net 實作全文搜尋

    DataRow row = this.Results.NewRow();

Lucene.net 實作全文搜尋

    row["title"] = doc.Get("title");

Lucene.net 實作全文搜尋

    string IP=Request.Url.Host;//擷取伺服器IP

Lucene.net 實作全文搜尋

    //Request.Url.Port;     

Lucene.net 實作全文搜尋

    row["path"]=@"http://"+IP+"/WebUI/Search/documents/"+path;

Lucene.net 實作全文搜尋

    row["sample"] = highlighter.GetBestFragments(plainText, 80, 2, "

Lucene.net 實作全文搜尋

");   

Lucene.net 實作全文搜尋

    this.Results.Rows.Add(row);

Lucene.net 實作全文搜尋

   } 

Lucene.net 實作全文搜尋

   searcher.Close();//關閉搜尋器

Lucene.net 實作全文搜尋

想對Lucene.Net 進行更進階,更全面,更深層次了解的請參閱一下網站:

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

http://www.alphatom.com/

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

http://blog.tianya.cn/blogger/view_blog.asp?BlogName=aftaft

Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋
Lucene.net 實作全文搜尋

繼續閱讀