在建立索引前,先了解下lucene中的一些與索引相關的重要類。
為了對文檔進行索引,Lucene 提供了五個基礎的類,他們分別是 Document, Field, IndexWriter, Analyzer, Directory。下面我們分別介紹一下這五個類的用途:
Document
Document 是用來描述文檔的,這裏的文檔可以指一個 HTML 頁面,一封電子郵件,或者是一個文本文件。一個 Document 對象由多個 Field 對象組成的。可以把一個 Document 對象想象成數據庫中的一個記錄,而每個 Field 對象就是記錄的一個字段。在建立索引的時候,也就是實例化一個索引器IndexWriter的之前,必須通過已經建立好的Document邏輯文件,將 Document的對象添加到IndexWriter實例中,才能算是建立索引。
Field
Field 對象是用來描述一個文檔的某個屬性的,比如一封電子郵件的標題和內容可以用兩個 Field 對象分別描述。
Document彙集數據源,這個數據源是通過Field來構造的。構造好Field之後,將每個Field對象加入到Document之中,可以通過Document來管理Field,然後將聚集的Document加入到IndexWriter中,建立索引,寫入指定的Directory,為檢索做準備。
Analyzer
在一個文檔被索引之前,首先需要對文檔內容進行分詞處理,這部分工作就是由 Analyzer 來做的。Analyzer 類是一個抽象類,它有多個實現。針對不同的語言和應用需要選擇適合的 Analyzer。Analyzer 把分詞後的內容交給 IndexWriter 來建立索引。
IndexWriter
IndexWriter 是 Lucene 用來創建索引的一個核心的類,他的作用是把一個個的 Document 對象加到索引中來。建立索引必須從它開始。而且,從它的構造函數開始。
Directory
這個類代表了 Lucene 的索引的存儲的位置,這是一個抽象類,它目前有兩個實現,第一個是 FSDirectory,它表示一個存儲在文件系統中的索引的位置。第二個是 RAMDirectory,它表示一個存儲在內存當中的索引的位置
Field函數:public Field(string name, string value_Renamed, Field.Store store, Field.Index index)
Field函數 第一個參數是字段名稱,第二個參數是字段內容,第三個參數是存儲類型,第四個參數是索引類型
Field.Store: 有三個屬性:
Field.Store.YES:索引文件本來只存儲索引數據, 此設計將原文內容直接也存儲在索引文件中,如文檔的標題。
Field.Store.NO:原文不存儲在索引文件中,搜索結果命中後,再根據其他附加屬性如文件的Path,數據庫的主鍵等,重新連接打開原文,適合原文內容較大的情況。
Field.Store.COMPRESS 壓縮存儲;
Field.Index:有四個屬性:
Field.Index.TOKENIZED:分詞索引
Field.Index.UN_TOKENIZED:進行索引,但不對其進行分詞,如作者名,日期等,Rod Johnson本身為一單詞,不再需要分詞。
Field.Index.NO 和 Field.Index.NO_NORMS:
不進行索引,存放不能被搜索的內容如文檔的一些附加屬性如文檔類型, URL等。
/*
* 建立一個Form,命名為DemoIndex,添加四個控件:
* Button按鈕命名button1,添加單擊事件
* RichTextBox控件,命名richTextBox1
* StatusBar,命名statusBar1
* FolderBrowserDialog,命名folderBrowserDialog1
* 分詞類使用中文分詞類庫ChineseAnalyzer,也可以使用Lucene自帶的類庫
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Analysis.Cn;
using System.IO;
namespace WebCheck
{
public partial class DemoIndex : Form
{
public DemoIndex()
{
InitializeComponent();
}
const string INDEX_STORE_PATH = "index";
DateTime start = DateTime.Now;
private void button1_Click(object sender, EventArgs e)
{
DialogResult result = folderBrowserDialog1.ShowDialog();
if (result == DialogResult.OK)
{
IndexWriter writer = null;
try
{
///INDEX_STORE_PATH索引所在目錄,ChineseAnalyzer()分詞類,true是新建索引,flase追加索引。
writer = new IndexWriter(INDEX_STORE_PATH, new ChineseAnalyzer(), true);
writer.SetMaxFieldLength(250000);//設置建立索引的長度,就是對數據的前多少條建立索引
writer.SetMaxBufferedDocs(100);//控制寫入一個新的segment前內存中保存的document的數目,設置較大的數目可以加快建索引速度
writer.SetMaxMergeDocs(100);// 控制一個segment中可以保存的最大document數目,值較小有利於追加索引的速度
writer.SetMergeFactor(100);// 控制多個segment合併的頻率,值較大時建立索引速度較快,默認是10
output("開始建立索引..../n");
start = DateTime.Now;
#region 同步索引
//IndexDirectory(writer,new FileInfo(this.folderBrowserDialog1.SelectedPath));
//如果是同步索引的話,調用優化索引的函數可以對索引優化,如果是異步就不行了。
//異步的話,最好把writer也當做參數傳遞給回調函數裏,在回調函數裏優化,這裏
//傳遞了一個時間參數進去,你可以傳遞一個包含start和writer的自定義對象進去。
//為了讓示例簡單我沒有這個做。
//writer.Optimize();
//writer.Close();
#endregion
#region 異步索引
// BeginInvoke 方法可啓動異步調用。
//它與您需要異步執行的方法具有相同的參數,另外它還有兩個可選參數。
//第一個參數是一個 AsyncCallback 委託,該委託引用在異步調用完成時要調用的方法。
//第二個參數是一個用户定義的對象,該對象可向回調方法傳遞信息。
//BeginInvoke 立即返回,不等待異步調用完成。BeginInvoke 會返回 IAsyncResult,這個結果可用於監視異步調用進度。
//結果對象IAsyncResult是從開始操作返回的,並且可用於獲取有關異步開始操作是否已完成的狀態。
//結果對象被傳遞到結束操作,該操作返回調用的最終返回值。
//在開始操作中可以提供可選的回調。如果提供回調,在調用結束後,將調用該回調;並且回調中的代碼可以調用結束操作。
CheckForIllegalCrossThreadCalls = false;
AsyncIndexDirectoryCaller caller = new AsyncIndexDirectoryCaller(IndexDirectory);
IAsyncResult ar = caller.BeginInvoke(writer, new FileInfo(this.folderBrowserDialog1.SelectedPath), new AsyncCallback(searchCallback), writer);
caller.EndInvoke(ar);
this.statusBar1.Text = "準備";
#endregion
}
catch (Exception ex)
{
output(ex.Message);
}
}
}
delegate void AsyncIndexDirectoryCaller(IndexWriter writer, FileInfo file);//委託
public void IndexDirectory(IndexWriter writer, FileInfo file)
{
if (Directory.Exists(file.FullName))//如果file是一個目錄(該目錄下面可能有文件、目錄文件、空文件三種情況)
{
String[] files = Directory.GetFileSystemEntries(file.FullName);//獲取此目錄下子目錄和文件集合
if (files != null)
{
for (int i = 0; i < files.Length; i++)
{
IndexDirectory(writer, new FileInfo(files[i])); //這裏是一個遞歸
}
}
}
else // 到達葉節點時,説明是一個File,而不是目錄,則建立索引
{
if (file.Extension == ".txt" || file.Extension == ".htm" || file.Extension == ".html")//指定為特定的頁面建立索引
{
IndexFile(file, writer);
}
}
}
private void IndexFile(FileInfo file, IndexWriter writer)
{
try
{
Document doc = new Document();
output("正在建立索引" + file.FullName);
//Field函數 第一個參數是字段名稱,第二個參數是字段內容,第三個參數是存儲類型,第四個參數是索引類型
//Field.Store: 有三個屬性:
//Field.Store.YES:索引文件本來只存儲索引數據, 此設計將原文內容直接也存儲在索引文件中,如文檔的標題。
//Field.Store.NO:原文不存儲在索引文件中,搜索結果命中後,再根據其他附加屬性如文件的Path,數據庫的主鍵等,重新連接打開原文,適合原文內容較大的情況。
//Field.Store.COMPRESS 壓縮存儲;
//Field.Index:有四個屬性:
//Field.Index.TOKENIZED:分詞索引
//Field.Index.UN_TOKENIZED:不分詞進行索引,如作者名,日期等,Rod Johnson本身為一單詞,不再需要分詞。
//Field.Index.NO 和 Field.Index.NO_NORMS:
//不進行索引,存放不能被搜索的內容如文檔的一些附加屬性如文檔類型, URL等。
doc.Add(new Field("filename", file.FullName, Field.Store.YES, Field.Index.NO_NORMS));
//這裏一定要設置響應的編碼格式,否則建立索引的時候不能正確讀取內容並分詞
doc.Add(new Field("contents", GetContent(file), Field.Store.NO, Field.Index.UN_TOKENIZED));
writer.AddDocument(doc);
}
catch (FileNotFoundException fnfe)
{
output(fnfe.Message);
}
}
/// <summary>
///
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private string GetContent(FileInfo file)
{
string content = "";
StreamReader filestream = new StreamReader(file.FullName, System.Text.Encoding.Default);
content = filestream.ReadToEnd();
filestream.Close();
if (file.Extension == ".htm" || file.Extension == ".html")
{
content = parseHtml(content);
}
return content;
}
/// <summary>
/// 出去網頁中的html字符
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
private string parseHtml(string html)
{
string temp = System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", "");
return temp.Replace(" ", " ");
}
void searchCallback(IAsyncResult ar)
{
IndexWriter writer = (IndexWriter)ar.AsyncState;
writer.Optimize();//Optimize()方法是對索引進行優化
writer.Close();
TimeSpan s = DateTime.Now - start;
output("索引完成,共用時" + s.Milliseconds + "毫秒");
MessageBox.Show("索引完成,共用時" + s.Milliseconds + "毫秒", "提示");
}
void output(string s)
{
richTextBox1.AppendText(s + "/n");
this.statusBar1.Text = s;
}
}
}