Lucene入門

Lucene 基礎指南

作者:lighter, 江南白衣

    Lucene是apache組織的一個用java實現全文搜索引擎的開源項目。其功能非常的強大,但api其實很簡單的,它最主要就是做兩件事:建立索引和進行搜索。

1. 建立索引時最重要的幾個術語

  • Document:一個要進行索引的單元,相當於數據庫的一行紀錄,任何想要被索引的數據,都必須轉化爲Document對象存放。
  • Field:Document中的一個字段,相當於數據庫中的Column ,Field是lucene比較多概念一個術語,詳細見後。
  • IndexWriter:負責將Document寫入索引文件。通常情況下,IndexWriter的構造函數包括了以下3個參數:索引存放的路徑,分析器和是否重新創建索引。特別注意的一點,當IndexWriter執行完addDocument方法後,一定要記得調用自身的close方法來關閉它。只有在調用了close方法後,索引器纔會將存放在內在中的所有內容寫入磁盤並關閉輸出流。
  • Analyzer:分析器,主要用於文本分詞。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。
  • Directory:索引存放的位置。lucene提供了兩種索引存放的位置,一種是磁盤,一種是內存。一般情況將索引放在磁盤上;相應地lucene提供了FSDirectory和RAMDirectory兩個類。
  • 段:Segment,是Lucene索引文件的最基本的一個單位。Lucene說到底就是不斷加入新的Segment,然後按一定的規則算法合併不同的Segment以合成新的Segment。

       lucene建立索引的過程就是將待索引的對象轉化爲Lucene的Document對象,使用IndexWriter將其寫入lucene 自定義格式的索引文件中。

       待索引的對象可以來自文件、數據庫等任意途徑,用戶自行編碼遍歷目錄讀取文件或者查詢數據庫表取得ResultSet,Lucene的API只負責和字符串打交道。

1.1 Field 的解釋

從源代碼中,可以看出Field 構造函數如下:

Field(String name, byte[] value, Field.Store store)
Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
Field(String name, String value, Field.Store store, Field.Index index)
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector)

在Field當中有三個內部類:Field.Index,Field.Store,Field.termVector。其中

  • Field.Index有四個屬性,分別是:
    Field.Index.TOKENIZED:分詞索引
    Field.Index.UN_TOKENIZED:分詞進行索引,如作者名,日期等,Rod Johnson本身爲一單詞,不再需要分詞。
    Field.Index:不進行索引,存放不能被搜索的內容如文檔的一些附加屬性如文檔類型, URL等。
    Field.Index.NO_NORMS:;
  • Field.Store也有三個屬性,分別是:
    Field.Store.YES:索引文件本來只存儲索引數據, 此設計將原文內容直接也存儲在索引文件中,如文檔的標題。
    Field.Store.NO:原文不存儲在索引文件中,搜索結果命中後,再根據其他附加屬性如文件的Path,數據庫的主鍵等,重新連接打開原文,適合原文內容較大的情況。
    Field.Store.COMPRESS 壓縮存儲;
  • termVector是Lucene 1.4.3新增的它提供一種向量機制來進行模糊查詢,很少用。

     上面所說的Field屬性與lucene1.4.3版本的有比較大的不同,在舊版的1.4.3裏lucene是通過Field.Keyword(...),FieldUnIndexed(...),FieldUnstored(...)和Field.Text(...)來設置不同字段的類型以達到不同的用途,而當前版本由Field.Index和Field.Store兩個字段的不同組合來達到上述效果。
還有一點說明,其中的兩個構造函數其默認的值爲Field.Store.NO和Field.Index.TOKENIZED。:

Field(String name, Reader reader)
Field(String name, Reader reader, Field.TermVector termVector)
  • 限制Field的長度:
    IndexWriter類提供了一個setMaxFieldLength的方法來對Field的長度進行限制,看一下源代碼就知道其默認值爲10000;我們可以在使用時重新設置此參數。如果使用默認值,那麼Lucene就僅僅對文檔的前面的10000個term進行索引,超過這一個數的文檔就不會被建立索引。

1.2 索引的合併、刪除、優化

  • IndexWriter中的addIndexes方法將索引進行合併;當在不同的地方創建了索引後,如果需要將索引合併,這時候使用addIndexes方法就顯得很有意義。
  • 可以通過IndexReader類從索引中進行文檔的刪除。IndexReader是很特別的一個類,看源代碼就知道它主要是通過自身的靜態方法來完成構造的。示例:
    IndexReader reader = IndexReader.open("C:\\springside");
    reader.deleteDocument(X);                            //這裏的X是一個int的常數;不推薦這一種刪除方法
    reader.deleteDocument(new Term("name","springside"));//這是另一種刪除索引的方法,按字段來刪除,推薦使用這一種做法
    reader.close();
  • 優化索引:可以使用IndexWriter類的optimize方法來進行優先,它會將多個Segment進行合併,組成一個新的Segment,可以加快建立索引後搜索的速度。另外需要注意的一點,optimize方法會降低建立索引的速度,而且要求的磁盤空間會增加。

2. 進行搜索時最常用的幾個術語

  • IndexSearcher:是lucene中最基本的檢索工具,所有的檢索都會用到IndexSearcher工具。初始化IndexSearcher需要設置索引存放的路徑,讓查詢器能定位索引而進行搜索。
  • Query:查詢,lucene中支持模糊查詢,語義查詢,短語查詢,組合查詢等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些類。
  • QueryParser: 是一個解析用戶輸入的工具,可以通過掃描用戶輸入的字符串,生成Query對象。
  • Hits:在搜索完成之後,需要把搜索結果返回並顯示給用戶,只有這樣纔算是完成搜索的目的。在lucene中,搜索的結果的集合是用Hits類的實例來表示的。Hits對象中主要方法有:
    length(): 返回搜索結果的總數,下面簡單的用法中有用到Hit的這一個方法
    doc(int n): 返回第n個文檔
    iterator(): 返回一個迭代器

    這裏再提一下Hits,這也是Lucene比較精彩的地方,熟悉hibernate的朋友都知道hibernate有一個延遲加載的屬性,同樣,Lucene也有。Hits對象也是採用延遲加載的方式返回結果的,當要訪問某個文檔時,Hits對象就在內部對Lucene的索引又進行一次檢索,最後纔將結果返回到頁面顯示。

3. 一個簡單的實例:

首先把lucene的包放在classpath路徑中去,寫下面一個簡單的類:

public class FSDirectoryTest {
    //建立索引的路徑
    public static final String path = "c:\\index2";

    public static void main(String[] args) throws Exception {
        Document doc1 = new Document();
        doc1.add( new Field("name", "lighter springside com",Field.Store.YES,Field.Index.TOKENIZED));

        Document doc2 = new Document();
        doc2.add(new Field("name", "lighter blog",Field.Store.YES,Field.Index.TOKENIZED));

        IndexWriter writer = new IndexWriter(FSDirectory.getDirectory(path, true), new StandardAnalyzer(), true);
        writer.addDocument(doc1);
        writer.addDocument(doc2);
        writer.close();

        IndexSearcher searcher = new IndexSearcher(path);
        Hits hits = null;
        Query query = null;
        QueryParser qp = new QueryParser("name",new StandardAnalyzer());

        query = qp.parse("lighter");
        hits = searcher.search(query);
        System.out.println("查找\"lighter\" 共" + hits.length() + "個結果");

        query = qp.parse("springside");
        hits = searcher.search(query);
        System.out.println("查找\"springside\" 共" + hits.length() + "個結果");

    }
}

執行的結果:

查找"lighter" 共2個結果
查找"springside" 共1個結果

4. 一個複雜一點的實例

  • 在windows系統下的的C盤,建一個名叫s的文件夾,在該文件夾裏面隨便建三個txt文件,隨便起名啦,就叫"1.txt","2.txt"和"3.txt"啦
    其中1.txt的內容如下:
    springside社區
    更大進步,吸引更多用戶,更多貢獻   
    2007年

    而"2.txt"和"3.txt"的內容也可以隨便寫幾寫,這裏懶寫,就複製一個和1.txt文件的內容一樣吧

  • 下載lucene包,放在classpath路徑中,然後建立索引:
    /**
     * author lighter date 2006-8-7
     */
    public class LuceneExample {
    	public static void main(String[] args) throws Exception {
    		
    		File fileDir = new File("c:\\s");     // 指明要索引文件夾的位置,這裏是C盤的S文件夾下 
    		File indexDir = new File("c:\\index"); // 這裏放索引文件的位置
    		File[] textFiles = fileDir.listFiles();
    		
    		Analyzer luceneAnalyzer = new StandardAnalyzer();
    		IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
    	      indexFile(luceneAnalyzer,indexWriter, textFiles);	
    		indexWriter.optimize();//optimize()方法是對索引進行優化
    		indexWriter.close();	
    	}
    	
    	public static void indexFile(Analyzer luceneAnalyzer,IndexWriter indexWriter,File[] textFiles) throws Exception
    	{
    		//增加document到索引去
    		for (int i = 0; i < textFiles.length; i++) {
    			if (textFiles[i].isFile() && textFiles[i].getName().endsWith(".txt")) {
    				String temp = FileReaderAll(textFiles[i].getCanonicalPath(),"GBK");
    				Document document = new Document();
    				Field FieldBody = new Field("body", temp, Field.Store.YES,Field.Index.TOKENIZED);
    				document.add(FieldBody);
    				indexWriter.addDocument(document);
    			}
    		}
    	}
    	public static String FileReaderAll(String FileName, String charset)throws IOException {
    		BufferedReader reader = new BufferedReader(new InputStreamReader(
    				new FileInputStream(FileName), charset));
    		String line = "";
    		String temp = "";
    		while ((line = reader.readLine()) != null) {
    			temp += line;
    		}
    		reader.close();
    		return temp;
    	}
    }
  • 執行查詢:
    public class TestQuery {   
        public static void main(String[] args) throws IOException, ParseException {   
            Hits hits = null;   
            String queryString = "社區";   
            Query query = null;   
            IndexSearcher searcher = new IndexSearcher("c:\\index");   
      
            Analyzer analyzer = new StandardAnalyzer();   
            try {   
                QueryParser qp = new QueryParser("body", analyzer);   
                query = qp.parse(queryString);   
            } catch (ParseException e) {   
            }   
            if (searcher != null) {   
                hits = searcher.search(query);   
                if (hits.length() > 0) {   
                    System.out.println("找到:" + hits.length() + " 個結果!");   
                }   
            }   
        }   
    }
  • 執行結果:
    找到:3 個結果!

5、Hibernate與lucene的結合使用:

參考這一篇文章,裏面講得很詳細
http://wiki.redsaga.com/confluence/display/HART/Hibernate+Lucene+Integration

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