在建立索引前,先了解下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;
        }
    }
}