使用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);;
 
 }
}
}