搜索那些事——細談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 index file 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 make index 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,寫到這裏,我們就來看一下運行這個創建索引程序能看到什麼結果吧。


Center


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

Center

二:搜索相關


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);
        QueryParser parser = new QueryParser(Version.LUCENE_30,"content",new StandardAnalyzer(Version.LUCENE_30));
        Query query = parser.parse(queryStr);
        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 result num 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我們會得到以下結果:


Center


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


三、索引過程中的核心類


1. IndexWriter(寫索引)是索引過程中的核心類。這個類負責創建新索引或者打開已有索引,以及向索引中添加、刪除或者更新被索引文檔的信息。其實這個類可以這樣理解,它就是對索引中的內容進行CRUD的。但不能進行讀取或者搜索。可以改變索引裏面的內容。

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

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


Center



4. Document:document對象是指一組域的集合,其實他是指一個虛擬的文檔。這裏的文檔是我們從很多類型文件中(比如web頁面,郵件內容)中提取相關信息組成的一些元數據。一個Document 對象由多個Field 對象組成的。可以把一個Document 對象想象成數據庫中的一個記錄,而每個Field 對象就是記錄的一個字段。

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


四、搜索過程的核心類


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


Center


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常用核心類的功能簡單說了一下,在以後的文章中幾乎每個類我們都會仔細的研究的,甚至應該會讀一下他的源碼。所以,有些內容弄不明白大家不用着急。

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章