Swing


          天行健 君子以自強不息

          posts - 69, comments - 215, trackbacks - 0, articles - 16
             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          Lucene學習

          Posted on 2007-10-30 10:02 zht 閱讀(1175) 評論(1)  編輯  收藏 所屬分類: J2SE

          (轉)

          本文首先介紹了Lucene的一些基本概念,然后開發了一個應用程序演示了利用Lucene建立索引并在該索引上進行搜索的過程。

          Lucene 簡介

          Lucene 是一個基于 Java 的全文信息檢索工具包,它不是一個完整的搜索應用程序,而是為你的應用程序提供索引和搜索功能。Lucene 目前是 Apache Jakarta 家族中的一個開源項目。也是目前最為流行的基于 Java 開源全文檢索工具包。

          目前已經有很多應用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的幫助系統的搜索功能。Lucene 能夠為文本類型的數據建立索引,所以你只要能把你要索引的數據格式轉化的文本的,Lucene 就能對你的文檔進行索引和搜索。比如你要對一些 HTML 文檔,PDF 文檔進行索引的話你就首先需要把 HTML 文檔和 PDF 文檔轉化成文本格式的,然后將轉化后的內容交給 Lucene 進行索引,然后把創建好的索引文件保存到磁盤或者內存中,最后根據用戶輸入的查詢條件在索引文件上進行查詢。不指定要索引的文檔的格式也使 Lucene 能夠幾乎適用于所有的搜索應用程序。

          圖 1 表示了搜索應用程序和 Lucene 之間的關系,也反映了利用 Lucene 構建搜索應用程序的流程:


          圖1. 搜索應用程序和 Lucene 之間的關系
          圖1. 搜索應用程序和 Lucene 之間的關系




          索引和搜索

          索引是現代搜索引擎的核心,建立索引的過程就是把源數據處理成非常方便查詢的索引文件的過程。為什么索引這么重要呢,試想你現在要在大量的文檔中搜索含有某個關鍵詞的文檔,那么如果不建立索引的話你就需要把這些文檔順序的讀入內存,然后檢查這個文章中是不是含有要查找的關鍵詞,這樣的話就會耗費非常多的時間,想想搜索引擎可是在毫秒級的時間內查找出要搜索的結果的。這就是由于建立了索引的原因,你可以把索引想象成這樣一種數據結構,他能夠使你快速的隨機訪問存儲在索引中的關鍵詞,進而找到該關鍵詞所關聯的文檔。Lucene 采用的是一種稱為反向索引(inverted index)的機制。反向索引就是說我們維護了一個詞/短語表,對于這個表中的每個詞/短語,都有一個鏈表描述了有哪些文檔包含了這個詞/短語。這樣在用戶輸入查詢條件的時候,就能非常快的得到搜索結果。我們將在本系列文章的第二部分詳細介紹 Lucene 的索引機制,由于 Lucene 提供了簡單易用的 API,所以即使讀者剛開始對全文本進行索引的機制并不太了解,也可以非常容易的使用 Lucene 對你的文檔實現索引。

          對文檔建立好索引后,就可以在這些索引上面進行搜索了。搜索引擎首先會對搜索的關鍵詞進行解析,然后再在建立好的索引上面進行查找,最終返回和用戶輸入的關鍵詞相關聯的文檔。





          Lucene 軟件包分析

          Lucene 軟件包的發布形式是一個 JAR 文件,下面我們分析一下這個 JAR 文件里面的主要的 JAVA 包,使讀者對之有個初步的了解。

          Package: org.apache.lucene.document

          這個包提供了一些為封裝要索引的文檔所需要的類,比如 Document, Field。這樣,每一個文檔最終被封裝成了一個 Document 對象。

          Package: org.apache.lucene.analysis

          這個包主要功能是對文檔進行分詞,因為文檔在建立索引之前必須要進行分詞,所以這個包的作用可以看成是為建立索引做準備工作。

          Package: org.apache.lucene.index

          這個包提供了一些類來協助創建索引以及對創建好的索引進行更新。這里面有兩個基礎的類:IndexWriter 和 IndexReader,其中 IndexWriter 是用來創建索引并添加文檔到索引中的,IndexReader 是用來刪除索引中的文檔的。

          Package: org.apache.lucene.search

          這個包提供了對在建立好的索引上進行搜索所需要的類。比如 IndexSearcher 和 Hits, IndexSearcher 定義了在指定的索引上進行搜索的方法,Hits 用來保存搜索得到的結果。





          一個簡單的搜索應用程序

          假設我們的電腦的目錄中含有很多文本文檔,我們需要查找哪些文檔含有某個關鍵詞。為了實現這種功能,我們首先利用 Lucene 對這個目錄中的文檔建立索引,然后在建立好的索引中搜索我們所要查找的文檔。通過這個例子讀者會對如何利用 Lucene 構建自己的搜索應用程序有個比較清楚的認識。





          建立索引

          為了對文檔進行索引,Lucene 提供了五個基礎的類,他們分別是 Document, Field, IndexWriter, Analyzer, Directory。下面我們分別介紹一下這五個類的用途:

          Document

          Document 是用來描述文檔的,這里的文檔可以指一個 HTML 頁面,一封電子郵件,或者是一個文本文件。一個 Document 對象由多個 Field 對象組成的。可以把一個 Document 對象想象成數據庫中的一個記錄,而每個 Field 對象就是記錄的一個字段。

          Field

          Field 對象是用來描述一個文檔的某個屬性的,比如一封電子郵件的標題和內容可以用兩個 Field 對象分別描述。

          Analyzer

          在一個文檔被索引之前,首先需要對文檔內容進行分詞處理,這部分工作就是由 Analyzer 來做的。Analyzer 類是一個抽象類,它有多個實現。針對不同的語言和應用需要選擇適合的 Analyzer。Analyzer 把分詞后的內容交給 IndexWriter 來建立索引。

          IndexWriter

          IndexWriter 是 Lucene 用來創建索引的一個核心的類,他的作用是把一個個的 Document 對象加到索引中來。

          Directory

          這個類代表了 Lucene 的索引的存儲的位置,這是一個抽象類,它目前有兩個實現,第一個是 FSDirectory,它表示一個存儲在文件系統中的索引的位置。第二個是 RAMDirectory,它表示一個存儲在內存當中的索引的位置。

          熟悉了建立索引所需要的這些類后,我們就開始對某個目錄下面的文本文件建立索引了,清單1給出了對某個目錄下的文本文件建立索引的源代碼。


          清單 1. 對文本文件建立索引
          package TestLucene;
                      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 demonstrate the process of creating index with Lucene
                      * for text files
                      */
                      public class TxtFileIndexer {
                      public static void main(String[] args) throws Exception{
                      //indexDir is the directory that hosts Lucene's index files
                      File   indexDir = new File("D:\\luceneIndex");
                      //dataDir is the directory that hosts the text files that to be indexed
                      File   dataDir  = new File("D:\\luceneData");
                      Analyzer luceneAnalyzer = new StandardAnalyzer();
                      File[] dataFiles  = dataDir.listFiles();
                      IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
                      long startTime = new Date().getTime();
                      for(int i = 0; i < dataFiles.length; i++){
                      if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){
                      System.out.println("Indexing file " + dataFiles[i].getCanonicalPath());
                      Document document = new Document();
                      Reader txtReader = new FileReader(dataFiles[i]);
                      document.add(new Field("path", dataFiles[i].getCanonicalPath(),
                                        Field.Store.YES, Field.Index.TOKENIZED)); document.add(new Field("contents", txtReader)); indexWriter.addDocument(document); } } indexWriter.optimize(); indexWriter.close(); long endTime = new Date().getTime(); System.out.println("It takes " + (endTime - startTime) + " milliseconds to create index for the files in directory " + dataDir.getPath()); } }

          在清單1中,我們注意到類 IndexWriter 的構造函數需要三個參數,第一個參數指定了所創建的索引要存放的位置,他可以是一個 File 對象,也可以是一個 FSDirectory 對象或者 RAMDirectory 對象。第二個參數指定了 Analyzer 類的一個實現,也就是指定這個索引是用哪個分詞器對文擋內容進行分詞。第三個參數是一個布爾型的變量,如果為 true 的話就代表創建一個新的索引,為 false 的話就代表在原來索引的基礎上進行操作。接著程序遍歷了目錄下面的所有文本文檔,并為每一個文本文檔創建了一個 Document 對象。然后把文本文檔的兩個屬性:路徑和內容加入到了兩個 Field 對象中,接著在把這兩個 Field 對象加入到 Document 對象中,最后把這個文檔用 IndexWriter 類的 add 方法加入到索引中去。這樣我們便完成了索引的創建。接下來我們進入在建立好的索引上進行搜索的部分。





          搜索文檔

          利用Lucene進行搜索就像建立索引一樣也是非常方便的。在上面一部分中,我們已經為一個目錄下的文本文檔建立好了索引,現在我們就要在這個索引上進行搜索以找到包含某個關鍵詞或短語的文檔。Lucene提供了幾個基礎的類來完成這個過程,它們分別是呢IndexSearcher, Term, Query, TermQuery, Hits. 下面我們分別介紹這幾個類的功能。

          Query

          這是一個抽象類,他有多個實現,比如TermQuery, BooleanQuery, PrefixQuery. 這個類的目的是把用戶輸入的查詢字符串封裝成Lucene能夠識別的Query。

          Term

          Term是搜索的基本單位,一個Term對象有兩個String類型的域組成。生成一個Term對象可以有如下一條語句來完成:Term term = new Term(“fieldName”,”queryWord”); 其中第一個參數代表了要在文檔的哪一個Field上進行查找,第二個參數代表了要查詢的關鍵詞。

          TermQuery

          TermQuery是抽象類Query的一個子類,它同時也是Lucene支持的最為基本的一個查詢類。生成一個TermQuery對象由如下語句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的構造函數只接受一個參數,那就是一個Term對象。

          IndexSearcher

          IndexSearcher是用來在建立好的索引上進行搜索的。它只能以只讀的方式打開一個索引,所以可以有多個IndexSearcher的實例在一個索引上進行操作。

          Hits

          Hits是用來保存搜索的結果的。

          介紹完這些搜索所必須的類之后,我們就開始在之前所建立的索引上進行搜索了,清單2給出了完成搜索功能所需要的代碼。


          清單2 :在建立好的索引上進行搜索
          package TestLucene;
                      import java.io.File;
                      import org.apache.lucene.document.Document;
                      import org.apache.lucene.index.Term;
                      import org.apache.lucene.search.Hits;
                      import org.apache.lucene.search.IndexSearcher;
                      import org.apache.lucene.search.TermQuery;
                      import org.apache.lucene.store.FSDirectory;
                      /**
                      * This class is used to demonstrate the
                      * process of searching on an existing
                      * Lucene index
                      *
                      */
                      public class TxtFileSearcher {
                      public static void main(String[] args) throws Exception{
                      String queryStr = "lucene";
                      //This is the directory that hosts the Lucene index
                      File indexDir = new File("D:\\luceneIndex");
                      FSDirectory directory = FSDirectory.getDirectory(indexDir,false);
                      IndexSearcher searcher = new IndexSearcher(directory);
                      if(!indexDir.exists()){
                      System.out.println("The Lucene index is not exist");
                      return;
                      }
                      Term term = new Term("contents",queryStr.toLowerCase());
                      TermQuery luceneQuery = new TermQuery(term);
                      Hits hits = searcher.search(luceneQuery);
                      for(int i = 0; i < hits.length(); i++){
                      Document document = hits.doc(i);
                      System.out.println("File: " + document.get("path"));
                      }
                      }
                      }
                      

          在清單2中,類IndexSearcher的構造函數接受一個類型為Directory的對象,Directory是一個抽象類,它目前有兩個子類:FSDirctory和RAMDirectory. 我們的程序中傳入了一個FSDirctory對象作為其參數,代表了一個存儲在磁盤上的索引的位置。構造函數執行完成后,代表了這個IndexSearcher以只讀的方式打開了一個索引。然后我們程序構造了一個Term對象,通過這個Term對象,我們指定了要在文檔的內容中搜索包含關鍵詞”lucene”的文檔。接著利用這個Term對象構造出TermQuery對象并把這個TermQuery對象傳入到IndexSearcher的search方法中進行查詢,返回的結果保存在Hits對象中。最后我們用了一個循環語句把搜索到的文檔的路徑都打印了出來。好了,我們的搜索應用程序已經開發完畢,怎么樣,利用Lucene開發搜索應用程序是不是很簡單。





          總結

          本文首先介紹了 Lucene 的一些基本概念,然后開發了一個應用程序演示了利用 Lucene 建立索引并在該索引上進行搜索的過程。希望本文能夠為學習 Lucene 的讀者提供幫助。

          架構概覽

          圖一顯示了 Lucene 的索引機制的架構。Lucene 使用各種解析器對各種不同類型的文檔進行解析。比如對于 HTML 文檔,HTML 解析器會做一些預處理的工作,比如過濾文檔中的 HTML 標簽等等。HTML 解析器的輸出的是文本內容,接著 Lucene 的分詞器(Analyzer)從文本內容中提取出索引項以及相關信息,比如索引項的出現頻率。接著 Lucene 的分詞器把這些信息寫到索引文件中。


          圖一:Lucene 索引機制架構
          圖一:Lucene 索引機制架構




          用Lucene索引文檔

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

          1. 準備文本文件

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

          2. 創建索引

          清單1是為我們所準備的文檔創建索引的代碼。


          清單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 非常方便的為文檔創建索引。接下來我們分析一下清單1中的比較關鍵的代碼,我們先從下面的一條語句開始看起。


          Analyzer luceneAnalyzer = new StandardAnalyzer();
                      

          這條語句創建了類 StandardAnalyzer 的一個實例,這個類是用來從文本中提取出索引項的。它只是抽象類 Analyzer 的其中一個實現。Analyzer 也有一些其它的子類,比如 SimpleAnalyzer 等。

          我們接著看另外一條語句:


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

          這條語句創建了類 IndexWriter 的一個實例,該類也是 Lucene 索引機制里面的一個關鍵類。這個類能創建一個新的索引或者打開一個已存在的索引并為該所引添加文檔。我們注意到該類的構造函數接受三個參數,第一個參數指定了存儲索引文件的路徑。第二個參數指定了在索引過程中使用什么樣的分詞器。最后一個參數是個布爾變量,如果值為真,那么就表示要創建一個新的索引,如果值為假,就表示打開一個已經存在的索引。

          接下來的代碼演示了如何添加一個文檔到索引文件中。


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

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

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


          indexWriter.close();
                      

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





          從索引中刪除文檔

          類IndexReader負責從一個已經存在的索引中刪除文檔,如清單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中,第二行用靜態方法 IndexReader.open(indexDir) 初始化了類 IndexReader 的一個實例,這個方法的參數指定了索引的存儲路徑。類 IndexReader 提供了兩種方法去刪除一個文檔,如程序中的第三行和第四行所示。第三行利用文檔的編號來刪除文檔。每個文檔都有一個系統自動生成的編號。第四行刪除了路徑為"C:\\file_to_index\lucene.txt"的文檔。你可以通過指定文件路徑來方便的刪除一個文檔。值得注意的是雖然利用上述代碼刪除文檔使得該文檔不能被檢索到,但是并沒有物理上刪除該文檔。Lucene 只是通過一個后綴名為 .delete 的文件來標記哪些文檔已經被刪除。既然沒有物理上刪除,我們可以方便的把這些標記為刪除的文檔恢復過來,如清單 3 所示,首先打開一個索引,然后調用方法 ir.undeleteAll() 來完成恢復工作。


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

          你現在也許想知道如何物理上刪除索引中的文檔,方法也非常簡單。清單 4 演示了這個過程。


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

          在清單 4 中,第三行創建了類 IndexWriter 的一個實例,并且打開了一個已經存在的索引。第 4 行對索引進行清理,清理過程中將把所有標記為刪除的文檔物理刪除。

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





          提高索引性能

          利用 Lucene,在創建索引的工程中你可以充分利用機器的硬件資源來提高索引的效率。當你需要索引大量的文件時,你會注意到索引過程的瓶頸是在往磁盤上寫索引文件的過程中。為了解決這個問題, Lucene 在內存中持有一塊緩沖區。但我們如何控制 Lucene 的緩沖區呢?幸運的是,Lucene 的類 IndexWriter 提供了三個參數用來調整緩沖區的大小以及往磁盤上寫索引文件的頻率。

          1.合并因子(mergeFactor)

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

          2.最小合并文檔數

          這個參數也會影響索引的性能。它決定了內存中的文檔數至少達到多少才能將它們寫回磁盤。這個參數的默認值是10,如果你有足夠的內存,那么將這個值盡量設的比較大一些將會顯著的提高索引性能。

          3.最大合并文檔數

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

          清單 5 列出了這個三個參數用法,清單 5 和清單 1 非常相似,除了清單 5 中會設置剛才提到的三個參數。


          清單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");
                      }
                      }
                      

          通過這個例子,我們注意到在調整緩沖區的大小以及寫磁盤的頻率上面 Lucene 給我們提供了非常大的靈活性。現在我們來看一下代碼中的關鍵語句。如下的代碼首先創建了類 IndexWriter 的一個實例,然后對它的三個參數進行賦值。


          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;
                      

          下面我們來看一下這三個參數取不同的值對索引時間的影響,注意參數值的不同和索引之間的關系。我們為這個實驗準備了 10000 個測試文檔。表 1 顯示了測試結果。


          表1:測試結果
          表1:測試結果

          通過表 1,你可以清楚地看到三個參數對索引時間的影響。在實踐中,你會經常的改變合并因子和最小合并文檔數的值來提高索引性能。只要你有足夠大的內存,你可以為合并因子和最小合并文檔數這兩個參數賦盡量大的值以提高索引效率,另外我們一般無需更改最大合并文檔數這個參數的值,因為系統已經默認將它設置成了最大。





          Lucene 索引文件結構分析

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





          索引文件的邏輯視圖

          在Lucene 中有索引塊的概念,每個索引塊包含了一定數目的文檔。我們能夠對單獨的索引塊進行檢索。圖 2 顯示了 Lucene 索引結構的邏輯視圖。索引塊的個數由索引的文檔的總數以及每個索引塊所能包含的最大文檔數來決定。


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




          Lucene 中的關鍵索引文件

          下面的部分將會分析Lucene中的主要的索引文件,可能分析有些索引文件的時候沒有包含文件的所有的字段,但不會影響到對索引文件的理解。

          1.索引塊文件

          這個文件包含了索引中的索引塊信息,這個文件包含了每個索引塊的名字以及大小等信息。表 2 顯示了這個文件的結構信息。


          表2:索引塊文件結構
          表2:索引塊文件結構

          2.域信息文件

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


          表3:域信息文件結構
          表3:域信息文件結構

          3.索引項信息文件

          這是索引文件里面最核心的一個文件,它存儲了所有的索引項的值以及相關信息,并且以索引項來排序。表 4 顯示了這個文件的結構。


          表4:索引項信息文件結構
          表4:索引項信息文件結構

          4.頻率文件

          這個文件包含了包含索引項的文檔的列表,以及索引項在每個文檔中出現的頻率信息。如果Lucene在索引項信息文件中發現有索引項和搜索詞相匹配。那么 Lucene 就會在頻率文件中找有哪些文件包含了該索引項。表5顯示了這個文件的一個大致的結構,并沒有包含這個文件的所有字段。


          表5:頻率文件的結構
          表5:頻率文件的結構

          5.位置文件

          這個文件包含了索引項在每個文檔中出現的位置信息,你可以利用這些信息來參與對索引結果的排序。表 6 顯示了這個文件的結構


          表6:位置文件的結構
          表6:位置文件的結構

          到目前為止我們介紹了 Lucene 中的主要的索引文件結構,希望能對你理解 Lucene 的物理的存儲結構有所幫助。





          總結

          目前已經有非常多的知名的組織正在使用 Lucene,比如,Lucene 為 Eclipse 的幫助系統,麻省理工學院的 OpenCourseWare 提供了搜索功能。通過閱讀這篇文章,希望你能對 Lucene 的索引機制有所了解,并且你會發現利用 Lucene 創建索引是非常簡單的事情。



          參考資料

          學習


          獲得產品和技術

          • 下載 Lucene 最新版本。

          主站蜘蛛池模板: 南阳市| 泰宁县| 平武县| 邯郸县| 南丹县| 海晏县| 铜川市| 台北市| 类乌齐县| 喜德县| 定陶县| 上犹县| 长阳| 宜章县| 苍山县| 观塘区| 安岳县| 方正县| 巴青县| 沽源县| 海南省| 渭源县| 商河县| 乐业县| 道真| 阿城市| 萍乡市| 大理市| 镶黄旗| 鹰潭市| 儋州市| 黑水县| 渭南市| 武功县| 都安| 藁城市| 太原市| 彭阳县| 柞水县| 靖西县| 寿宁县|