忙了幾天終于實現一個簡單的全文搜索在此回顧總結一下
本文介紹一下Lucene.Net 是什么?Lucene.Net 能作什么?以及怎么做的問題?最后給出 Lucene.Net 實現全文搜索的一個示例
1、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 是 Java 世界中常用的索引 API,使用它提供的方法可以為文本資料創建索引,并提供檢索。(參考:NLucene 和 Lucene .NET)NLucene 是第一個的 .net 移植,也是一個有 .net 風格的版本,使用 .net 的命名規范和類庫設計。不過 NLucene 項目的 leader 由于精力原因,只發布了 1.2beta 版本。Lucene.NET 項目出現后,NLucene 就沒有新的計劃了。
Lucene.NET 當初號稱要做 up-to-date 的 .net Lucene 移植,它只在命名方面采納了 .net 的建議,主要目標傾向于和 Java Lucene 兼容:一個是索引格式兼容,達到可以共同工作的目的;一個是命名接近(只相差很少,比如大小寫等),目的是可以方便開發者使用 Java Lucene 相關的代碼和資料。
不知什么時候 Lucene.NET 項目已經放棄了開源計劃,轉向了商業。它居然把 SourceForge 上已經開源的文件也刪除了。與此同時,SourceForge 上又出現了 dotLucene 項目,出于對 Lucene.NET 的抗議,dotLucene 幾乎將 Lucene.NET 的代碼原封不動放在上面作為他們的起點。(https://sourceforge.net/forum/forum.php?thread_id=1153933&forum_id=408004)。
說白了Lucene.Net就是是一個信息檢索的函數庫(Library),利用它你可以為你的應用加上索引和搜索的功能.
Lucene的使用者不必深入了解有關全文檢索的知識,僅僅學會使用庫中的幾個類,知道怎么調用Library中的函數,就可以為你的應用實現全文檢索的功能.
不過千萬別期望Lucene是一個象google和百度那樣的搜索引擎,它僅僅是一個工具,一個Library.你也可以把它理解為一個將索引,搜索功能封裝的很好的一套簡單易用的API.利用這套API你可以做很多有關搜索的事情,而且很方便,它可以滿足你對一個應用做簡單的全文搜索,作為應用的開發者(非專業搜索引擎開發者)來說,它的功能足以滿足你。
2、Lucene.Net 可以作什么?
Lucene可以對任何的數據做索引和搜索. Lucene不管數據源是什么格式,只要它能被轉化為文字的形式,就可以被Lucene所分析利用.也就是說不管是MS Word, Html ,pdf還是其他什么形式的文件只要你可以從中抽取出文字形式的內容就可以被Lucene所用.你就可以用Lucene對它們進行索引以及搜索.
3、使用 Lucene.Net 怎么做?
簡單的歸結為:創建索引,和使用索引,其中創建索引就是將要搜索的數據源的那些信息作為我們的關鍵信息來存儲或者是分析,為搜索留下標記就象Word里面創建目錄(個人理解),使用索引就是在搜索的時候根據索引的信息來分析數據源將我們需要的信息提取出來。
具體請看一下示例:
創建索引的類
public class IntranetIndexer
{
/**/////索引寫入器
PRivate IndexWriter writer;
//要寫入索引的文件的根目錄
private string docRootDirectory;
//要匹配的文件格式
private string[] pattern;
/**//// <summary>
/// 初始化一個索引寫入器writer,directory為創建索引的目錄,true代表如果不存在索引文件將重新創建索引文件,如果已經存在索引文件將覆寫索引文件
/// </summary>
/// <param name="directory">傳入的要創建索引的目錄,注意是字符串值,如果目錄不存在,他將會被自動創建</param>
public IntranetIndexer(string directory)
{
writer = new IndexWriter(directory, new StandardAnalyzer(), true);
writer.SetUseCompoundFile(true);
}
public void AddDirectory(DirectoryInfo directory, string [] pattern)
{
this.docRootDirectory = directory.FullName;
this.pattern = pattern;
addSubDirectory(directory);
}
private void addSubDirectory(DirectoryInfo directory)
{
for(int i=0;i<pattern .Length ;i++)
{
foreach (FileInfo fi in directory.GetFiles(pattern[i]))
{
AdDHTMLDocument(fi.FullName);
}
}
foreach (DirectoryInfo di in directory.GetDirectories())
{
addSubDirectory(di);
}
}
public void AddHtmlDocument(string path)
{
string exname=Path.GetExtension (path);
Document doc = new Document();
string html;
if(exname.ToLower ()==".html" ||exname .ToLower ()==".htm"||exname .ToLower ()==".txt")
{
using(StreamReader sr=new StreamReader (path,System .Text .Encoding .Default ))
{
html = sr.ReadToEnd();
}
}
else
{
using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Unicode ))
{
html = sr.ReadToEnd();
}
}
int relativePathStartsAt = this.docRootDirectory.EndsWith("http://") ? this.docRootDirectory.Length : this.docRootDirectory.Length + 1;
string relativePath = path.Substring(relativePathStartsAt);
string title=Path.GetFileName(path);
//判斷若是網頁則去標簽否則不用
if(exname.ToLower ()==".html" ||exname .ToLower ()==".htm")
{
doc.Add(Field.UnStored("text", parseHtml(html)));
}
else
{
doc.Add (Field .UnStored ("text",html));
}
doc.Add(Field.Keyword("path", relativePath));
//doc.Add(Field.Text("title", getTitle(html)));
doc.Add (Field .Text ("title",title));
writer.AddDocument(doc);
}
/**//// <summary>
/// 去除網頁中的標簽
/// </summary>
/// <param name="html">網頁</param>
/// <returns>返回去除后的網頁文本</returns>
private string parseHtml(string html)
{
string temp = Regex.Replace(html, "<[^>]*>", "");
return temp.Replace(" ", " ");
}
/**//// <summary>
/// 獲取網頁標題
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
private string getTitle(string html)
{
Match m = Regex.Match(html, "<title>(.*)</title>");
if (m.Groups.Count == 2)
return m.Groups[1].Value;
return "文檔標題未知";
}
/**//// <summary>
/// 優化索引并關閉寫入器
/// </summary>
public void Close()
{
writer.Optimize();
writer.Close();
}
}
首先建立Document對象,然后為Document對象添加一些屬性Field.你可以把Document對象看成是虛擬文件,將來將從此獲取信息.而Field則看成是描述此虛擬文件的元數據(metadata).其中Field包括四個類型: Keywork
該類型的數據將不被分析,而會被索引并保存保存在索引中.
UnIndexed
該類型的數據不會被分析也不會被索引,但是會保存在索引.
UnStored
和UnIndexed剛好相反,被分析被索引,但是不被保存.
Text
和UnStrored類似.如果值的類型為string還會被保存.如果值的類型為Reader就不會被保存和UnStored一樣.
最后將每一個Document添加到索引當中。
下面是對索引進行搜索
//創建一個索引器
IndexSearcher searcher = new IndexSearcher(indexDirectory);
//解析索引的text字段以便搜索
Query query = QueryParser.Parse(this.Q, "text", new StandardAnalyzer());
//將搜索結果放在hits中
Hits hits = searcher.Search(query);
//統計搜索的總記錄數
this.total = hits.Length();
//高亮顯示
QueryHighlightExtractor highlighter = new QueryHighlightExtractor(query, new StandardAnalyzer(), "<font color=red>", "</font>");
第一步利用IndexSearcher打開索引文件用于后面搜索,其中的參數是索引文件的路徑.
第二步使用QueryParser將可讀性較好的查詢語句(比如查詢的詞lucene ,以及一些高級方式lucene AND .net)轉化為Lucene內部使用的查詢對象.
第三步執行搜索.并將結果返回到hits集合.需要注意的是Lucene并不是一次將所有的結果放入hits中而是采取一次放一部分的方式.出于空間考慮.
然后將搜索的結果進行處理并在頁面上顯示出來:
for (int i = startAt; i < resultsCount; i++)
{
Document doc = hits.Doc(i);
string path = doc.Get("path");
string location =Server.MapPath("documents")+"//"+path;
string exname=Path.GetExtension (path);
string plainText ;
string str=doc.Get ("title");
if(exname==".html" || exname ==".htm" || exname ==".txt")
{
using (StreamReader sr = new StreamReader(location, System.Text.Encoding.Default))
{
plainText = parseHtml(sr.ReadToEnd());
}
}
else
{
using (StreamReader sr = new StreamReader(location, System.Text.Encoding.Unicode ))
{
plainText = sr.ReadToEnd();
}
}
//DataTable 添加行
DataRow row = this.Results.NewRow();
row["title"] = doc.Get("title");
string IP=Request.Url.Host;//獲取服務器IP
//Request.Url.Port;
row["path"]=@"http://"+IP+"/WebUI/Search/documents/"+path;
row["sample"] = highlighter.GetBestFragments(plainText, 80, 2, "");
this.Results.Rows.Add(row);
}
searcher.Close();//關閉搜索器
想對Lucene.Net 進行更高級,更全面,更深層次了解的請參閱一下網站:
http://blog.tianya.cn/blogger/view_blog.asp?BlogName=aftaft
新聞熱點
疑難解答