1. 程式人生 > >【搜尋那些事】細談lucene(三)lucene核心API簡介

【搜尋那些事】細談lucene(三)lucene核心API簡介


經過前面的簡單理論介紹,相信大家對搜尋引擎lucene有個簡單的瞭解。前面我們也提到過在lucene中主要包括索引和搜尋這兩大方面的元件。今天我們我們就通過一個簡單的例項來看一下lucene給我們提供的有關這兩個元件的簡單用法。


一:建立索引

在用lucene搜尋之前,我們首先要做的就是是建立索引,只有有索引了,我們才有了搜尋的物件,下面我們就根據一個建立索引的小demo來一步步分析一下建立索引的步驟:

public class Indexer {
    private IndexWriter writer = null;
    public Indexer(String indexDir) throws Exception {
       Directory dir = FSDirectory.open(new File(indexDir));//開啟儲存索引目錄
       writer = new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30),
              true, IndexWriter.MaxFieldLength.UNLIMITED);//建立lucene IndexWriter,建立索引工具
    }
   
    public void close() throws Exception
    {
       writer.close();
    }
 
    public int index(String dataDir, FileFilter filter) throws Exception {
 
       File[] files = new File(dataDir).listFiles();
       for (File file : files) {//遍歷檔案目錄下所有txt檔案,把檔案加入索引
           if(!file.isDirectory() && !file.isHidden() && file.exists()
                  && (filter == null || filter.accept(file))) {
              indexFile(file);
           }
       }
       return writer.numDocs();
    }
 
    private Document getDocument(File f) {
       Document doc = new Document();
       try {
           doc.add(new Field("content", new FileReader(f)));
           doc.add(new Field("fileName", f.getName(), Field.Store.YES,
                  Field.Index.NOT_ANALYZED));
           doc.add(new Field("filePath", f.getCanonicalPath(),
                  Field.Store.YES, Field.Index.NOT_ANALYZED));
       } catch (Exception e) {
           e.printStackTrace();
       }
       return doc;
    }
 
    public void indexFile(File f) throws Exception {
       System.out.println("make indexfile is " + f.getCanonicalPath());
       Document doc = getDocument(f);//建立檔案document
       writer.addDocument(doc);//把當前檔案document加入索引
    }
 
    public static void main(String[] args) throws Exception {
 
       String indexDir = "d:\\";// 建立索引的目錄
       String dataDir = "d:\\a\\";// 建立檔案目錄
       long begin = System.currentTimeMillis();
       Indexer index = new Indexer(indexDir);
       int numIndexed;
       numIndexed = index.index(dataDir, new TextFilesFilter());
       long end = System.currentTimeMillis();
index.close();
       System.out.println("all makeindex num is:"+numIndexed+" use time :"+(end-begin));
    }
}
class TextFilesFilter implements FileFilter
{
       public boolean accept(File file)
       {
           return file.getName().toLowerCase().endsWith(".txt");
       }
}
 


從上面demo中的註釋中相信大家也差不多也能看懂大體意思。下面我們還是從main函式一步步的看一下吧

1. 首先根據構造方法建立例項物件時建立索引器indexWriter。這是建立索引時用到的一個主要類,在下面我們會詳細降到。

2. 建立完索引器之後,根據指定的檔案目錄遍歷所有符合要求的檔案,然後對每一個檔案建立一個Document物件。

3. 把建立的document物件加入索引器進行索引。

上面只是大體的介紹了一下整個索引步驟,有些細節我沒有提到,相信大家也很容易看懂。比如床架檔案過濾fiter之類的操作。

Ok,寫到這裡,我們就來看一下執行這個建立索引程式能看到什麼結果吧。



執行之後,我們會發現,在我們制定的索引資料夾下會多出以下幾個檔案:這幾個檔案具體是什麼東東,其實我也不知道,也許以後慢慢的會研究到這些索引檔案的內容:

 二:搜尋相關


Ok,索引內容我們就簡單介紹到這,下面我們來看一下lucene中搜索方面的簡單實現,還是以一段簡單的程式碼來認識一下:

public class Searcher {
   
   
    public static void main(String[] args) throws Exception {
 
       String indexDir = "d:\\index";// 建立索引的目錄
       System.out.println("請輸入您要搜尋的關鍵字:");
       Scanner scanner = new Scanner(System.in);
       String queryStr = scanner.next();
       Search(indexDir,queryStr);
    }
 
    public static void Search(String indexDir, String queryStr) throws Exception
    {
       Directory dir = FSDirectory.open(new File(indexDir));
       IndexSearcher searcher = new IndexSearcher(dir);
    QueryParserparser = new QueryParser(Version.LUCENE_30,"content",new     StandardAnalyzer(Version.LUCENE_30));
       Query query = parser.parse(queryStr);
TopDocs hits = searcher.search(query, 10);
       long begin = System.currentTimeMillis();
       TopDocs hits = searcher.search(query, 10);
       long end = System.currentTimeMillis();
       System.out.println("search the word: "+queryStr+".  all search use time is:"+(end-begin)+"。 and get resultnum is : "+hits.totalHits);
       for(ScoreDoc doc :hits.scoreDocs)
       {
           Document document = searcher.doc(doc.doc);
           System.out.println(document.get("filePath"));
       }
    }
}
 


下面還是簡單說一下實現的基本過程:

1.     首先開啟索引檔案,然後輸入要搜尋的關鍵字

2.     建立索引搜尋器IndexSearcher,建立查詢條件QueryParser

3.     利用查詢字串解析字串在索引返回Query物件。

4.     呼叫indexsearcher的search方法進行查詢,返回TopDocs物件。

5.     遍歷查詢得到的結果。


執行一下這個searcher我們會得到以下結果:



當然了。我們這裡只是做一個小小的demo來學習lucene的搜尋元件,讀者自己可以做成web應用程式型別或者桌面應用的型別。這裡我們只是簡單的實現後端程式碼。搜尋過程就在控制檯簡單實現一下。有興趣的讀者可以美化一下。這裡我們也只是簡單的搜尋了一個關鍵字,也沒有進行分詞之類的,在以後的文章中我們會慢慢完善,一點點與應用搜索引擎靠近。


三、索引過程中的核心類


1.     IndexWriter(寫索引)是索引過程中的核心類。這個類負責建立新索引或者開啟已有索引,以及向索引中新增、刪除或者更新被索引文件的資訊。其實這個類可以這樣理解,它就是對索引中的內容進行CRUD的。但不能進行讀取或者搜尋。可以改變索引裡面的內容。

2.     Directrory類描述了lucene索引的存放位置。這個類是一個抽象類,它的子類負責實現指定具體的索引位置。在上面的例子中我們用FSDirectory.open方法來獲取真實檔案在檔案系統的儲存路徑。

3.     Analyzer:IndexWriter不能直接索引文字,這需要先由Analyzer類將文字進行分詞。我們看一下在Lucenein Action 這本書中對lucene的介紹:


 


4. Document:document物件是指一組域的集合,其實他是指一個虛擬的文件。這裡的文件是我們從很多型別檔案中(比如web頁面,郵件內容)中提取相關資訊組成的一些元資料。一個Document 物件由多個Field 物件組成的。可以把一個Document 物件想象成資料庫中的一個記錄,而每個Field 物件就是記錄的一個欄位。

5. Filed索引中的每個文件都包含一個或多個不同命名的域。這些域都包含在field的類中。每個域都有一個域名和對應的域值。就和鍵值對類似。也就是說Field物件是用來描述一個文件的某個屬性的,比如一封電子郵件的標題和內容可以用兩個Field 物件分別描述。


四、搜尋過程的核心類


1. IndexSearcher:這個類類似於一個搜尋元件的大管家,負責對索引的搜尋。它只能以只讀的方式開啟一個索引,所以可以有多個IndexSearcher 的例項在一個索引上進行操作。利用其search方法可以把query類封裝好的查詢關鍵字和條件進行查詢。一個典型的應用例項:




2. Term 是搜尋的基本單位,這個類主要有查詢域名和域值組成。一個Term 物件有兩個String 型別的域組成。生成一個Term 物件可以有如下一條語句來完成:

Term term = newTerm(“fieldName”,”queryWord”); 其中第一個引數代表了要在文件的哪一個 Field 上進行查詢,第二個引數代表了要查詢的關鍵詞。

3. Query:此類主要是一個查詢類的綜合,類中可以指定查詢域和一些查詢條件。這算是一個查詢父類,它有很多子類。最常用的實現類是TermQuery。其餘它還有一些BooleanQuery、TermRangeQuery、FiteredQuery等類。

4. TermQuery:TermQuery 是抽象類 Query 的一個子類,它同時也是 Lucene 支援的最為基本的一個查詢類。生成一個 TermQuery 物件由如下語句完成: TermQuerytermQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的建構函式只接受一個引數,那就是一個 Term 物件。

5.Hits 是用來儲存搜尋的結果的。在搜尋完成之後,需要把搜尋結果返回並顯示給使用者,只有這樣才算是完成搜尋的目的。在lucene中,搜尋的結果的集合是用hits類的例項來表示的。這裡再提一下hits,這也是lucene比較精彩的地方,熟悉hibernate的朋友都知道hibernate有一個延遲載入的屬性,同樣,lucene也有。hits物件也是採用延遲載入的方式返回結果的,當要訪問某個文件時,hits物件就在內部對lucene的索引又進行一次檢索,最後
才將結果返回到頁面顯示。hits物件中主要方法有:
length(): 返回搜尋結果的總數,下面簡單的用法中有用到hit的這一個方法
doc(int n): 返回第n個文件
iterator(): 返回一個迭代器


6.TopDocs: 搜尋結果的容器。TopFieldDocs是其派生類,也是存放搜尋結果的容器。他是一個簡單的指標容器。指標一般指向排名前N項的搜尋結果。


OK,以上就是我們簡單的對Lucene常用核心類的功能簡單說了一下,在以後的文章中幾乎每個類我們都會仔細的研究的,甚至應該會讀一下他的原始碼。所以,有些內容弄不明白大家不用著急。

欲知更多精彩內容,歡迎繼續關注本博。