posts - 33,  comments - 70,  trackbacks - 0

          轉(zhuǎn):http://www.zdnet.com.cn/developer/webdevelop/story/0,2000081602,39154640,00.htm

          用Lucene來建立一個(gè)索引

          給你的Web網(wǎng)站加上搜索的功能是增強(qiáng)用戶瀏覽體驗(yàn)的最簡(jiǎn)單方式之一,但是在你的應(yīng)用程序里集成一個(gè)搜索引擎并不總是很容易。為了幫助你為自己的Java應(yīng)用程序提供一個(gè)靈活的搜索引擎,我會(huì)講解如何使用Lucene,它是一個(gè)極其靈活的開放源代碼的搜索引擎。

          Lucene會(huì)直接同你的Web應(yīng)用程序集成到一起。它是由Jakarta Apache工作組使用Java編寫成的。你的Java應(yīng)用程序能夠?qū)ucene作為任何搜索功能的核心來使用。Lucene能夠處理任何類型的文本數(shù)據(jù);但是它沒有內(nèi)置對(duì)Word、Excel、PDF和XML的支持。但是還是有一些解決方案能夠讓Lucene支持它們中的每一個(gè)。

          關(guān)于Lucene的重要一點(diǎn)是,它只是一個(gè)搜索引擎,因此沒有內(nèi)置Web圖形用戶界面和Web crawler。要把Lucene集成到你的Web應(yīng)用程序里,你就要編寫一個(gè)顯示查詢表單的servlet或者JSP頁面,還要編寫另一個(gè)列出結(jié)果的頁面。

          用Lucene來建立一個(gè)索引

          你應(yīng)用程序的文本內(nèi)容由Lucene來索引,并被作為一系列索引文件保存在文件系統(tǒng)里。Lucene能夠接受代表單篇內(nèi)容的文檔(Document)對(duì)象,例如一個(gè)Web頁面或者PDF文件。你的應(yīng)用程序就負(fù)責(zé)將其內(nèi)容轉(zhuǎn)變成Lucene能夠理解的文檔對(duì)象。

          每個(gè)文檔都是由有一個(gè)或者多個(gè)的字段(Field)對(duì)象。這些字段包含有一個(gè)名稱和一個(gè)值,非常像散裂圖里的一個(gè)項(xiàng)目(entry)。每個(gè)字段都應(yīng)該對(duì)應(yīng)一段信息,這段信息是同你需要查詢或者顯示的檢索結(jié)果相關(guān)的。例如,標(biāo)題應(yīng)該被用在搜索結(jié)果里,因此它會(huì)被作為一個(gè)字段添加到文檔對(duì)象里。這些字段可以被索引,也可以不被索引,而原始的數(shù)據(jù)也可以選擇保存在索引里。保存在索引里的字段在創(chuàng)建檢索結(jié)果頁面的時(shí)候會(huì)很有用。對(duì)于搜索沒有用處的字段,例如唯一的ID,就不需要被索引,只需要被保存就行了。

          字段也可以是標(biāo)記化了的(tokenized),這就意味著一個(gè)分析程序會(huì)將輸入到字段里的內(nèi)容分解成搜索引擎能夠使用的標(biāo)記。Lucene帶有多個(gè)分析程序,但是我只會(huì)使用最強(qiáng)大的分析程序——StandardAnalyzer類。

          StandardAnalyzer類會(huì)將文本的所有內(nèi)容變成小寫的,并去掉一些常用的停頓詞(stop word)。停頓詞是像“a”、“the”和“in”這樣的詞,它們都是內(nèi)容里非常常見的詞,但是對(duì)搜索卻一點(diǎn)用處都沒有。分析程序也會(huì)分析搜索查詢,這就意味著查詢會(huì)找到匹配的部分。例如,這段文本“The dog is a golden retriever(這條狗是一只金毛獵犬)”,就會(huì)被處理為“dog golden retriever”作為索引。當(dāng)用戶搜索“a Golden Dog”的時(shí)候,分析程序會(huì)處理這個(gè)查詢,并將其轉(zhuǎn)變?yōu)椤癵olden dog”,這就符合我們的內(nèi)容了。

          我們的例子準(zhǔn)備使用數(shù)據(jù)訪問對(duì)象(Data Access Object,DAO)的商務(wù)對(duì)象(business object),前者是Java應(yīng)用程序開發(fā)的一個(gè)常見模式。我要使用的DAO——ProductDAO見Listing A。

          ?1 Listing?A
          ?2 package ?com.greenninja.lucene;?
          ?3 ?
          ?4 importjava.util. * ;?
          ?5 public ? class ?ProductDAO {
          ?6 ?????? private ?Map?map? = ? new ?HashMap();??????
          ?7 ?????? /**
          ?8 ???????*?Initializes?the?map?with?new?Products
          ?9 ???????*
          10 ??????? */

          11 ?????? public ? void ?init()?????? {
          12 ?????????????
          13 ?????????????Product?product1? = ? new ?Product( " 1E344 " , " Blizzard?Convertible " ,
          14 ????????????? " The?Blizzard?is?the?finest?convertible?on?the?market?today,?with?120?horsepower,?6?seats,?and?a?steering?wheel. " ,
          15 ????????????? " The?Blizzard?convertible?model?is?a?revolutionary?vehicle?that?looks?like?a?minivan,?but?has?a?folding?roof?like?a?roadster.?We?took?all?of?the?power?from?our?diesel?engines?and?put?it?into?our?all?new?fuel?cell?power?system. " );
          16 ?????????????map.put(product1.getId(),product1);
          17 ?????????????
          18 ?????????????Product?product2? = ? new ?Product( " R5TS7 " , " Truck?3000 " ,
          19 ????????????? " Our?Truck?3000?model?comes?in?all?shapes?and?sizes,?including?dump?truck,?garbage?truck,?and?pickup?truck.?The?garbage?truck?has?a?full?3?year?warranty. " ,
          20 ????????????? " The?Truck?3000?is?built?on?the?same?base?as?our?bulldozers?and?can?be?outfitted?with?an?optional?hovercraft?attachment?for?all-terrain?travel. " );
          21 ?????????????map.put(product2.getId(),product2);
          22 ?????????????
          23 ?????????????Product?product3? = ? new ?Product( " VC456 " , " i954d-b?Motorcycle " ,
          24 ????????????? " The?motorcycle?comes?with?a?sidecar?on?each?side,?for?additional?stability?and?cornering?ability. " ,
          25 ????????????? " Our?motorcycle?has?the?same?warranty?as?our?other?products?and?is?guaranteed?for?many?miles?of?fun?biking.?Each?motorcycle?is?shipped?with?a?nylon?windbreaker,?goggles,?and?a?helmet?with?a?neat?visor. " );
          26 ?????????????map.put(product3.getId(),product3);
          27 ??????????????????????????
          28 ??????}

          29 ??????
          30 ?????? /**
          31 ???????*?Gets?a?collection?of?all?of?the?products
          32 ???????*
          33 ???????*? @return ?all?of?the?products
          34 ??????? */

          35 ?????? public ?Collection?getAllProducts()
          36 ?????? {
          37 ????????????? return ?map.values();
          38 ??????}

          39 ??????
          40 ?????? /**
          41 ???????*?Gets?a?product,?given?the?unique?id
          42 ???????*
          43 ???????*? @param ?id?the?unique?id
          44 ???????*? @return ?the?Product?object,?or?null?if?the?id?wasn't?found
          45 ??????? */

          46 ?????? public ?Product?getProduct(String?id)
          47 ?????? {
          48 ????????????? if ?(map.containsKey(id))
          49 ????????????? {
          50 ???????????????????? return ?(Product)?map.get(id);
          51 ?????????????}

          52 ?????????????
          53 ????????????? // the?product?id?wasn't?found
          54 ????????????? return ? null ;
          55 ??????}
          ?
          56 ?
          57 }

          58 ?
          59

          為了讓這個(gè)演示程序簡(jiǎn)單,我不準(zhǔn)備使用數(shù)據(jù)庫,DAO也只會(huì)包含產(chǎn)品(Product)對(duì)象的一個(gè)集合。在本例里,我會(huì)采用Listing B

          Listing?B?

          package ?com.greenninja.lucene;?
          ?
          public ? class ?Product
          {
          ??????
          private ?String?name;
          ??????
          private ?String?shortDescription;
          ??????
          private ?String?longDescription;??????
          ??????
          private ?String?id;?
          ??????
          ??????
          /**
          ???????*?Constructor?to?create?a?new?product
          ???????
          */

          ??????
          public ?Product(String?i,?String?n,?String?sd,?String?ld)
          ??????
          {
          ?????????????
          this .id? = ?i;
          ?????????????
          this .name = ?n;
          ?????????????
          this .shortDescription? = ?sd;
          ?????????????
          this .longDescription? = ?ld;
          ??????}

          ?
          ?
          ??????setter
          / getter??
          }

          ?


          里的產(chǎn)品對(duì)象,并將它們轉(zhuǎn)變成為用于索引的文檔。

          索引符(Indexer)類在Listing C

          Listing?C??
          ?
          package ?com.greenninja.lucene;?
          ?
          import ?java.io.IOException;
          import ?java.util.Collection;
          import ?java.util.Iterator;?
          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;?
          ?
          ?
          public ? class ?Indexer
          {?
          ??????
          protected ?IndexWriter?writer? = ? null ;
          ??????
          ??????
          protected ?Analyzer?analyzer? = ? new ?StandardAnalyzer();
          ??????
          ??????
          public ? void ?init(String?indexPath)? throws ?IOException
          ??????
          {
          ????????????
          ?????????????
          // create?a?new?index?every?time?this?is?run
          ?????????????writer? = ? new ?IndexWriter(indexPath,?analyzer,? true );
          ??????}

          ??????
          ??????
          public ? void ?buildIndex()? throws ?IOException
          ??????
          {
          ?????????????
          // get?the?products?from?the?DAO
          ?????????????ProductDAO?dao? = ? new ?ProductDAO();
          ?????????????dao.init();
          ?????????????Collection?products?
          = ?dao.getAllProducts();
          ?????????????
          ?????????????Iterator?iter?
          = ?products.iterator();
          ?????????????
          ?????????????
          while ?(iter.hasNext())
          ?????????????
          {
          ????????????????????Product?product?
          = ?(Product)?iter.next();
          ????????????????????
          ????????????????????
          // ?convert?the?product?to?a?document.
          ????????????????????Document?doc? = ? new ?Document();
          ????????????????????
          ????????????????????
          // ?create?an?unindexed,?untokenized,?stored?field?for?the?product?id
          ????????????????????doc.add(Field.UnIndexed( " productId " ,product.getId()));
          ????????????????????
          ????????????????????
          // ?create?an?indexed,?untokenized,?stored?field?for?the?name
          ????????????????????doc.add(Field.Keyword( " name " ,product.getName()));
          ????????????????????
          ????????????????????
          // ?create?an?indexed,?untokenized,?stored?field?for?the?short?description
          ???????????????????doc.add(Field.Keyword( " short " ,product.getShortDescription()));
          ????????????????????
          ????????????????????
          // ?create?an?indexed,?tokenized,?unstored?field?for?all?of?the?content
          ???????????????????String?content? = ?product.getName()? + ? " ? " ? + ?product.getShortDescription()? +
          ??????????????????????????
          " ? " ? + ?product.getLongDescription();
          ????????????????????doc.add(Field.Text(
          " content " ,content));
          ????????????????????
          ????????????????????
          // ?add?the?document?to?the?index
          ???????????????????? try
          ????????????????????
          {
          ??????????????????????????writer.addDocument(doc);
          ??????????????????????????System.out.println(
          " Document? " ? + ?product.getName()? + ? " ?added?to?index. " );
          ????????????????????}

          ????????????????????
          catch ?(IOException?e)
          ????????????????????
          {
          ??????????????????????????System.out.println(
          " Error?adding?document:? " ? + ?e.getMessage());
          ????????????????????}
          ????????????????????
          ?????????????}
          ?????????????
          ?????????????
          // optimize?the?index
          ?????????????writer.optimize();
          ?????????????
          ?????????????
          // close?the?index
          ?????????????writer.close();?????????????
          ??????}
          ????????????
          }

          ?


          里,它將負(fù)責(zé)把Product轉(zhuǎn)換成為Lucene文檔,還負(fù)責(zé)創(chuàng)建Lucene索引。

          產(chǎn)品類里的字段是ID名、簡(jiǎn)短描述和詳細(xì)描述。通過使用字段(Field)類的UnIndexed方法,ID會(huì)被作為一個(gè)非索引的非標(biāo)記字段被保存。通過使用字段類的Keyword方法,名稱和簡(jiǎn)短描述會(huì)被作為索引的非標(biāo)記字段被保存。搜索引擎會(huì)對(duì)內(nèi)容字段進(jìn)行查詢,而內(nèi)容字段里會(huì)包含有產(chǎn)品的名稱、簡(jiǎn)短描述和詳細(xì)描述字段。

          在所有的文檔都添加完之后,就要優(yōu)化索引并關(guān)閉索引編寫器,這樣你才能夠使用索引。Lucene的大多數(shù)實(shí)現(xiàn)都要使用增量索引(incremental indexing),在增量索引里,已經(jīng)在索引里的文檔都是獨(dú)立更新的,而不是每次先刪除索引再創(chuàng)建一個(gè)新的。

          運(yùn)行查詢

          運(yùn)行查詢

          創(chuàng)建一個(gè)查詢并在索引里搜索結(jié)果要比創(chuàng)建一個(gè)索引簡(jiǎn)單。你的應(yīng)用程序會(huì)要求使用者提供一個(gè)搜索查詢,這個(gè)查詢可以是一個(gè)簡(jiǎn)單的詞語。Lucene擁有一些更加高級(jí)的查詢(Query)類,用于布爾搜索或者整句搜索。

          高級(jí)查詢的一個(gè)例子是”Mutual Fund”(互惠基金)AND stock*(股票),它會(huì)搜索包含有短語Mutual Fund和以stock開頭的詞(例如stocks、stock或者甚至是stockings)的文檔。


          獲取更多關(guān)于Lucene里查詢的信息
          Lucene Web網(wǎng)站里的句法頁面會(huì)提供更加詳細(xì)的信息。



          搜索符(Searcher)類放在Listing D

          Listing?D?
          ?
          package ?com.greenninja.lucene;?
          ?
          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 ?Searcher
          {
          ??????
          protected ?Analyzer?analyzer? = ? new ?StandardAnalyzer();
          ?
          ?
          ??????
          public ?Hits?search(String?indexPath,?String?queryString)? throws ?IOException,?ParseException
          ??????
          {
          ?????????????
          // the?Lucene?index?Searcher?class,?which?uses?the?query?on?the?index
          ?????????????IndexSearcher?indexSearcher? = ? new ?IndexSearcher(indexPath);
          ?????????????
          ?????????????
          // ?make?the?query?with?our?content?field,?the?query?string,?and?the?analyzer
          ?????????????Query?query? = ?QueryParser.parse(queryString, " content " ,analyzer);
          ?????????????
          ?????????????Hits?hits?
          = ?indexSearcher.search(query);
          ?????????????
          ?????????????
          return ?hits;?
          ??????}
          ??????
          }

          ?


          里,它負(fù)責(zé)在Lucene索引里查找你所使用的詞語。對(duì)于本篇演示程序而言,我使用了一個(gè)簡(jiǎn)單的查詢,它只是一個(gè)字符串,而沒有使用任何高級(jí)查詢功能。我用QueryParser類從查詢字符串里創(chuàng)建了一個(gè)查詢(Query)對(duì)象,QueryParser這個(gè)類會(huì)使用StandardAnalyzer類將查詢字符串分解成標(biāo)記,再去掉停頓詞,然后將這個(gè)字符串轉(zhuǎn)換成小寫的。

          這個(gè)查詢被傳遞給一個(gè)IndexSearcher對(duì)象。IndexSearcher會(huì)在索引的文件系統(tǒng)里被初始化。IndexSearcher的搜索方法將接受這個(gè)查詢并返回一個(gè)命中(Hits)對(duì)象。這個(gè)命中對(duì)象包含有作為Lucene文檔對(duì)象的檢索結(jié)果,以及結(jié)果的長度。使用命中對(duì)象的Doc方法將取回命中對(duì)象里的每個(gè)文檔。

          文檔對(duì)象包含有我添加到索引符文檔里的字段。這些字段中的一些被保存了,但是沒有被標(biāo)記化,你可以將它們從文檔里提取出來。示例應(yīng)用程序會(huì)用搜索引擎運(yùn)行一個(gè)查詢,然后顯示它所找到的產(chǎn)品名稱。


          運(yùn)行演示程序

          要運(yùn)行本文里的示例程序,你需要從Lucene的Web網(wǎng)站下載最新版本的Lucene二進(jìn)制發(fā)布版本(binary distribution)。Lucene發(fā)行版的lucene-1.3-rc1.jar文件需要被添加到你Java類的路徑下才能夠運(yùn)行這個(gè)演示程序。演示程序會(huì)在運(yùn)行com.greenninja.lucene.Demo類的目錄下創(chuàng)建一個(gè)叫做index的索引目錄。你還需要安裝好JDK。一行典型的命令是:java -cp c:\java\lucene-1.3-rc1\lucene-1.3-rc1.jar;. com.greenninja.lucene.Demo(見A)。本例所使用的示例數(shù)據(jù)包含在ProductDAO類里。這個(gè)查詢是演示(Demo)類的一部分。



          圖A

          ?

          ??命令行示例


          參考資料
          · 下載本文相關(guān)代碼
          ·javaworld.com:javaworld.com
          ·Matrix-Java開發(fā)者社區(qū):http://www.matrix.org.cn/
          ·Lucene 搜索引擎庫:
          http://jakarta.apache.org/lucene/docs/index.html
          ·MAOS 開源項(xiàng)目:
          http://sourceforge.net/projects/maos/

          posted on 2006-05-15 22:54 地獄男爵(hellboys) 閱讀(1066) 評(píng)論(0)  編輯  收藏 所屬分類: 編程語言(c/c++ java python sql ......)
          <2006年5月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          隨筆分類

          隨筆檔案

          文章檔案

          相冊(cè)

          連接

          最新隨筆

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 丹东市| 化德县| 美姑县| 昌平区| 深圳市| 游戏| 赤壁市| 龙川县| 凤庆县| 河西区| 巴里| 宜丰县| 新建县| 岳西县| 宁海县| 德化县| 遵化市| 黔东| 凯里市| 松溪县| 西乌珠穆沁旗| 海南省| 柘荣县| 紫阳县| 桑植县| 兰西县| 曲松县| 南丰县| 噶尔县| 文水县| 渝中区| 洪洞县| 崇左市| 石泉县| 延寿县| 东台市| 湘阴县| 井研县| 罗平县| 宣汉县| 民县|