<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-72  評(píng)論-63  文章-0  trackbacks-0

    Lucene 是一個(gè)基于 Java 的全文檢索工具包,你可以利用它來(lái)為你的應(yīng)用程序加入索引和檢索功能。Lucene 目前是著名的 Apache Jakarta 家族中的一個(gè)開(kāi)源項(xiàng)目,下面我們即將學(xué)習(xí) Lucene 的索引機(jī)制以及它的索引文件的結(jié)構(gòu)。

    在這篇文章中,我們首先演示如何使用 Lucene 來(lái)索引文檔,接著討論如何提高索引的性能。最后我們來(lái)分析 Lucene 的索引文件結(jié)構(gòu)。需要記住的是,Lucene 不是一個(gè)完整的應(yīng)用程序,而是一個(gè)信息檢索包,它方便你為你的應(yīng)用程序添加索引和搜索功能。

    架構(gòu)概覽

    圖一顯示了 Lucene 的索引機(jī)制的架構(gòu)。Lucene 使用各種解析器對(duì)各種不同類(lèi)型的文檔進(jìn)行解析。比如對(duì)于 HTML 文檔,HTML 解析器會(huì)做一些預(yù)處理的工作,比如過(guò)濾文檔中的 HTML 標(biāo)簽等等。HTML 解析器的輸出的是文本內(nèi)容,接著 Lucene 的分詞器(Analyzer)從文本內(nèi)容中提取出索引項(xiàng)以及相關(guān)信息,比如索引項(xiàng)的出現(xiàn)頻率。接著 Lucene 的分詞器把這些信息寫(xiě)到索引文件中。


    圖一:Lucene 索引機(jī)制架構(gòu)
    圖一:Lucene 索引機(jī)制架構(gòu)




    回頁(yè)首


    用Lucene索引文檔

    接下來(lái)我將一步一步的來(lái)演示如何利用 Lucene 為你的文檔創(chuàng)建索引。只要你能將要索引的文件轉(zhuǎn)化成文本格式,Lucene 就能為你的文檔建立索引。比如,如果你想為 HTML 文檔或者 PDF 文檔建立索引,那么首先你就需要從這些文檔中提取出文本信息,然后把文本信息交給 Lucene 建立索引。我們接下來(lái)的例子用來(lái)演示如何利用 Lucene 為后綴名為 txt 的文件建立索引。

    1. 準(zhǔn)備文本文件

    首先把一些以 txt 為后綴名的文本文件放到一個(gè)目錄中,比如在 Windows 平臺(tái)上,你可以放到 C:\\files_to_index 下面。

    2. 創(chuàng)建索引

    清單1是為我們所準(zhǔn)備的文檔創(chuàng)建索引的代碼。


    清單1:用 Lucene 索引你的文檔
    package lucene.index;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.Reader;
    import java.util.Date;
    
    import org.apache.lucene.analysis.Analyzer;
    import org.apache.lucene.analysis.standard.StandardAnalyzer;
    import org.apache.lucene.document.Document;
    import org.apache.lucene.document.Field;
    import org.apache.lucene.index.IndexWriter;
    
    /**
     * This class demonstrates the process of creating an index with Lucene 
     * for text files in a directory.
     */
    public class TextFileIndexer {
     public static void main(String[] args) throws Exception{
       //fileDir is the directory that contains the text files to be indexed
       File   fileDir  = new File("C:\\files_to_index ");
    
       //indexDir is the directory that hosts Lucene's index files
       File   indexDir = new File("C:\\luceneIndex");
       Analyzer luceneAnalyzer = new StandardAnalyzer();
       IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
       File[] textFiles  = fileDir.listFiles();
       long startTime = new Date().getTime();
    
       //Add documents to the index
       for(int i = 0; i < textFiles.length; i++){
         if(textFiles[i].isFile() >> textFiles[i].getName().endsWith(".txt")){
           System.out.println("File " + textFiles[i].getCanonicalPath() 
                  + " is being indexed");
           Reader textReader = new FileReader(textFiles[i]);
           Document document = new Document();
           document.add(Field.Text("content",textReader));
           document.add(Field.Text("path",textFiles[i].getPath()));
           indexWriter.addDocument(document);
         }
       }
    
       indexWriter.optimize();
       indexWriter.close();
       long endTime = new Date().getTime();
    
       System.out.println("It took " + (endTime - startTime) 
                  + " milliseconds to create an index for the files in the directory "
                  + fileDir.getPath());
      }
    }
    

    正如清單1所示,你可以利用 Lucene 非常方便的為文檔創(chuàng)建索引。接下來(lái)我們分析一下清單1中的比較關(guān)鍵的代碼,我們先從下面的一條語(yǔ)句開(kāi)始看起。


    Analyzer luceneAnalyzer = new StandardAnalyzer();
    

    這條語(yǔ)句創(chuàng)建了類(lèi) StandardAnalyzer 的一個(gè)實(shí)例,這個(gè)類(lèi)是用來(lái)從文本中提取出索引項(xiàng)的。它只是抽象類(lèi) Analyzer 的其中一個(gè)實(shí)現(xiàn)。Analyzer 也有一些其它的子類(lèi),比如 SimpleAnalyzer 等。

    我們接著看另外一條語(yǔ)句:


    IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
    

    這條語(yǔ)句創(chuàng)建了類(lèi) IndexWriter 的一個(gè)實(shí)例,該類(lèi)也是 Lucene 索引機(jī)制里面的一個(gè)關(guān)鍵類(lèi)。這個(gè)類(lèi)能創(chuàng)建一個(gè)新的索引或者打開(kāi)一個(gè)已存在的索引并為該所引添加文檔。我們注意到該類(lèi)的構(gòu)造函數(shù)接受三個(gè)參數(shù),第一個(gè)參數(shù)指定了存儲(chǔ)索引文件的路徑。第二個(gè)參數(shù)指定了在索引過(guò)程中使用什么樣的分詞器。最后一個(gè)參數(shù)是個(gè)布爾變量,如果值為真,那么就表示要?jiǎng)?chuàng)建一個(gè)新的索引,如果值為假,就表示打開(kāi)一個(gè)已經(jīng)存在的索引。

    接下來(lái)的代碼演示了如何添加一個(gè)文檔到索引文件中。


    Document document = new Document();
    document.add(Field.Text("content",textReader));
    document.add(Field.Text("path",textFiles[i].getPath()));
    indexWriter.addDocument(document);
    

    首先第一行創(chuàng)建了類(lèi) Document 的一個(gè)實(shí)例,它由一個(gè)或者多個(gè)的域(Field)組成。你可以把這個(gè)類(lèi)想象成代表了一個(gè)實(shí)際的文檔,比如一個(gè) HTML 頁(yè)面,一個(gè) PDF 文檔,或者一個(gè)文本文件。而類(lèi) Document 中的域一般就是實(shí)際文檔的一些屬性。比如對(duì)于一個(gè) HTML 頁(yè)面,它的域可能包括標(biāo)題,內(nèi)容,URL 等。我們可以用不同類(lèi)型的 Field 來(lái)控制文檔的哪些內(nèi)容應(yīng)該索引,哪些內(nèi)容應(yīng)該存儲(chǔ)。如果想獲取更多的關(guān)于 Lucene 的域的信息,可以參考 Lucene 的幫助文檔。代碼的第二行和第三行為文檔添加了兩個(gè)域,每個(gè)域包含兩個(gè)屬性,分別是域的名字和域的內(nèi)容。在我們的例子中兩個(gè)域的名字分別是"content"和"path"。分別存儲(chǔ)了我們需要索引的文本文件的內(nèi)容和路徑。最后一行把準(zhǔn)備好的文檔添加到了索引當(dāng)中。

    當(dāng)我們把文檔添加到索引中后,不要忘記關(guān)閉索引,這樣才保證 Lucene 把添加的文檔寫(xiě)回到硬盤(pán)上。下面的一句代碼演示了如何關(guān)閉索引。


    indexWriter.close();
    

    利用清單1中的代碼,你就可以成功的將文本文檔添加到索引中去。接下來(lái)我們看看對(duì)索引進(jìn)行的另外一種重要的操作,從索引中刪除文檔。





    回頁(yè)首


    從索引中刪除文檔

    類(lèi)IndexReader負(fù)責(zé)從一個(gè)已經(jīng)存在的索引中刪除文檔,如清單2所示。


    清單2:從索引中刪除文檔
    File   indexDir = new File("C:\\luceneIndex");
    IndexReader ir = IndexReader.open(indexDir);
    ir.delete(1);
    ir.delete(new Term("path","C:\\file_to_index\lucene.txt"));
    ir.close();
    

    在清單2中,第二行用靜態(tài)方法 IndexReader.open(indexDir) 初始化了類(lèi) IndexReader 的一個(gè)實(shí)例,這個(gè)方法的參數(shù)指定了索引的存儲(chǔ)路徑。類(lèi) IndexReader 提供了兩種方法去刪除一個(gè)文檔,如程序中的第三行和第四行所示。第三行利用文檔的編號(hào)來(lái)刪除文檔。每個(gè)文檔都有一個(gè)系統(tǒng)自動(dòng)生成的編號(hào)。第四行刪除了路徑為"C:\\file_to_index\lucene.txt"的文檔。你可以通過(guò)指定文件路徑來(lái)方便的刪除一個(gè)文檔。值得注意的是雖然利用上述代碼刪除文檔使得該文檔不能被檢索到,但是并沒(méi)有物理上刪除該文檔。Lucene 只是通過(guò)一個(gè)后綴名為 .delete 的文件來(lái)標(biāo)記哪些文檔已經(jīng)被刪除。既然沒(méi)有物理上刪除,我們可以方便的把這些標(biāo)記為刪除的文檔恢復(fù)過(guò)來(lái),如清單 3 所示,首先打開(kāi)一個(gè)索引,然后調(diào)用方法 ir.undeleteAll() 來(lái)完成恢復(fù)工作。


    清單3:恢復(fù)已刪除文檔
    File   indexDir = new File("C:\\luceneIndex");
    IndexReader ir = IndexReader.open(indexDir);
    ir.undeleteAll();
    ir.close();
    

    你現(xiàn)在也許想知道如何物理上刪除索引中的文檔,方法也非常簡(jiǎn)單。清單 4 演示了這個(gè)過(guò)程。


    清單4:如何物理上刪除文檔
    File   indexDir = new File("C:\\luceneIndex");
    Analyzer luceneAnalyzer = new StandardAnalyzer();
    IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,false);
    indexWriter.optimize();
    indexWriter.close();
    

    在清單 4 中,第三行創(chuàng)建了類(lèi) IndexWriter 的一個(gè)實(shí)例,并且打開(kāi)了一個(gè)已經(jīng)存在的索引。第 4 行對(duì)索引進(jìn)行清理,清理過(guò)程中將把所有標(biāo)記為刪除的文檔物理刪除。

    Lucene 沒(méi)有直接提供方法對(duì)文檔進(jìn)行更新,如果你需要更新一個(gè)文檔,那么你首先需要把這個(gè)文檔從索引中刪除,然后把新版本的文檔加入到索引中去。





    回頁(yè)首


    提高索引性能

    利用 Lucene,在創(chuàng)建索引的工程中你可以充分利用機(jī)器的硬件資源來(lái)提高索引的效率。當(dāng)你需要索引大量的文件時(shí),你會(huì)注意到索引過(guò)程的瓶頸是在往磁盤(pán)上寫(xiě)索引文件的過(guò)程中。為了解決這個(gè)問(wèn)題, Lucene 在內(nèi)存中持有一塊緩沖區(qū)。但我們?nèi)绾慰刂?Lucene 的緩沖區(qū)呢?幸運(yùn)的是,Lucene 的類(lèi) IndexWriter 提供了三個(gè)參數(shù)用來(lái)調(diào)整緩沖區(qū)的大小以及往磁盤(pán)上寫(xiě)索引文件的頻率。

    1.合并因子(mergeFactor)

    這個(gè)參數(shù)決定了在 Lucene 的一個(gè)索引塊中可以存放多少文檔以及把磁盤(pán)上的索引塊合并成一個(gè)大的索引塊的頻率。比如,如果合并因子的值是 10,那么當(dāng)內(nèi)存中的文檔數(shù)達(dá)到 10 的時(shí)候所有的文檔都必須寫(xiě)到磁盤(pán)上的一個(gè)新的索引塊中。并且,如果磁盤(pán)上的索引塊的隔數(shù)達(dá)到 10 的話,這 10 個(gè)索引塊會(huì)被合并成一個(gè)新的索引塊。這個(gè)參數(shù)的默認(rèn)值是 10,如果需要索引的文檔數(shù)非常多的話這個(gè)值將是非常不合適的。對(duì)批處理的索引來(lái)講,為這個(gè)參數(shù)賦一個(gè)比較大的值會(huì)得到比較好的索引效果。

    2.最小合并文檔數(shù)

    這個(gè)參數(shù)也會(huì)影響索引的性能。它決定了內(nèi)存中的文檔數(shù)至少達(dá)到多少才能將它們寫(xiě)回磁盤(pán)。這個(gè)參數(shù)的默認(rèn)值是10,如果你有足夠的內(nèi)存,那么將這個(gè)值盡量設(shè)的比較大一些將會(huì)顯著的提高索引性能。

    3.最大合并文檔數(shù)

    這個(gè)參數(shù)決定了一個(gè)索引塊中的最大的文檔數(shù)。它的默認(rèn)值是 Integer.MAX_VALUE,將這個(gè)參數(shù)設(shè)置為比較大的值可以提高索引效率和檢索速度,由于該參數(shù)的默認(rèn)值是整型的最大值,所以我們一般不需要改動(dòng)這個(gè)參數(shù)。

    清單 5 列出了這個(gè)三個(gè)參數(shù)用法,清單 5 和清單 1 非常相似,除了清單 5 中會(huì)設(shè)置剛才提到的三個(gè)參數(shù)。


    清單5:提高索引性能
    /**
     * This class demonstrates how to improve the indexing performance 
     * by adjusting the parameters provided by IndexWriter.
     */
    public class AdvancedTextFileIndexer  {
      public static void main(String[] args) throws Exception{
        //fileDir is the directory that contains the text files to be indexed
        File   fileDir  = new File("C:\\files_to_index");
    
        //indexDir is the directory that hosts Lucene's index files
        File   indexDir = new File("C:\\luceneIndex");
        Analyzer luceneAnalyzer = new StandardAnalyzer();
        File[] textFiles  = fileDir.listFiles();
        long startTime = new Date().getTime();
    
        int mergeFactor = 10;
        int minMergeDocs = 10;
        int maxMergeDocs = Integer.MAX_VALUE;
        IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);        
        indexWriter.mergeFactor = mergeFactor;
        indexWriter.minMergeDocs = minMergeDocs;
        indexWriter.maxMergeDocs = maxMergeDocs;
    
        //Add documents to the index
        for(int i = 0; i < textFiles.length; i++){
          if(textFiles[i].isFile() >> textFiles[i].getName().endsWith(".txt")){
            Reader textReader = new FileReader(textFiles[i]);
            Document document = new Document();
            document.add(Field.Text("content",textReader));
            document.add(Field.Keyword("path",textFiles[i].getPath()));
            indexWriter.addDocument(document);
          }
        }
    
        indexWriter.optimize();
        indexWriter.close();
        long endTime = new Date().getTime();
    
        System.out.println("MergeFactor: " + indexWriter.mergeFactor);
        System.out.println("MinMergeDocs: " + indexWriter.minMergeDocs);
        System.out.println("MaxMergeDocs: " + indexWriter.maxMergeDocs);
        System.out.println("Document number: " + textFiles.length);
        System.out.println("Time consumed: " + (endTime - startTime) + " milliseconds");
      }
    }
    

    通過(guò)這個(gè)例子,我們注意到在調(diào)整緩沖區(qū)的大小以及寫(xiě)磁盤(pán)的頻率上面 Lucene 給我們提供了非常大的靈活性。現(xiàn)在我們來(lái)看一下代碼中的關(guān)鍵語(yǔ)句。如下的代碼首先創(chuàng)建了類(lèi) IndexWriter 的一個(gè)實(shí)例,然后對(duì)它的三個(gè)參數(shù)進(jìn)行賦值。


    int mergeFactor = 10;
    int minMergeDocs = 10;
    int maxMergeDocs = Integer.MAX_VALUE;
    IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);        
    indexWriter.mergeFactor = mergeFactor;
    indexWriter.minMergeDocs = minMergeDocs;
    indexWriter.maxMergeDocs = maxMergeDocs;
    

    下面我們來(lái)看一下這三個(gè)參數(shù)取不同的值對(duì)索引時(shí)間的影響,注意參數(shù)值的不同和索引之間的關(guān)系。我們?yōu)檫@個(gè)實(shí)驗(yàn)準(zhǔn)備了 10000 個(gè)測(cè)試文檔。表 1 顯示了測(cè)試結(jié)果。


    表1:測(cè)試結(jié)果
    表1:測(cè)試結(jié)果

    通過(guò)表 1,你可以清楚地看到三個(gè)參數(shù)對(duì)索引時(shí)間的影響。在實(shí)踐中,你會(huì)經(jīng)常的改變合并因子和最小合并文檔數(shù)的值來(lái)提高索引性能。只要你有足夠大的內(nèi)存,你可以為合并因子和最小合并文檔數(shù)這兩個(gè)參數(shù)賦盡量大的值以提高索引效率,另外我們一般無(wú)需更改最大合并文檔數(shù)這個(gè)參數(shù)的值,因?yàn)橄到y(tǒng)已經(jīng)默認(rèn)將它設(shè)置成了最大。





    回頁(yè)首


    Lucene 索引文件結(jié)構(gòu)分析

    在分析 Lucene 的索引文件結(jié)構(gòu)之前,我們先要理解反向索引(Inverted index)這個(gè)概念,反向索引是一種以索引項(xiàng)為中心來(lái)組織文檔的方式,每個(gè)索引項(xiàng)指向一個(gè)文檔序列,這個(gè)序列中的文檔都包含該索引項(xiàng)。相反,在正向索引中,文檔占據(jù)了中心的位置,每個(gè)文檔指向了一個(gè)它所包含的索引項(xiàng)的序列。你可以利用反向索引輕松的找到那些文檔包含了特定的索引項(xiàng)。Lucene正是使用了反向索引作為其基本的索引結(jié)構(gòu)。





    回頁(yè)首


    索引文件的邏輯視圖

    在Lucene 中有索引塊的概念,每個(gè)索引塊包含了一定數(shù)目的文檔。我們能夠?qū)为?dú)的索引塊進(jìn)行檢索。圖 2 顯示了 Lucene 索引結(jié)構(gòu)的邏輯視圖。索引塊的個(gè)數(shù)由索引的文檔的總數(shù)以及每個(gè)索引塊所能包含的最大文檔數(shù)來(lái)決定。


    圖2:索引文件的邏輯視圖
    圖2:索引文件的邏輯視圖




    回頁(yè)首


    Lucene 中的關(guān)鍵索引文件

    下面的部分將會(huì)分析Lucene中的主要的索引文件,可能分析有些索引文件的時(shí)候沒(méi)有包含文件的所有的字段,但不會(huì)影響到對(duì)索引文件的理解。

    1.索引塊文件

    這個(gè)文件包含了索引中的索引塊信息,這個(gè)文件包含了每個(gè)索引塊的名字以及大小等信息。表 2 顯示了這個(gè)文件的結(jié)構(gòu)信息。


    表2:索引塊文件結(jié)構(gòu)
    表2:索引塊文件結(jié)構(gòu)

    2.域信息文件

    我們知道,索引中的文檔由一個(gè)或者多個(gè)域組成,這個(gè)文件包含了每個(gè)索引塊中的域的信息。表 3 顯示了這個(gè)文件的結(jié)構(gòu)。


    表3:域信息文件結(jié)構(gòu)
    表3:域信息文件結(jié)構(gòu)

    3.索引項(xiàng)信息文件

    這是索引文件里面最核心的一個(gè)文件,它存儲(chǔ)了所有的索引項(xiàng)的值以及相關(guān)信息,并且以索引項(xiàng)來(lái)排序。表 4 顯示了這個(gè)文件的結(jié)構(gòu)。


    表4:索引項(xiàng)信息文件結(jié)構(gòu)
    表4:索引項(xiàng)信息文件結(jié)構(gòu)

    4.頻率文件

    這個(gè)文件包含了包含索引項(xiàng)的文檔的列表,以及索引項(xiàng)在每個(gè)文檔中出現(xiàn)的頻率信息。如果Lucene在索引項(xiàng)信息文件中發(fā)現(xiàn)有索引項(xiàng)和搜索詞相匹配。那么 Lucene 就會(huì)在頻率文件中找有哪些文件包含了該索引項(xiàng)。表5顯示了這個(gè)文件的一個(gè)大致的結(jié)構(gòu),并沒(méi)有包含這個(gè)文件的所有字段。


    表5:頻率文件的結(jié)構(gòu)
    表5:頻率文件的結(jié)構(gòu)

    5.位置文件

    這個(gè)文件包含了索引項(xiàng)在每個(gè)文檔中出現(xiàn)的位置信息,你可以利用這些信息來(lái)參與對(duì)索引結(jié)果的排序。表 6 顯示了這個(gè)文件的結(jié)構(gòu)


    表6:位置文件的結(jié)構(gòu)
    表6:位置文件的結(jié)構(gòu)

    到目前為止我們介紹了 Lucene 中的主要的索引文件結(jié)構(gòu),希望能對(duì)你理解 Lucene 的物理的存儲(chǔ)結(jié)構(gòu)有所幫助。





    回頁(yè)首


    總結(jié)

    目前已經(jīng)有非常多的知名的組織正在使用 Lucene,比如,Lucene 為 Eclipse 的幫助系統(tǒng),麻省理工學(xué)院的 OpenCourseWare 提供了搜索功能。通過(guò)閱讀這篇文章,希望你能對(duì) Lucene 的索引機(jī)制有所了解,并且你會(huì)發(fā)現(xiàn)利用 Lucene 創(chuàng)建索引是非常簡(jiǎn)單的事情。

    posted on 2006-08-01 23:12 船長(zhǎng) 閱讀(1560) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): J2EE

    評(píng)論:
    # re: 深入 Lucene 索引機(jī)制 2010-10-29 11:45 | liuxuejin
    可以分析一下 索引文件在 磁盤(pán)上的具體實(shí)現(xiàn)嗎?  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲成a人片在线观看中文!!! | 久久国产精品免费看| 亚洲一区二区三区久久| 亚洲中文字幕无码av在线| 国产亚洲一区区二区在线| 国产精品免费小视频| 一二三四免费观看在线视频中文版| 伊人久久五月丁香综合中文亚洲 | 久久青青草原亚洲av无码| 99在线免费视频| 亚洲乱人伦精品图片| 国产成人A人亚洲精品无码| 永久在线毛片免费观看| 成年在线观看网站免费| 精品亚洲国产成人av| 亚洲粉嫩美白在线| 亚洲小视频在线播放| 亚洲麻豆精品果冻传媒| 亚洲国产精品无码专区影院| 国产精品亚洲mnbav网站| 免费a级毛片无码av| 四虎影院永久免费观看| 天天摸夜夜摸成人免费视频| eeuss影院免费92242部| 免费国产va在线观看| 西西人体大胆免费视频| 久久亚洲AV无码精品色午夜麻豆| 免费日本黄色网址| 成年女人永久免费观看片| 四虎影院在线免费播放| 日韩特黄特色大片免费视频| 99re6免费视频| g0g0人体全免费高清大胆视频| 亚洲伊人久久大香线蕉影院| 国产亚洲美女精品久久久2020| 成人特黄a级毛片免费视频| 久久久久久久久免费看无码| 国产免费一区二区视频| 7m凹凸精品分类大全免费| 午夜福利不卡片在线播放免费| 中文字幕免费在线看电影大全|