Lucene教程

悠悠隱於市發表於2011-03-18
Lucene教程
文章分類:Java程式設計 
Lucene是apache組織的一個用java實現全文搜尋引擎的開源專案。 其功能非常的強大,api也很簡單。總得來說用Lucene來進行建立 和搜尋和運算元據庫是差不多的(有點像),Document可以看作是 資料庫的一行記錄,Field可以看作是資料庫的欄位。用lucene實 現搜尋引擎就像用JDBC實現連線資料庫一樣簡單。 

Lucene2.0,它與以前廣泛應用和介紹的Lucene 1.4.3並不相容。 Lucene2.0的下載地址是http://apache.justdn.org/lucene/java/ 


例子一 : 

1、在windows系統下的的C盤,建一個名叫s的資料夾,在該資料夾裡面隨便建三個txt檔案,隨便起名啦,就叫"1.txt","2.txt"和"3.txt"啦 
其中1.txt的內容如下: 

中華人民共和國   
全國人民   
2006年   

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

2、下載lucene包,放在classpath路徑中 
建立索引: 
Java程式碼  
package  lighter.iteye.com;      
     
import  java.io.BufferedReader;      
import  java.io.File;      
import  java.io.FileInputStream;      
import  java.io.IOException;      
import  java.io.InputStreamReader;      
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;      
     
/** */ /**     
 * author lighter date 2006-8-7    
  */      
public   class  TextFileIndexer  {      
     public   static   void  main(String[] args)  throws  Exception  {      
         /**/ /*  指明要索引資料夾的位置,這裡是C盤的S資料夾下  */      
        File fileDir  =   new  File( " c:\\s " );      
     
         /**/ /*  這裡放索引檔案的位置  */      
        File indexDir  =   new  File( " c:\\index " );      
        Analyzer luceneAnalyzer  =   new  StandardAnalyzer();      
        IndexWriter indexWriter  =   new  IndexWriter(indexDir, luceneAnalyzer,      
                 true );      
        File[] textFiles  =  fileDir.listFiles();      
         long  startTime  =   new  Date().getTime();      
              
         // 增加document到索引去       
         for  ( int  i  =   0 ; i  <  textFiles.length; i ++ )  {      
             if  (textFiles[i].isFile()      
                     &&  textFiles[i].getName().endsWith( " .txt " ))  {      
                System.out.println( " File  "   +  textFiles[i].getCanonicalPath()      
                         +   " 正在被索引 . " );      
                String temp  =  FileReaderAll(textFiles[i].getCanonicalPath(),      
                         " GBK " );      
                System.out.println(temp);      
                Document document  =   new  Document();      
                Field FieldPath  =   new  Field( " path " , textFiles[i].getPath(),      
                        Field.Store.YES, Field.Index.NO);      
                Field FieldBody  =   new  Field( " body " , temp, Field.Store.YES,      
                        Field.Index.TOKENIZED,      
                        Field.TermVector.WITH_POSITIONS_OFFSETS);      
                document.add(FieldPath);      
                document.add(FieldBody);      
                indexWriter.addDocument(document);      
            }       
        }       
         // optimize()方法是對索引進行優化       
        indexWriter.optimize();      
        indexWriter.close();      
              
         // 測試一下索引的時間       
         long  endTime  =   new  Date().getTime();      
        System.out      
                .println( " 這花費了 "      
                         +  (endTime  -  startTime)      
                         +   "  毫秒來把文件增加到索引裡面去! "      
                         +  fileDir.getPath());      
    }       
     
     public   static  String FileReaderAll(String FileName, String charset)      
             throws  IOException  {      
        BufferedReader reader  =   new  BufferedReader( new  InputStreamReader(      
                 new  FileInputStream(FileName), charset));      
        String line  =   new  String();      
        String temp  =   new  String();      
              
         while  ((line  =  reader.readLine())  !=   null )  {      
            temp  +=  line;      
        }       
        reader.close();      
         return  temp;      
    }       
}     

package  lighter.iteye.com;   
  
import  java.io.BufferedReader;   
import  java.io.File;   
import  java.io.FileInputStream;   
import  java.io.IOException;   
import  java.io.InputStreamReader;   
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;   
  
/** */ /**   
 * author lighter date 2006-8-7  
  */   
public   class  TextFileIndexer  {   
     public   static   void  main(String[] args)  throws  Exception  {   
         /**/ /*  指明要索引資料夾的位置,這裡是C盤的S資料夾下  */   
        File fileDir  =   new  File( " c:\\s " );   
  
         /**/ /*  這裡放索引檔案的位置  */   
        File indexDir  =   new  File( " c:\\index " );   
        Analyzer luceneAnalyzer  =   new  StandardAnalyzer();   
        IndexWriter indexWriter  =   new  IndexWriter(indexDir, luceneAnalyzer,   
                 true );   
        File[] textFiles  =  fileDir.listFiles();   
         long  startTime  =   new  Date().getTime();   
           
         // 增加document到索引去    
         for  ( int  i  =   0 ; i  <  textFiles.length; i ++ )  {   
             if  (textFiles[i].isFile()   
                     &&  textFiles[i].getName().endsWith( " .txt " ))  {   
                System.out.println( " File  "   +  textFiles[i].getCanonicalPath()   
                         +   " 正在被索引 . " );   
                String temp  =  FileReaderAll(textFiles[i].getCanonicalPath(),   
                         " GBK " );   
                System.out.println(temp);   
                Document document  =   new  Document();   
                Field FieldPath  =   new  Field( " path " , textFiles[i].getPath(),   
                        Field.Store.YES, Field.Index.NO);   
                Field FieldBody  =   new  Field( " body " , temp, Field.Store.YES,   
                        Field.Index.TOKENIZED,   
                        Field.TermVector.WITH_POSITIONS_OFFSETS);   
                document.add(FieldPath);   
                document.add(FieldBody);   
                indexWriter.addDocument(document);   
            }    
        }    
         // optimize()方法是對索引進行優化    
        indexWriter.optimize();   
        indexWriter.close();   
           
         // 測試一下索引的時間    
         long  endTime  =   new  Date().getTime();   
        System.out   
                .println( " 這花費了 "   
                         +  (endTime  -  startTime)   
                         +   "  毫秒來把文件增加到索引裡面去! "   
                         +  fileDir.getPath());   
    }    
  
     public   static  String FileReaderAll(String FileName, String charset)   
             throws  IOException  {   
        BufferedReader reader  =   new  BufferedReader( new  InputStreamReader(   
                 new  FileInputStream(FileName), charset));   
        String line  =   new  String();   
        String temp  =   new  String();   
           
         while  ((line  =  reader.readLine())  !=   null )  {   
            temp  +=  line;   
        }    
        reader.close();   
         return  temp;   
    }    
}   



索引的結果: 

File C:\s\ 1 .txt正在被索引 .   
中華人民共和國全國人民2006年   
File C:\s\ 2 .txt正在被索引 .   
中華人民共和國全國人民2006年   
File C:\s\ 3 .txt正在被索引 .   
中華人民共和國全國人民2006年   
這花費了297 毫秒來把文件增加到索引裡面去 ! c:\s   


3、建立了索引之後,查詢啦.... 
Java程式碼  
package  lighter.iteye.com;      
     
import  java.io.IOException;      
     
import  org.apache.lucene.analysis.Analyzer;      
import  org.apache.lucene.analysis.standard.StandardAnalyzer;      
import  org.apache.lucene.queryParser.ParseException;      
import  org.apache.lucene.queryParser.QueryParser;      
import  org.apache.lucene.search.Hits;      
import  org.apache.lucene.search.IndexSearcher;      
import  org.apache.lucene.search.Query;      
     
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()  +   "  個結果! " );      
            }       
        }       
    }     
     
}     

package  lighter.iteye.com;   
  
import  java.io.IOException;   
  
import  org.apache.lucene.analysis.Analyzer;   
import  org.apache.lucene.analysis.standard.StandardAnalyzer;   
import  org.apache.lucene.queryParser.ParseException;   
import  org.apache.lucene.queryParser.QueryParser;   
import  org.apache.lucene.search.Hits;   
import  org.apache.lucene.search.IndexSearcher;   
import  org.apache.lucene.search.Query;   
  
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  個結果 ! 



Lucene 其實很簡單的,它最主要就是做兩件事:建立索引和進行搜尋 
來看一些在lucene中使用的術語,這裡並不打算作詳細的介紹,只是點一下而已----因為這一個世界有一種好東西,叫搜尋。 

IndexWriter:lucene中最重要的的類之一,它主要是用來將文件加入索引,同時控制索引過程中的一些引數使用。 

Analyzer:分析器,主要用於分析搜尋引擎遇到的各種文字。常用的有StandardAnalyzer分析器,StopAnalyzer分析器,WhitespaceAnalyzer分析器等。 

Directory:索引存放的位置;lucene提供了兩種索引存放的位置,一種是磁碟,一種是記憶體。一般情況將索引放在磁碟上;相應地lucene提供了FSDirectory和RAMDirectory兩個類。 

Document:文件;Document相當於一個要進行索引的單元,任何可以想要被索引的檔案都必須轉化為Document物件才能進行索引。 

Field:欄位。 

IndexSearcher:是lucene中最基本的檢索工具,所有的檢索都會用到IndexSearcher工具; 

Query:查詢,lucene中支援模糊查詢,語義查詢,短語查詢,組合查詢等等,如有TermQuery,BooleanQuery,RangeQuery,WildcardQuery等一些類。 

QueryParser: 是一個解析使用者輸入的工具,可以通過掃描使用者輸入的字串,生成Query物件。 

Hits:在搜尋完成之後,需要把搜尋結果返回並顯示給使用者,只有這樣才算是完成搜尋的目的。在lucene中,搜尋的結果的集合是用Hits類的例項來表示的。 

上面作了一大堆名詞解釋,下面就看幾個簡單的例項吧: 
1、簡單的的StandardAnalyzer測試例子 


Java程式碼  
package  lighter.iteye.com;      
     
import  java.io.IOException;      
import  java.io.StringReader;      
     
import  org.apache.lucene.analysis.Analyzer;      
import  org.apache.lucene.analysis.Token;      
import  org.apache.lucene.analysis.TokenStream;      
import  org.apache.lucene.analysis.standard.StandardAnalyzer;      
     
public   class  StandardAnalyzerTest       
{      
     // 建構函式,       
     public  StandardAnalyzerTest()      
     {      
    }       
     public   static   void  main(String[] args)       
     {      
         // 生成一個StandardAnalyzer物件       
        Analyzer aAnalyzer  =   new  StandardAnalyzer();      
         // 測試字串       
        StringReader sr  =   new  StringReader( " lighter javaeye com is the are on " );      
         // 生成TokenStream物件       
        TokenStream ts  =  aAnalyzer.tokenStream( " name " , sr);       
         try   {      
             int  i = 0 ;      
            Token t  =  ts.next();      
             while (t != null )      
             {      
                 // 輔助輸出時顯示行號       
                i ++ ;      
                 // 輸出處理後的字元       
                System.out.println( " 第 " + i + " 行: " + t.termText());      
                 // 取得下一個字元       
                t = ts.next();      
            }       
        }   catch  (IOException e)  {      
            e.printStackTrace();      
        }       
    }       
}      

package  lighter.iteye.com;   
  
import  java.io.IOException;   
import  java.io.StringReader;   
  
import  org.apache.lucene.analysis.Analyzer;   
import  org.apache.lucene.analysis.Token;   
import  org.apache.lucene.analysis.TokenStream;   
import  org.apache.lucene.analysis.standard.StandardAnalyzer;   
  
public   class  StandardAnalyzerTest    
{   
     // 建構函式,    
     public  StandardAnalyzerTest()   
     {   
    }    
     public   static   void  main(String[] args)    
     {   
         // 生成一個StandardAnalyzer物件    
        Analyzer aAnalyzer  =   new  StandardAnalyzer();   
         // 測試字串    
        StringReader sr  =   new  StringReader( " lighter javaeye com is the are on " );   
         // 生成TokenStream物件    
        TokenStream ts  =  aAnalyzer.tokenStream( " name " , sr);    
         try   {   
             int  i = 0 ;   
            Token t  =  ts.next();   
             while (t != null )   
             {   
                 // 輔助輸出時顯示行號    
                i ++ ;   
                 // 輸出處理後的字元    
                System.out.println( " 第 " + i + " 行: " + t.termText());   
                 // 取得下一個字元    
                t = ts.next();   
            }    
        }   catch  (IOException e)  {   
            e.printStackTrace();   
        }    
    }    
}    


顯示結果: 

第1行:lighter 
第2行:javaeye 
第3行:com 

提示一下: 
StandardAnalyzer是lucene中內建的"標準分析器",可以做如下功能: 
1、對原有句子按照空格進行了分詞 
2、所有的大寫字母都可以能轉換為小寫的字母 
3、可以去掉一些沒有用處的單詞,例如"is","the","are"等單詞,也刪除了所有的標點 
檢視一下結果與"new StringReader("lighter javaeye com is the are on")"作一個比較就清楚明瞭。 
這裡不對其API進行解釋了,具體見lucene的官方文件。需要注意一點,這裡的程式碼使用的是lucene2的API,與1.43版有一些明顯的差別。 

2、看另一個例項,簡單地建立索引,進行搜尋 
Java程式碼  
package  lighter.iteye.com;      
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;      
import  org.apache.lucene.queryParser.QueryParser;      
import  org.apache.lucene.search.Hits;      
import  org.apache.lucene.search.IndexSearcher;      
import  org.apache.lucene.search.Query;      
import  org.apache.lucene.store.FSDirectory;      
     
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 javaeye 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.setMaxFieldLength( 3 );      
        writer.addDocument(doc1);      
        writer.setMaxFieldLength( 3 );      
        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( " javaeye " );      
        hits  =  searcher.search(query);      
        System.out.println( " 查詢\ " javaeye\ "  共 "   +  hits.length()  +   " 個結果 " );      
     
    }       
     
}     

package  lighter.iteye.com;   
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;   
import  org.apache.lucene.queryParser.QueryParser;   
import  org.apache.lucene.search.Hits;   
import  org.apache.lucene.search.IndexSearcher;   
import  org.apache.lucene.search.Query;   
import  org.apache.lucene.store.FSDirectory;   
  
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 javaeye 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.setMaxFieldLength( 3 );   
        writer.addDocument(doc1);   
        writer.setMaxFieldLength( 3 );   
        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( " javaeye " );   
        hits  =  searcher.search(query);   
        System.out.println( " 查詢\ " javaeye\ "  共 "   +  hits.length()  +   " 個結果 " );   
  
    }    
  
}   


執行結果: 

查詢 " lighter "  共2個結果   
查詢 " javaeye "  共1個結果  


到現在我們已經可以用lucene建立索引了 
下面介紹一下幾個功能來完善一下: 
1.索引格式 

其實索引目錄有兩種格式, 

一種是除配置檔案外,每一個Document獨立成為一個檔案(這種搜尋起來會影響速度)。 

另一種是全部的Document成一個檔案,這樣屬於複合模式就快了。 

2.索引檔案可放的位置: 

索引可以存放在兩個地方1.硬碟,2.記憶體 
放在硬碟上可以用FSDirectory(),放在記憶體的用RAMDirectory()不過一關機就沒了 

FSDirectory.getDirectory(File file,  boolean  create) 
FSDirectory.getDirectory(String path,  boolean  create) 

兩個工廠方法返回目錄 
New RAMDirectory()就直接可以 
再和 

IndexWriter(Directory d, Analyzer a,  boolean  create) 

一配合就行了 
如: 

Java程式碼  
IndexWrtier indexWriter  =   new  IndexWriter(FSDirectory.getDirectory(“c:\\index”, true ), new  StandardAnlyazer(), true );   
IndexWrtier indexWriter  =   new  IndexWriter( new  RAMDirectory(), new  StandardAnlyazer(), true );   

IndexWrtier indexWriter  =   new  IndexWriter(FSDirectory.getDirectory(“c:\\index”, true ), new  StandardAnlyazer(), true );
IndexWrtier indexWriter  =   new  IndexWriter( new  RAMDirectory(), new  StandardAnlyazer(), true ); 

3.索引的合併 
這個可用 

IndexWriter.addIndexes(Directory[] dirs) 

將目錄加進去 
來看個例子: 
Java程式碼  
public   void  UniteIndex()  throws  IOException   
     {   
        IndexWriter writerDisk  =   new  IndexWriter(FSDirectory.getDirectory( " c:\\indexDisk " ,  true ), new  StandardAnalyzer(), true );   
        Document docDisk  =   new  Document();   
        docDisk.add( new  Field( " name " , " 程式設計師之家 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writerDisk.addDocument(docDisk);   
        RAMDirectory ramDir  =   new  RAMDirectory();   
        IndexWriter writerRam  =   new  IndexWriter(ramDir, new  StandardAnalyzer(), true );   
        Document docRam  =   new  Document();   
        docRam.add( new  Field( " name " , " 程式設計師雜誌 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writerRam.addDocument(docRam);   
        writerRam.close(); // 這個方法非常重要,是必須呼叫的    
        writerDisk.addIndexes( new  Directory[] {ramDir} );   
        writerDisk.close();   
    }    
     public   void  UniteSearch()  throws  ParseException, IOException   
     {   
        QueryParser queryParser  =   new  QueryParser( " name " , new  StandardAnalyzer());   
        Query query  =  queryParser.parse( " 程式設計師 " );   
        IndexSearcher indexSearcher  = new  IndexSearcher( " c:\\indexDisk " );   
        Hits hits  =  indexSearcher.search(query);   
        System.out.println( " 找到了 " + hits.length() + " 結果 " );   
         for ( int  i = 0 ;i   
         {   
            Document doc  =  hits.doc(i);   
            System.out.println(doc.get( " name " ));   
        }    
}   

public   void  UniteIndex()  throws  IOException
     {
        IndexWriter writerDisk  =   new  IndexWriter(FSDirectory.getDirectory( " c:\\indexDisk " ,  true ), new  StandardAnalyzer(), true );
        Document docDisk  =   new  Document();
        docDisk.add( new  Field( " name " , " 程式設計師之家 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writerDisk.addDocument(docDisk);
        RAMDirectory ramDir  =   new  RAMDirectory();
        IndexWriter writerRam  =   new  IndexWriter(ramDir, new  StandardAnalyzer(), true );
        Document docRam  =   new  Document();
        docRam.add( new  Field( " name " , " 程式設計師雜誌 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writerRam.addDocument(docRam);
        writerRam.close(); // 這個方法非常重要,是必須呼叫的 
        writerDisk.addIndexes( new  Directory[] {ramDir} );
        writerDisk.close();
    } 
     public   void  UniteSearch()  throws  ParseException, IOException
     {
        QueryParser queryParser  =   new  QueryParser( " name " , new  StandardAnalyzer());
        Query query  =  queryParser.parse( " 程式設計師 " );
        IndexSearcher indexSearcher  = new  IndexSearcher( " c:\\indexDisk " );
        Hits hits  =  indexSearcher.search(query);
        System.out.println( " 找到了 " + hits.length() + " 結果 " );
         for ( int  i = 0 ;i
         {
            Document doc  =  hits.doc(i);
            System.out.println(doc.get( " name " ));
        } 
} 


這個例子是將記憶體中的索引合併到硬碟上來. 
注意:合併的時候一定要將被合併的那一方的IndexWriter的close()方法呼叫。 

4.對索引的其它操作: 
IndexReader類是用來操作索引的,它有對Document,Field的刪除等操作。 
下面一部分的內容是:全文的搜尋 
全文的搜尋主要是用:IndexSearcher,Query,Hits,Document(都是Query的子類),有的時候用QueryParser 
主要步驟: 

1 . new  QueryParser(Field欄位, new  分析器) 
2 .Query query  =  QueryParser.parser(“要查詢的字串”);這個地方我們可以用反射api看一下query究竟是什麼型別 
3 . new  IndexSearcher(索引目錄).search(query);返回Hits 
4 .用Hits.doc(n);可以遍歷出Document 
5 .用Document可得到Field的具體資訊了。 

其實1 ,2兩步就是為了弄出個Query 例項,究竟是什麼型別的看分析器了。 

拿以前的例子來說吧 

Java程式碼  
QueryParser queryParser  =   new  QueryParser( " name " , new  StandardAnalyzer());   
        Query query  =  queryParser.parse( " 程式設計師 " );   
/**/ /* 這裡返回的就是org.apache.lucene.search.PhraseQuery */    
        IndexSearcher indexSearcher  = new  IndexSearcher( " c:\\indexDisk " );   
        Hits hits  =  indexSearcher.search(query);   

QueryParser queryParser  =   new  QueryParser( " name " , new  StandardAnalyzer());
        Query query  =  queryParser.parse( " 程式設計師 " );
/**/ /* 這裡返回的就是org.apache.lucene.search.PhraseQuery */ 
        IndexSearcher indexSearcher  = new  IndexSearcher( " c:\\indexDisk " );
        Hits hits  =  indexSearcher.search(query); 


不管是什麼型別,無非返回的就是Query的子類,我們完全可以不用這兩步直接new個Query的子類的例項就ok了,不過一般還是用這兩步因為它返回的是PhraseQuery這個是非常強大的query子類它可以進行多字搜尋用QueryParser可以設定各個關鍵字之間的關係這個是最常用的了。 
IndexSearcher: 
其實IndexSearcher它內部自帶了一個IndexReader用來讀取索引的,IndexSearcher有個close()方法,這個方法不是用來關閉IndexSearche的是用來關閉自帶的IndexReader。 

QueryParser呢可以用parser.setOperator()來設定各個關鍵字之間的關係(與還是或)它可以自動通過空格從字串裡面將關鍵字分離出來。 
注意:用QueryParser搜尋的時候分析器一定的和建立索引時候用的分析器是一樣的。 
Query: 
可以看一個lucene2.0的幫助文件有很多的子類: 
BooleanQuery, ConstantScoreQuery, ConstantScoreRangeQuery, DisjunctionMaxQuery, FilteredQuery, MatchAllDocsQuery, MultiPhraseQuery, MultiTermQuery, PhraseQuery, PrefixQuery, RangeQuery, SpanQuery, TermQuery 
各自有用法看一下文件就能知道它們的用法了 
下面一部分講一下lucene的分析器: 
分析器是由分詞器和過濾器組成的,拿英文來說吧分詞器就是通過空格把單詞分開,過濾器就是把the,to,of等詞去掉不被搜尋和索引。 
我們最常用的是StandardAnalyzer()它是lucene的標準分析器它整合了內部的許多的分析器。 
最後一部分了:lucene的高階搜尋了 
1.排序 
Lucene有內建的排序用IndexSearcher.search(query,sort)但是功能並不理想。我們需要自己實現自定義的排序。 
這樣的話得實現兩個介面: ScoreDocComparator, SortComparatorSource 
用IndexSearcher.search(query,new Sort(new SortField(String Field,SortComparatorSource))); 
就看個例子吧: 
這是一個建立索引的例子: 

Java程式碼  
public   void  IndexSort()  throws  IOException   
{   
        IndexWriter writer  =   new  IndexWriter( " C:\\indexStore " , new  StandardAnalyzer(), true );   
        Document doc  =   new  Document()   
        doc.add( new  Field( " sort " , " 1 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 4 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 3 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 5 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 9 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 6 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        doc  =   new  Document();   
        doc.add( new  Field( " sort " , " 7 " ,Field.Store.YES,Field.Index.TOKENIZED));   
        writer.addDocument(doc);   
        writer.close();   
}   

public   void  IndexSort()  throws  IOException
{
        IndexWriter writer  =   new  IndexWriter( " C:\\indexStore " , new  StandardAnalyzer(), true );
        Document doc  =   new  Document()
        doc.add( new  Field( " sort " , " 1 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 4 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 3 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 5 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 9 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 6 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        doc  =   new  Document();
        doc.add( new  Field( " sort " , " 7 " ,Field.Store.YES,Field.Index.TOKENIZED));
        writer.addDocument(doc);
        writer.close();
} 



下面是搜尋的例子: 
Java程式碼  
public void SearchSort1() throws IOException, ParseException   
{   
        IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");   
        QueryParser queryParser = new QueryParser("sort",new StandardAnalyzer());   
        Query query = queryParser.parse("4");   
          
        Hits hits = indexSearcher.search(query);   
        System.out.println("有"+hits.length()+"個結果");   
        Document doc = hits.doc(0);   
        System.out.println(doc.get("sort"));   
}   
public void SearchSort2() throws IOException, ParseException   
{   
        IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");   
        Query query = new RangeQuery(new Term("sort","1"),new Term("sort","9"),true);//這個地方前面沒有提到,它是用於範圍的Query可以看一下幫助文件.   
        Hits hits = indexSearcher.search(query,new Sort(new SortField("sort",new MySortComparatorSource())));   
        System.out.println("有"+hits.length()+"個結果");   
        for(int i=0;i   
        {   
            Document doc = hits.doc(i);   
            System.out.println(doc.get("sort"));   
        }   
}   
public class MyScoreDocComparator implements ScoreDocComparator   
{   
    private Integer[]sort;   
    public MyScoreDocComparator(String s,IndexReader reader, String fieldname) throws IOException   
    {   
        sort = new Integer[reader.maxDoc()];   
        for(int i = 0;i   
        {   
            Document doc =reader.document(i);   
            sort[i]=new Integer(doc.get("sort"));   
        }   
    }   
    public int compare(ScoreDoc i, ScoreDoc j)   
    {   
        if(sort[i.doc]>sort[j.doc])   
            return 1;   
        if(sort[i.doc]   
            return -1;   
        return 0;   
    }   
    public int sortType()   
    {   
        return SortField.INT;   
    }   
    public Comparable sortValue(ScoreDoc i)   
    {   
        // TODO 自動生成方法存根   
        return new Integer(sort[i.doc]);   
    }   
}   
public class MySortComparatorSource implements SortComparatorSource   
{   
    private static final long serialVersionUID = -9189690812107968361L;   
    public ScoreDocComparator newComparator(IndexReader reader, String fieldname)   
            throws IOException   
    {   
        if(fieldname.equals("sort"))   
            return new MyScoreDocComparator("sort",reader,fieldname);   
        return null;   
    }   
}  

public void SearchSort1() throws IOException, ParseException
{
        IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");
        QueryParser queryParser = new QueryParser("sort",new StandardAnalyzer());
        Query query = queryParser.parse("4");
       
        Hits hits = indexSearcher.search(query);
        System.out.println("有"+hits.length()+"個結果");
        Document doc = hits.doc(0);
        System.out.println(doc.get("sort"));
}
public void SearchSort2() throws IOException, ParseException
{
        IndexSearcher indexSearcher = new IndexSearcher("C:\\indexStore");
        Query query = new RangeQuery(new Term("sort","1"),new Term("sort","9"),true);//這個地方前面沒有提到,它是用於範圍的Query可以看一下幫助文件.
        Hits hits = indexSearcher.search(query,new Sort(new SortField("sort",new MySortComparatorSource())));
        System.out.println("有"+hits.length()+"個結果");
        for(int i=0;i
        {
            Document doc = hits.doc(i);
            System.out.println(doc.get("sort"));
        }
}
public class MyScoreDocComparator implements ScoreDocComparator
{
    private Integer[]sort;
    public MyScoreDocComparator(String s,IndexReader reader, String fieldname) throws IOException
    {
        sort = new Integer[reader.maxDoc()];
        for(int i = 0;i
        {
            Document doc =reader.document(i);
            sort[i]=new Integer(doc.get("sort"));
        }
    }
    public int compare(ScoreDoc i, ScoreDoc j)
    {
        if(sort[i.doc]>sort[j.doc])
            return 1;
        if(sort[i.doc]
            return -1;
        return 0;
    }
    public int sortType()
    {
        return SortField.INT;
    }
    public Comparable sortValue(ScoreDoc i)
    {
        // TODO 自動生成方法存根
        return new Integer(sort[i.doc]);
    }
}
public class MySortComparatorSource implements SortComparatorSource
{
    private static final long serialVersionUID = -9189690812107968361L;
    public ScoreDocComparator newComparator(IndexReader reader, String fieldname)
            throws IOException
    {
        if(fieldname.equals("sort"))
            return new MyScoreDocComparator("sort",reader,fieldname);
        return null;
    }
}
SearchSort1()輸出的結果沒有排序,SearchSort2()就排序了。 
2.多域搜尋MultiFieldQueryParser 
如果想輸入關鍵字而不想關心是在哪個Field裡的就可以用MultiFieldQueryParser了 
用它的建構函式即可後面的和一個Field一樣。 
MultiFieldQueryParser. parse(String[] queries, String[] fields, BooleanClause.Occur[] flags, Analyzer analyzer)                                          ~~~~~~~~~~~~~~~~~ 
第三個引數比較特殊這裡也是與以前lucene1.4.3不一樣的地方 
看一個例子就知道了 
String[] fields = {"filename", "contents", "description"}; 
BooleanClause.Occur[] flags = {BooleanClause.Occur.SHOULD, 
                BooleanClause.Occur.MUST,//在這個Field裡必須出現的 
                BooleanClause.Occur.MUST_NOT};//在這個Field裡不能出現 
MultiFieldQueryParser.parse("query", fields, flags, analyzer); 

1、lucene的索引不能太大,要不然效率會很低。大於1G的時候就必須考慮分佈索引的問題 

2、不建議用多執行緒來建索引,產生的互鎖問題很麻煩。經常發現索引被lock,無法重新建立的情況 

3、中文分詞是個大問題,目前免費的分詞效果都很差。如果有能力還是自己實現一個分詞模組,用最短路徑的切分方法,網上有教材和demo原始碼,可以參考。 

4、建增量索引的時候很耗cpu,在訪問量大的時候會導致cpu的idle為0 

5、預設的評分機制不太合理,需要根據自己的業務定製 



整體來說lucene要用好不容易,必須在上述方面擴充他的功能,才能作為一個商用的搜尋引擎 


本文來自CSDN部落格:http://blog.csdn.net/neusoftware_20063500/archive/2009/03/08/3969365.aspx 

 

相關文章