seaairland

           

          Lucene之起源現狀及初步應用

          作者: 陳光--ZDNet reader
          2004-09-09 03:11 PM

          本文是Lucene研究文集的首篇,主要介紹了Lucene的起源、發展、現狀,以及Luence的初步應用,可以作為了解和學習Lucene的入門資料。

          ?

          1. 起源與發展

          ?

          Lucene是一個高性能、純Java的全文檢索引擎,而且免費、開源。Lucene幾乎適合于任何需要全文檢索的應用,尤其是跨平臺的應用。

          ?

          Lucene的作者Doug Cutting是一個資深的全文檢索專家,剛開始,Doug Cutting將Lucene發表在自己的主頁上,2000年3月將其轉移到sourceforge,于2001年10捐獻給Apache,作為Jakarta的一個子工程。

          ?

          2 .使用現狀

          ?

          經過多年的發展,Lucene在全文檢索領域已經有了很多的成功案例,并積累了良好的聲譽。

          ?

          基于Lucene的全文檢索產品(Lucene本身只是一個組件,而非一個完整的應用)和應用Lucene的項目在世界各地已經非常之多,比較知名的有:

          l???????? Eclipse:主流Java開發工具,其幫助文檔采用Lucene作為檢索引擎

          l???????? Jive:知名論壇系統,其檢索功能基于Lucene

          l???????? Ifinder:出自德國的網站檢索系統,基于Lucene(http://ifinder.intrafind.org/)

          l???????? MIT DSpace Federation:一個文檔管理系統(http://www.dspace.org/)

          ?

          國內外采用Lucene作為網站全文檢索引擎的也很多,比較知名的有:

          l???????? http://www.blogchina.com/weblucene/

          l???????? http://www.ioffer.com/

          l???????? http://search.soufun.com/

          l???????? http://www.taminn.com/

          ?

          (更多案例,請參見http://wiki.apache.org/jakarta-lucene/PoweredBy)

          ?

          在所有這些案例中,開源應用占了很大一部分,但更多的還是商化業產品和網站。毫不夸張的說,Lucene的出現,極大的推動了全文檢索技術在各個行業或領域中的深層次應用。

          ?

          3 .初步應用

          ?

          前面提到,Lucene本身只是一個組件,而非一個完整的應用,所以若想讓Lucene跑起來,還得在Lucene基礎上進行必要的二次開發

          下載與安裝

          首先,你需要到Lucene的官方網站http://jakarta.apache.org/lucene/去下載一份拷貝,最新版是1.4。下載后將得到一個名為lucene-1.4-final.zip的壓縮文件,將其解壓,里面有一個名為lucene-1.4-final.jar的文件,這就是Lucene組件包了,若需要在項目使用Lucene,只需要把lucene-1.4-final.jar置于類路徑下即可,至于解壓后的其他文件都是參考用的。

          接下來,我用Eclipse建立一個工程,實現基于Lucene的建庫、記錄加載和記錄查詢等功能。

          ?

          如上圖所示,這是開發完成后的工程,其中有三個源文件CreateDataBase.java,InsertRecords.java,QueryRecords.java,分別實現建庫、入庫、檢索的功能。

          ?

          以下是對這三個源文件的分析。

          CreateDataBase.java

          packagecom.holen.part1;

          ?

          importjava.io.File;

          importorg.apache.lucene.analysis.standard.StandardAnalyzer;

          importorg.apache.lucene.index.IndexWriter;

          ?

          /**

          ?* @authorHolenChen

          ?*初始化檢索庫

          ?*/

          public classCreateDataBase{

          ?

          ??? publicCreateDataBase(){??

          ??? }

          ???

          ??? publicintcreateDataBase(Filefile){

          ?????? intreturnValue=0;

          ?????? if(!file.isDirectory()){

          ?????????? file.mkdirs();

          ?????? }

          ?????? try{

          ?????????? IndexWriterindexWriter= newIndexWriter(file,newStandardAnalyzer(),true);

          ?????????? indexWriter.close();

          ?????????? returnValue=1;

          ?????? }catch(Exceptionex){

          ?????????? ex.printStackTrace();

          ?????? }

          ?????? returnreturnValue;

          ??? }

          ???

          ??? /**

          ??? ?*傳入檢索庫路徑,初始化庫

          ??? ?* @paramfile

          ??? ?* @return

          ??? ?*/

          ??? publicintcreateDataBase(Stringfile){

          ?????? returnthis.createDataBase(newFile(file));??

          ??? }

          ?

          ??? publicstaticvoidmain(String[]args){

          ?????? CreateDataBasetemp= newCreateDataBase();

          ?????? if(temp.createDataBase("e:\\lucene\\holendb")==1){

          ?????????? System.out.println("db init succ");

          ?????? }

          ??? }

          }

          ?

          ?

          說明:這里最關鍵的語句是IndexWriterindexWriter= newIndexWriter(file,newStandardAnalyzer(),true)。

          ?

          第一個參數是庫的路徑,也就是說你準備把全文檢索庫保存在哪個位置,比如main方法中設定的“e:\\lucene\\holendb”,Lucene支持多庫,且每個庫的位置允許不同。

          ?

          第二個參數是分析器,這里采用的是Lucene自帶的標準分析器,分析器用于對整篇文章進行分詞解析,這里的標準分析器實現對英文(或拉丁文,凡是由字母組成,由空格分開的文字均可)的分詞,分析器將把整篇英文按空格切成一個個的單詞(在全文檢索里這叫切詞,切詞是全文檢索的核心技術之一,Lucene默認只能切英文或其他拉丁文,默認不支持中日韓等雙字節文字,關于中文切詞技術將在后續章節重點探討)。

          ?

          第三個參數是是否初始化庫,這里我設的是true,true意味著新建庫或覆蓋已經存在的庫,false意味著追加到已經存在的庫。這里新建庫,所以肯定需要初始化,初始化后,庫目錄下只存在一個名為segments的文件,大小為1k。但是當庫中存在記錄時執行初始化,庫中內容將全部丟失,庫回復到初始狀態,即相當于新建了該庫,所以真正做項目時,該方法一定要慎用。

          [被屏蔽廣告]

          加載記錄源碼及說明

          ?

          InsertRecords.java

          packagecom.holen.part1;

          ?

          importjava.io.File;

          importjava.io.FileReader;

          importjava.io.Reader;

          importorg.apache.lucene.analysis.standard.StandardAnalyzer;

          importorg.apache.lucene.document.Document;

          importorg.apache.lucene.document.Field;

          importorg.apache.lucene.index.IndexWriter;

          ?

          /**

          ?* @authorHolenChen

          ?*記錄加載

          ?*/

          public classInsertRecords{

          ?

          ??? publicInsertRecords(){

          ??? }

          ???

          ??? publicintinsertRecords(Stringdbpath,Filefile){

          ?????? intreturnValue=0;

          ?????? try{

          ?????????? IndexWriterindexWriter

          ?????????? ?= newIndexWriter(dbpath,newStandardAnalyzer(),false);

          ?????????? this.addFiles(indexWriter,file);

          ?????????? returnValue=1;

          ?????? }catch(Exceptionex){

          ?????????? ex.printStackTrace();

          ?????? }

          ?????? returnreturnValue;

          ??? }

          ???

          ??? /**

          ??? ?*傳入需加載的文件名

          ??? ?* @paramfile

          ??? ?* @return

          ??? ?*/

          ??? publicintinsertRecords(Stringdbpath,Stringfile){

          ?????? returnthis.insertRecords(dbpath,newFile(file));

          ??? }

          ???

          ??? publicvoidaddFiles(IndexWriterindexWriter,Filefile){

          ?????? Documentdoc= newDocument();

          ?????? try{

          ?????????? doc.add(Field.Keyword("filename",file.getName()));??

          ?????????????????

          ?????????? //以下兩句只能取一句,前者是索引不存儲,后者是索引且存儲

          ?????????? //doc.add(Field.Text("content",new FileReader(file)));?

          ?????? ??? doc.add(Field.Text("content",this.chgFileToString(file)));

          ??????????

          ?????????? indexWriter.addDocument(doc);

          ?????????? indexWriter.close();

          ?????? }catch(Exceptionex){

          ?????????? ex.printStackTrace();

          ?????? }

          ??? }

          ???

          ??? /**

          ??? ?*從文本文件中讀取內容

          ??? ?* @paramfile

          ??? ?* @return

          ??? ?*/

          ??? publicStringchgFileToString(Filefile){

          ?????? StringreturnValue= null;

          ?????? StringBuffersb= newStringBuffer();

          ?????? char[]c= newchar[4096];

          ?????? try{

          ?????????? Readerreader= newFileReader(file);

          ?????????? intn=0;

          ?????????? while(true){????????????

          ????????????? n=reader.read(c);

          ????????????? if(n>0){

          ????????????????? sb.append(c,0,n);

          ????????????? }else{

          ????????????????? break;

          ????????????? }

          ?????????? }

          ?????????? reader.close();

          ?????? }catch(Exceptionex){

          ?????????? ex.printStackTrace();

          ?????? }

          ?????? returnValue=sb.toString();

          ?????? returnreturnValue;?

          ??? }

          ?

          ??? publicstaticvoidmain(String[]args){

          ?????? InsertRecordstemp= newInsertRecords();

          ?????? Stringdbpath="e:\\lucene\\holendb";

          ?????? //holen1.txt中包含關鍵字"holen"和"java"

          ?????? if(temp.insertRecords(dbpath,"e:\\lucene\\holen1.txt")==1){

          ?????????? System.out.println("add file1 succ");

          ?????? }

          ?????? //holen2.txt中包含關鍵字"holen"和"chen"

          ?????? if(temp.insertRecords(dbpath,"e:\\lucene\\holen2.txt")==1){

          ?????????? System.out.println("add file2 succ");

          ?????? }??

          ??? }

          }


          說明:這個類里面主要有3個方法insertRecords(Stringdbpath,Filefile),addFiles(IndexWriterindexWriter,Filefile),chgFileToString(Filefile)。

          ?

          ChgFileToString方法用于讀取文本型文件到一個String變量中。

          ?

          InsertRecords方法用于加載一條記錄,這里是將單個文件入全文檢索庫,第一個參數是庫路徑,第二個參數是需要入庫的文件。

          ?

          InsertRecords需要調用addFiles,addFiles是文件入庫的真正執行者。AddFiles里有如下幾行重點代碼:

          doc.add(Field.Keyword("filename",file.getName()));

          注意,在Lucene里沒有嚴格意義上表,Lucene的表是通過Field類的方法動態構建的,比如Field.Keyword("filename",file.getName())就相當于在一條記錄加了一個字段,字段名為filename,該字段的內容為file.getName()。

          ?

          ?

          常用的Field方法如下:

          方法

          切詞

          索引

          存儲

          用途

          Field.Text(String name, String value)

          Y

          Y

          Y

          標題,文章內容

          Field.Text(String name, Reader value)

          Y

          Y

          N

          META信息

          Field.Keyword(String name, String value)

          N

          Y

          Y

          作者

          Field.UnIndexed(String name, String value)

          N

          N

          Y

          文件路徑

          Field.UnStored(String name, String value)

          Y

          Y

          N

          與第二種類似

          ?

          為了更深入的了解全文檢索庫,我們可以將全文檢索庫與通常的關系型數據庫(如Oracle,Mysql)作一下對比。

          ?

          全文檢索庫對關系型數據庫對比

          對比項

          全文檢索庫(Lucene)

          關系型數據庫(Oracle)

          核心功能

          以文本檢索為主,插入(insert)、刪除(delete)、修改(update)比較麻煩,適合于大文本塊的查詢。

          插入(insert)、刪除(delete)、修改(update)十分方便,有專門的SQL命令,但對于大文本塊(如CLOB)類型的檢索效率低下。

          與Oracle類似,都可以建多個庫,且各個庫的存儲位置可以不同。

          可以建多個庫,每個庫一般都有控制文件和數據文件等,比較復雜。

          沒有嚴格的表的概念,比如Lucene的表只是由入庫時的定義字段松散組成。

          有嚴格的表結構,有主鍵,有字段類型等。

          記錄

          由于沒有嚴格表的概念,所以記錄體現為一個對象,在Lucene里記錄對應的類是Document。

          Record,與表結構對應。

          字段

          字段類型只有文本和日期兩種,字段一般不支持運算,更無函數功能。

          在Lucene里字段的類是Field,如document(field1,field2…)

          字段類型豐富,功能強大。

          record(field1,field2…)

          查詢結果集

          在Lucene里表示查詢結果集的類是Hits,如hits(doc1,doc2,doc3…)

          在JDBC為例, Resultset(record1,record2,record3...)

          兩種庫對比圖如下:

          ?

          檢索源碼及說明

          ?

          QueryRecords.java

          packagecom.holen.part1;

          ?

          importjava.util.ArrayList;

          importorg.apache.lucene.analysis.standard.StandardAnalyzer;

          importorg.apache.lucene.document.Document;

          importorg.apache.lucene.queryParser.QueryParser;

          importorg.apache.lucene.search.Hits;

          importorg.apache.lucene.search.IndexSearcher;

          importorg.apache.lucene.search.Query;

          importorg.apache.lucene.search.Searcher;

          ?

          /**

          ?* @authorHolenChen

          ?*檢索查詢

          ?*/

          public classQueryRecords{

          ?

          ??? publicQueryRecords(){

          ??? }

          ???

          ??? /**

          ??? ?*檢索查詢,將結果集返回

          ??? ?* @paramsearchkey

          ??? ?* @paramdbpath

          ??? ?* @paramsearchfield

          ??? ?* @return

          ??? ?*/

          ??? publicArrayListqueryRecords(Stringsearchkey,Stringdbpath,Stringsearchfield){

          ?????? ArrayListlist= null;

          ?????? try{

          ?????????? Searchersearcher= newIndexSearcher(dbpath);

          ?????????? Queryquery

          ?????????? ?=QueryParser.parse(searchkey,searchfield,newStandardAnalyzer());

          ?????????? Hitshits=searcher.search(query);

          ?????????? if(hits!= null){

          ????????????? list= newArrayList();

          ????????????? inttemp_hitslength=hits.length();

          ????????????? Documentdoc= null;

          ????????????? for(inti=0;i<temp_hitslength;i++){

          ????????????????? doc=hits.doc(i);

          ????????????????? list.add(doc.get("filename"));

          ????????????? }

          ?????????? }

          ?????? }catch(Exceptionex){

          ?????????? ex.printStackTrace();

          ?????? }

          ?????? returnlist;

          ??? }

          ?

          ??? publicstaticvoidmain(String[]args){

          ?????? QueryRecordstemp= newQueryRecords();??????

          ?????? ArrayListlist= null;

          ?????? list=temp.queryRecords("holen","e:\\lucene\\holendb","content");

          ?????? for(inti=0;i<list.size();i++){

          ?????????? System.out.println((String)list.get(i));

          ?????? }?? ???

          ??? }

          }

          ?

          ?

          說明:該類中Searcher負責查詢,并把查詢結果以Hits對象集方式返回,Hits好比JDBC中的RecordSet,Hits是Document的集合,每個Document相當于一條記錄,Document中包含一個或多個字段,可以通過Document.get(“字段名”)方法得到每個字段的內容。

          ?

          通過這三個類,就完成了一個簡單的基于Lucene的全文檢索應用。

          ?

          4 .總結

          ?

          Lucene十分精練純粹,就一個jar包,引入到你的工程中,調用其接口,就可以為你的應用增添全文檢索功能。

          ?

          通過上一節的初步應用會發現,Lucene使用起來很簡單,與JDBC有些類似,應用時重點掌握好IndexWriter,Document,Field,Searcher等幾個類即可。

          ?

          Lucene的結構很清晰,每個package司職一項,比如org.apache.Lucene.search負責檢索,org.apache.Lucene.index索引,org.apache.Lucene.analysis切詞等,且Lucene的主要動作都采用了抽象類,擴展起來十分方便。

          ?

          相對于一些商業化全文檢索,Lucene的入庫速度更快。因為它的存儲采取分步合并的方法,先建立小索引,待時機成熟才把小索引合并到大索引樹上。因此,我們在操作應用數據時可以同步進行全文檢索庫的操作而不會(或許很少)影響系統的效能。

          ?

          Lucene性能穩定,使用簡單,而且開源免費,有Apache基金在后面做支撐,資金和技術力量都十分雄厚,這兩年也一直是穩步更新,每次新版本的推出,業界均爭相報導。

          ?

          參考資料

          ?

          1.? Introduction to Text Indexing with Apache Jakarta Lucene(Otis Gospodnetic)

          2.? Lucene Introduction in Chinese(車東)

          3.? Lucene Tutorial(Steven J. Owens)

          ?

          作者簡介

          陳光- J2EE項目經理,熟悉EJB、XML,致力于Aapche Jakarta項目的應用與推廣,可通過holen@263.net與作者聯系。

          posted on 2006-04-26 15:42 chenhui 閱讀(103) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          介紹 IOC

          友情鏈接

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 沙坪坝区| 九江县| 儋州市| 盘山县| 景东| 田阳县| 依兰县| 雷州市| 巴林左旗| 诸暨市| 绥宁县| 增城市| 新晃| 当涂县| 凌云县| 蓝山县| 永川市| 晋宁县| 扶绥县| 石狮市| 尚志市| 杂多县| 武胜县| 崇阳县| 兰坪| 马尔康县| 新余市| 南溪县| 七台河市| 罗城| 沂南县| 尼玛县| 奈曼旗| 南漳县| 广西| 遂昌县| 郁南县| 循化| 菏泽市| 威远县| 新竹县|