華山論劍

          一心一意做技術!

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            31 Posts :: 0 Stories :: 447 Comments :: 0 Trackbacks
            Lucene是apache軟件基金會 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包及架構,提供了完整的查詢引擎和索引引擎,實現了一些通用的分詞算法,預留很多詞法分析器接口。本文以myrss.easyjf.com網站系統中使用Lucene實現全文檢索的代碼為例,簡單演示Lucene在實際項目中的應用。
            使用Lucene實現全文檢索,主要有下面三個步驟:
            1、建立索引庫:根據網站新聞信息庫中的已有的數據資料建立Lucene索引文件。
            2、通過索引庫搜索:有了索引后,即可使用標準的詞法分析器或直接的詞法分析器實現進行全文檢索。
            3、維護索引庫:網站新聞信息庫中的信息會不斷的變動,包括新增、修改及刪除等,這些信息的變動都需要進一步反映到Lucene索引文件中。
          ??? 下面是myrss.easyjf.com相關代碼!
          ?
          ?一、索引管理(建立及維護)
            索引管理類MyRssIndexManage主要實現根據網站信息庫中的數據建立索引,維護索引等。由于索引的過程需要消耗一定的時間,因此,索引管理類實現Runnable接口,使得我們可以在程序中開新線程來運行。
          package com.easyjf.lucene;
          import java.util.Date;
          import java.util.List;
          import org.apache.lucene.analysis.standard.StandardAnalyzer;
          import org.apache.lucene.document.Document;
          import org.apache.lucene.document.Field;
          import org.apache.lucene.index.IndexReader;
          import org.apache.lucene.index.IndexWriter;
          import org.apache.lucene.queryParser.MultiFieldQueryParser;
          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.search.Searcher;
          import com.easyjf.dbo.EasyJDB;
          import com.easyjf.news.business.NewsDir;
          import com.easyjf.news.business.NewsDoc;
          import com.easyjf.news.business.NewsUtil;
          import com.easyjf.web.tools.IPageList;
          public class MyRssIndexManage implements Runnable {
          ?private String indexDir;
          ?private String indexType="add";?
          ?public void run() {
          ??// TODO Auto-generated method stub
          ??if("add".equals(indexType))
          ???normalIndex();
          ??else if ("init".equals(indexType)) reIndexAll();
          ?}
          ?public void normalIndex()
          ?{
          ??try{
          ???Date start = new Date();
          ???int num=0;
          ???IndexWriter writer=new IndexWriter(indexDir,new StandardAnalyzer(),false);???
          ???//NewsDir dir=NewsDir.readBySn();???
          ???String scope="(needIndex<2) or(needIndex is null)";
          ???IPageList pList=NewsUtil.pageList(scope,1,50);
          ???for(int p=0;p<pList.getPages();p++)
          ???{
          ???pList=NewsUtil.pageList(scope,p,100);
          ???List list=pList.getResult();
          ???for(int i=0;i<list.size();i++)
          ???{
          ????NewsDoc doc=(NewsDoc)list.get(i);?????
          ????writer.addDocument(newsdoc2lucenedoc(doc));???
          ????num++;
          ???}
          ???}
          ???writer.optimize();
          ???writer.close();
          ???EasyJDB.getInstance().execute("update NewsDoc set needIndex=2 where "+scope);
          ???Date end = new Date();
          ???System.out.print("新增索引"+num+"條信息,一共花:"+(end.getTime() - start.getTime())/60000+"分鐘!");??
          ???}
          ???catch(Exception e)
          ???{
          ????e.printStackTrace();
          ???}
          ?}
          ?public void reIndexAll()
          ?{
          ??try{
          ???Date start = new Date();
          ???int num=0;
          ???IndexWriter writer=new IndexWriter(indexDir,new StandardAnalyzer(),true);???
          ???NewsDir dir=NewsDir.readBySn("easyjf");???
          ???IPageList pList=NewsUtil.pageList(dir,1,50);
          ???for(int p=0;p<pList.getPages();p++)
          ???{
          ???pList=NewsUtil.pageList(dir,p,100);
          ???List list=pList.getResult();
          ???for(int i=0;i<list.size();i++)
          ???{????
          ????NewsDoc doc=(NewsDoc)list.get(i);?????
          ????writer.addDocument(newsdoc2lucenedoc(doc));
          ????num++;
          ???}
          ???}
          ???writer.optimize();
          ???writer.close();
          ???EasyJDB.getInstance().execute("update NewsDoc set needIndex=2 where dirPath like 'easyjf%'");
          ???Date end = new Date();
          ???System.out.print("全部重新做了一次索引,一共處理了"+num+"條信息,花:"+(end.getTime() - start.getTime())/60000+"分鐘!");??
          ???}
          ???catch(Exception e)
          ???{
          ????e.printStackTrace();
          ???}
          ?}
          ?private Document newsdoc2lucenedoc(NewsDoc doc)
          ?{
          ??Document lDoc=new Document();?????
          ??lDoc.add(new Field("title",doc.getTitle(),Field.Store.YES,Field.Index.TOKENIZED));
          ??lDoc.add(new Field("content",doc.getContent(),Field.Store.YES,Field.Index.TOKENIZED));
          ??lDoc.add(new Field("url",doc.getRemark(),Field.Store.YES,Field.Index.NO));????
          ??lDoc.add(new Field("cid",doc.getCid(),Field.Store.YES,Field.Index.NO));
          ??lDoc.add(new Field("source",doc.getSource(),Field.Store.YES,Field.Index.NO));
          ??lDoc.add(new Field("inputTime",doc.getInputTime().toString(),Field.Store.YES,Field.Index.NO));
          ??return lDoc;
          ?}
          ?public String getIndexDir() {
          ??return indexDir;
          ?}
          ?public void setIndexDir(String indexDir) {
          ??this.indexDir = indexDir;
          ?}
          ?
          ?public String getIndexType() {
          ??return indexType;
          ?}
          ?public void setIndexType(String indexType) {
          ??this.indexType = indexType;
          ?}
          }
          ?
          二、使用Lucene實現全文搜索
          ?? 下面是MyRssSearch類的源碼,該類主要實現使用Lucene中Searcher及QueryParser實現從索引庫中搜索關鍵詞。
          package com.easyjf.lucene;
          import java.util.List;
          import org.apache.lucene.analysis.standard.StandardAnalyzer;
          import org.apache.lucene.document.Document;
          import org.apache.lucene.index.IndexReader;
          import org.apache.lucene.queryParser.MultiFieldQueryParser;
          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.search.Searcher;
          import com.easyjf.search.MyRssUtil;
          import com.easyjf.search.SearchContent;
          import com.easyjf.web.tools.IPageList;
          import com.easyjf.web.tools.PageList;
          public class MyRssSearch {
          ?private String indexDir;?
          ?IndexReader ir;
          ?Searcher search;
          ?public IPageList search(String key,int pageSize,int currentPage)
          ?{
          ??IPageList pList=new PageList(new HitsQuery(doSearch(key)));
          ??pList.doList(pageSize,currentPage,"","",null);
          ??if(pList!=null)
          ??{??
          ???List list=pList.getResult();?
          ???if(list!=null){
          ???for(int i=0;i<list.size();i++)
          ???{
          ????list.set(i,lucene2searchObj((Document)list.get(i),key));
          ???}
          ???}
          ??}
          ??try{
          ??if(search!=null)search.close();
          ??if(ir!=null)ir.close();
          ??}
          ??catch(Exception e)
          ??{
          ???e.printStackTrace();
          ??}
          ??return pList;
          ?}
          ?private SearchContent lucene2searchObj(Document doc,String key)
          ?{
          ??SearchContent searchObj=new SearchContent();
          ??String title=doc.getField("title").stringValue();
          ??searchObj.setTitle(title.replaceAll(key,"<font color=red>"+key+"</font>"));??
          ??searchObj.setTvalue(doc.getField("cid").stringValue());??
          ??searchObj.setUrl(doc.getField("url").stringValue());
          ??searchObj.setSource(doc.getField("source").stringValue());??
          ??searchObj.setLastUpdated(doc.getField("inputTime").stringValue());?
          ??searchObj.setIntro(MyRssUtil.content2intro(doc.getField("content").stringValue(),key));??
          ??return searchObj;
          ?}
          ?public Hits doSearch(String key)
          ?{
          ??Hits hits=null;
          ??try{
          ??ir=IndexReader.open(indexDir);
          ??search=new IndexSearcher(ir);??
          ??String fields[]={"title","content"};
          ??QueryParser parser=new MultiFieldQueryParser(fields,new StandardAnalyzer());
          ??Query query=parser.parse(key);
          ??hits=search.search(query);???
          ??}
          ??catch(Exception e)
          ??{
          ???e.printStackTrace();
          ??}
          ??//System.out.println("搜索結果:"+hits.length());
          ??return hits;
          ?}
          ?
          ?public String getIndexDir() {
          ??return indexDir;
          ?}
          ?public void setIndexDir(String indexDir) {
          ??this.indexDir = indexDir;
          ?}
          }
            在上面的代碼中,search方法返回一個封裝了分頁查詢結果的IPageList,IPageList是EasyJWeb Tools業務引擎中的分頁引擎,對于IPageList的使用,請看本人寫的這篇文章《EasyJWeb Tools中業務引擎分頁的設計實現》:

            我們針對Lucene的的查詢結果Hits結構,寫了一個查詢器HitsQuery。代碼如下所示:
          package com.easyjf.lucene;
          import java.util.ArrayList;
          import java.util.Collection;
          import java.util.List;
          import org.apache.lucene.search.Hits;
          import com.easyjf.web.tools.IQuery;
          public class HitsQuery implements IQuery {
          ?private int begin=0;
          ?private int max=0;
          ?private Hits hits;
          ?public HitsQuery()
          ?{
          ??
          ?}
          ?public HitsQuery(Hits hits)
          ?{
          ??if(hits!=null)
          ??{
          ???this.hits=hits;???
          ???this.max=hits.length();
          ??}
          ?}
          ?public int getRows(String arg0) {
          ??// TODO Auto-generated method stub
          ??return (hits==null?0:hits.length());
          ?}
          ?public List getResult(String arg0) {
          ??// TODO Auto-generated method stub
          ??List list=new ArrayList();??
          ??for(int i=begin;i<(begin+max)&&(i<hits.length());i++)
          ??{
          ???try{
          ???list.add(hits.doc(i));
          ???}
          ???catch(Exception e)
          ???{
          ????e.printStackTrace();
          ???}
          ??}
          ??return list;
          ?}
          ?public void setFirstResult(int begin) {
          ??// TODO Auto-generated method stub
          ??this.begin=begin;
          ?}
          ?public void setMaxResults(int max) {
          ??// TODO Auto-generated method stub
          ??this.max=max;
          ?}
          ?public void setParaValues(Collection arg0) {
          ??// TODO Auto-generated method stub
          ??
          ?}
          ?public List getResult(String condition, int begin, int max) {
          ??// TODO Auto-generated method stub
          ??if((begin>=0)&&(begin<max))this.begin=begin;
          ??if(!(max>hits.length()))this.max=max;
          ??return getResult(condition);
          ?}
          }
          ?
          三、Web調用
            下面我們來看看在Web中如果調用商業邏輯層的全文檢索功能。下面是處理用戶請請的Action中關于搜索部分的源碼:
          package com.easyjf.news.action;
          public class SearchAction implements IWebAction {?
          public Page doSearch(WebForm form,Module module)throws Exception
          {
          ?String key=CommUtil.null2String(form.get("v"));?
          ?key=URLDecoder.decode(URLEncoder.encode(key,"ISO8859_1"),"utf-8");
          ?form.set("v",key);
          ?form.addResult("v2",URLEncoder.encode(key,"utf-8"));
          ?if(key.getBytes().length>2){
          ?String orderBy=CommUtil.null2String(form.get("order"));?
          ?int currentPage=CommUtil.null2Int(form.get("page"));
          ?int pageSize=CommUtil.null2Int(form.get("pageSize"));??
          ?if(currentPage<1)currentPage=1;
          ?if(pageSize<1)pageSize=15;
          ?SearchEngine search=new SearchEngine(key,orderBy,pageSize,currentPage);
          ?search.getLuceneSearch().setIndexDir(Globals.APP_BASE_DIR+"/WEB-INF/index");
          ?search.doSearchByLucene();
          ?IPageList pList=search.getResult();
          ?if(pList!=null && pList.getRowCount()>0){
          ??form.addResult("list",pList.getResult());
          ??form.addResult("pages",new Integer(pList.getPages()));
          ??form.addResult("rows",new Integer(pList.getRowCount()));
          ??form.addResult("page",new Integer(pList.getCurrentPage()));
          ??form.addResult("gotoPageHTML",CommUtil.showPageHtml(pList.getCurrentPage(),pList.getPages()));
          ??}
          ?else
          ?{
          ??form.addResult("notFound","true");//找不到數據
          ?}?
          ?}
          ?else
          ??form.addResult("errMsg","您輸入的關鍵字太短!");
          ?form.addResult("hotSearch",SearchEngine.getHotSearch(20));
          ?return null;
          }
          }
          其中調用的SearchEngine類中有關Lucene部分的源碼:
          public class SearchEngine {
          private MyRssSearch luceneSearch=new MyRssSearch();
          public void doSearchByLucene()
          {?
          ?SearchKey keyObj=readCache();?
          ?if(keyObj!=null){
          ??result=luceneSearch.search(key,pageSize,currentPage);??
          ??if(updateStatus){
          ??keyObj.setReadTimes(new Integer(keyObj.getReadTimes().intValue()+1));
          ??keyObj.update();
          ??}??
          ?}
          ?else//緩存中沒有該關鍵字信息,生成關鍵字搜索結果
          ?{?
          ??keyObj=new SearchKey();
          ??keyObj.setTitle(key);
          ??keyObj.setLastUpdated(new Date());
          ??keyObj.setReadTimes(new Integer(1));
          ??keyObj.setStatus(new Integer(0));
          ??keyObj.setSequence(new Integer(1));
          ??keyObj.setVdate(new Date());
          ??keyObj.save();?
          ??result=luceneSearch.search(key,pageSize,currentPage);;?
          ??
          ?}?
          }
          }
          ?
          四、程序演示效果
            這是EasyJF團隊官方網站上提供java信息搜索的myrss.easyjf.com的運行效果。
          ????

          ?
          posted on 2006-07-03 11:51 大峽 閱讀(3850) 評論(4)  編輯  收藏

          Feedback

          # re: lucene全文檢索應用示例及代碼簡析 2006-07-05 22:31 獵手
          呵呵,非常好啊,
          請問當添加,修改,刪除數據庫中的數據后,大俠是如何及時的處理索引,能不能說說你的實現思路,謝謝。
          很想看看你的代碼,如果可以的話,能給我發一份么?我的郵箱是jsrmade@126.com  回復  更多評論
            

          # re: lucene全文檢索應用示例及代碼簡析 2008-04-23 09:49 卡片
          給我發一份吧,郵箱是liangyao_86@163.com  回復  更多評論
            

          # re: lucene全文檢索應用示例及代碼簡析 2008-04-25 01:05 啊新
          為什么要對Hits 封裝成HitsQuery,看上去像是給查詢封裝,但我覺得應該是給查詢到的結果封裝。不太明白,請解釋一下,謝謝!  回復  更多評論
            

          # re: lucene全文檢索應用示例及代碼簡析 2010-09-26 09:13 maxin
          挺好的,佩服!  回復  更多評論
            


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


          網站導航:
           
          主站蜘蛛池模板: 黄冈市| 夏津县| 孙吴县| 鸡东县| 墨脱县| 岳普湖县| 厦门市| 郎溪县| 德清县| 桂东县| 双峰县| 鄱阳县| 棋牌| 清远市| 万载县| 木里| 福安市| 沧州市| 临洮县| 武义县| 岑巩县| 新源县| 剑阁县| 巴马| 阿拉善盟| 崇州市| 永靖县| 新绛县| 和林格尔县| 石楼县| 梨树县| 青州市| 远安县| 水城县| 宁明县| 平果县| 奉化市| 建宁县| 新巴尔虎右旗| 定陶县| 通化市|