實戰 Lucene,第 1 部分: 初識 Lucene

Lucene 簡介

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

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

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

圖 1. 搜索應用程序和 Lucene 之間的關係數據結構

索引和搜索

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

對文檔創建好索引後,就能夠在這些索引上面進行搜索了。搜索引擎首先會對搜索的關鍵詞進行解析,而後再在創建好的索引上面進行查找,最終返回和用戶輸入的關鍵詞相關聯的文檔。工具

Lucene 軟件包分析

Lucene 軟件包的發佈形式是一個 JAR 文件,下面咱們分析一下這個 JAR 文件裏面的主要的 JAVA 包,使讀者對之有個初步的瞭解。搜索引擎

Package: org.apache.lucene.documentspa

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

Package: org.apache.lucene.analysisorm

這個包主要功能是對文檔進行分詞,由於文檔在創建索引以前必需要進行分詞,因此這個包的做用能夠當作是爲創建索引作準備工做。

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 給出了對某個目錄下的文本文件創建索引的源代碼。

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(Field.Text("path",dataFiles[i].getCanonicalPath())); 
               document.add(Field.Text("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 開發搜索應用程序是否是很簡單。

相關文章
相關標籤/搜索