scud(飞云小侠) http://www.jscud.com 转载请注明来源/作者
关键字:lucene,html parser,全文检索,IndexReader,Document,Field,IndexWriter,Term,HTMLPAGE
无论是建立索引还是分析内容,都是为了用户的搜索服务.
在Lucene中,如果需要使用搜索,需要使用Searcher类,这是一个抽象类,它有2个子类:IndexSearcher和MultiSearcher.
IndexSearcher是对一个索引进行搜索,如果你需要对多个索引进行搜索,可以使用MultiSearcher.下面的内容只介绍了IndexSearcher.
搜索涉及到几个问题:分页,组合条件,根据条件过滤,排序等等.
分页:分页在记录列表的地方都会遇到,这里不在赘述,我也实现过一个保存分页结果和显示结果的类,用于自己的实际工作,下面也会用到保存分页结果的类,代码如下:
package com.jscud.support;
/**
* 分页显示用的参数.
*
* @author scud(飞云小侠) http://www.jscud.com
*
*/
public class DivPageInfo
{
//开始记录数
private int recStart;
//结束记录数
private int recEnd;
//总页数
private int pageCount;
//当前页
private int page;
//记录总数
private int recCount;
//每页记录数
private int perPageRows;
public int getNicePageCount()
{
return getNicePageNum(pageCount);
}
//get,set等,不在列出
//......
/**
* 得到友好的页数数字,页数为0时,返回1.
*
* @return 得到友好的页数
*/
public static int getNicePageNum(int nPage)
{
if (nPage == 0)
{
return 1;
}
else
{
return nPage;
}
}
}
显示分页结果的类就需要大家根据自己使用的框架来具体实现了.我使用的是WebWork.
组合条件:在Lucene中,搜索的条件可以组合的很复杂,相关的类有BooleanQuery, FilteredQuery, MultiTermQuery, PhrasePrefixQuery, PhraseQuery, PrefixQuery, RangeQuery, SpanQuery, TermQuery 等等,从而可以组合出很复杂的条件用于查询.
另外QueryParser可以根据用户输入的字符串和设定的解析器和字段设置等,可以自动产生新的组合条件用于查询,例如用户输入"john AND black",QueryParser可以自己分析出用户是需要查询字段中同时包含"john"和"black"的结果.
过滤条件:有时候根据具体的用户需求,有些记录对于一些用户是不可见的,此时就要使用过滤器来防止不合法的用户看到不应该看到的记录.过滤器同时也可以根据一些具体的条件来过滤掉一些用户不想看到的记录.如果需要实现自己的filter,只要参考QueryFilter,DateFilter实现Filter即可.
排序:有时候,可能需要根据某个字段进行排序,例如按照时间排序.当然更多的时候是按照搜索结果的符合度进行排序,lucene默认的排序就是按照符合度来进行排序的.
进行搜索的代码如下,根据自己的需要进行代码的修改:
/**
* 进行搜索.
*
* 参数依次为:搜索内容(支持lucene语法),当前页,每页记录数,分页信息对象
*
*/
public static List search(String searchText, int page, int perpage, final DivPageInfo pageinfo)
{
List docs = new ArrayList();
if(!LuceneSearch.indexExist(indexDir)) { return docs; } Searcher searcher = null;
try
{
StandardAnalyzer analyzer = new StandardAnalyzer();
//处理检索条件
Query titleQuery = QueryParser.parse(searchText, "title", analyzer);
Query contextQuery = QueryParser.parse(searchText, "content", analyzer);
Query otherQuery = QueryParser.parse(searchText, "other", analyzer);
BooleanQuery query = new BooleanQuery();
query.add(titleQuery, false, false);
query.add(contextQuery, false, false);
query.add(otherQuery, false, false);
//分页检索
searcher = new IndexSearcher(indexDir);
Hits hits = searcher.search(query);
DivPageInfo.divPage(hits.length(), perpage, page, pageinfo);
//取出当前页的记录
for (int i = pageinfo.getRecStart(); i <= pageinfo.getRecEnd(); i++)
{
docs.add(LuceneDocument.getDocument(hits.doc(i - 1)));
}
}
catch (IOException e)
{
LogMan.error("Error occur When Search Lucene", e);
}
catch (ParseException e)
{
LogMan.error("Error occur When Search Lucene", e);
}
finally
{
try
{
if (null != searcher)
{
searcher.close();
}
}
catch (IOException e)
{
LogMan.warn("Close searcher Error");
}
}
return docs;
}
代码中出现了一个新的类Hits,Hits是lucene的搜索结果集,是lazy load的结果集,只有你真正访问它,它才去装载真正的数据.
代码中还出现了一个LuceneDocument,这是为了在页面中显示而写的一个辅助类,因为lucene的Document是final的,无法进行扩展,而要显示时间字段必须要调用DateField中的函数,这样在页面中显示就不太直观了,所以写了这个辅助类,代码如下:
package com.jscud.www.support.search;
import java.sql.Timestamp;
import java.util.Date;
import org.apache.lucene.document.DateField;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
/**
* 对Lucene的Document的封装,用于显示目的.
*
* @author scud(飞云小侠) http://www.jscud.com
*
*/
public class LuceneDocument
{
private Document doc;
public LuceneDocument(Document doc)
{
this.doc = doc;
}
public static LuceneDocument getDocument(Document doc)
{
return new LuceneDocument(doc);
}
public String getValue(String name)
{
return doc.get(name);
}
public Field getField(String name)
{
return doc.getField(name);
}
public Timestamp getDateTime(String name)
{
String value = doc.get(name);
return new Timestamp( DateField.stringToTime(value));
}
public Date getDate(String name)
{
String value = doc.get(name);
return DateField.stringToDate(value);
}
}
使用WebWork对结果集进行了显示,代码如下:
<ww:iterator value="docs">
<tr >
<td>
<a href="<jscud:contextpath /><ww:property value="getValue('visiturl')" />" target="_blank" >
<ww:property value="getValue('title')" escape="true" />
</a> (<jscud:datetime value="getDateTime('addtime')" />)
</td>
</tr>
</ww:iterator>
然后调用分页信息显示tag即可.
通过以上的应用,可以看到,其实使用lucene很简单,以前总觉得很神秘,所以一直没有使用过,用过之后才觉得如此简单.
当然,对于大容量数据下,群集情况下,在网上都有很多解决方案,在此不一一提出,感兴趣的读者可以自己去搜索. :)