前言
再写不出那样的程序,看到都会红着脸躲避~~~~~
创建Field时需要指定的选项
我们在创建Field的时候,需要提供三个选项,用以指示Lucene对该FIeld的相关操作,他们分别是:
- org.apache.lucene.document.field.Index——指示该Field能否被索引。有如下值(其中红色部分是常用选项):
- ANALYZED——被索引,并且被分词
- ANALYZED_NO_NORMS——被索引,被分词,但是不记录index-time boost信息,使用本选项后无法对该Field加权
- NOT_ANALYZED——可以被索引,不分词。
- NOT_ANALYZED_NO_NORMS——可以被索引,不分词,不记录boost信息,无法对该Field加权
- NO——不被索引,自然也不分词了。
- org.apache.lucene.document.field.Store——指示该Field的值是否被保存在索引文件中以便能够快速访问。
- YES——将该Field的值存储在索引文件中。我们可以使用document.get("xxx");来快速获取内容。
- NO——不存储。
- org.apache.lucene.document.field.TermVector——指示是否保存该Field的关键字的位置信息。
- WITH_OFFSETS——记录词语(Token)在Field值中的结束位置
- WITH_POSITIONS——记录Token起始位置
- WITH_POSITIONS_OFFSETS——记录Token的起始与结束位置。
- NO——不记录Token的起始与结束位置
public Field(String name, String value, Store store, Index index),这是Field常用的构造方法。我们一般只需要给你Lucene指定Index和Store两个选项,TermVector采用默认即可。
下面的表格描述了Index、Store以及TermVector这三种选项组合时的情形:
Store Index TermVector 适用场景(适合什么样的Field)YES NOT_ANALYZED_NO_NORMAS NO bean的ID,身份证号,电话号码等不可拆分的字段 YES ANALYZED WITH_POSITIONS_OFFSETSbean的title,摘要等 NO ANALYZED WITH_POSITIONS_OFFSETSbean的内容部分(内容较多,不适合放到索引文件的情况) YES NO NO需要被快速访问,但是不能被检索的Field,例如URL,文件大小等
确定被索引Field
这是我们用于测试的业务bean——News:
通 过对类中各个属性的分析可以得出:
UML类图这种高大上的东西我就不贴了,呵~呵
- author——可以被检索,但是不适合被分词,其值最好存储在索引中以便快速访问
- conent——可以被检索、分词,但是由于内容过多而不适合存放在索引文件中
- date——可以被检索,但是不适合被分词,其值最好存储在索引中以便快速访问,另外这个日期是有权的(即优先显示最近发生的新闻,这是个高级话题了)
- id——不可以被检索、分词、但是应当放在索引文件中以便快速访问(比如根据id值去数据库里面找真正的News)
- title——新闻标题,可以被检索、分词,而且适合放在索引文件中以便快速访问
- url——不可以被检索、分词、但是应当放在索引文件中一遍快速访问(从而根据url值直接跳转到相关网页)
为News.java建立索引
//对JavaBean索引
public synchronized void createIndex(List<News> news){
Field idField,titleField,contentField,authorField,dateField;
News n;
Document doc;
for(int i=0;i<news.size();i++){
n=news.get(i);
idField=new Field("id",n.getId(),Store.YES,Index.NOT_ANALYZED);
idField.setBoost(0.0001F);
titleField=new Field("title",n.getTitle(),Store.YES,Index.ANALYZED);
contentField=new Field("content",n.getContent(),Store.NO,Index.ANALYZED);
authorField=new Field("author",n.getAuthor(),Store.YES,Index.NOT_ANALYZED);
dateField=new Field("date",n.getDate().getTime()+"",Store.YES,Index.NO);
doc=new Document();
doc.add(idField);
doc.add(titleField);
doc.add(contentField);
doc.add(authorField);
doc.add(dateField);
System.out.println(n);
try {
indexWriter.addDocument(doc);
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
indexWriter.commit();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
利用Lucene高亮搜索
所谓高亮搜索,就是在搜索结果(Field值)中添加指定的HTML元素。为此需要添加lucene-highlighter-3.5.0.jar。
package org.xiaom.lucene; import java.io.File; public class NewsSearcher { public static final Version CURRENT_VERSION=Version.LUCENE_35; public static final String DEFAULT_HIGHLIGHT_STYLE_pre="<strong>";//高亮的HTML前缀 public static final String DEFAULT_HIGHLIGHT_STYLE_post="</strong>";//后缀 private NewsService newsService; private Analyzer defaultAnalyzer=new StandardAnalyzer(CURRENT_VERSION); private IndexSearcher indexSearcher; private QueryParser queryParser; private Highlighter highlighter; private Formatter formatter; public NewsSearcher(String indexPath,String pre,String post) { IndexReader reader = null; try { reader = IndexReader.open(FSDirectory.open(new File(indexPath))); indexSearcher=new IndexSearcher(reader); queryParser=new QueryParser(CURRENT_VERSION, "title", defaultAnalyzer); } catch (CorruptIndexException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //1,利用高亮前后缀初始化一个SimpleHTMLFormatter formatter=new SimpleHTMLFormatter(pre, post); } public List<News> getNewsByKeyWordsInHightLight(String keyWord){ List<News> news=new ArrayList<News>(); try { Query query=queryParser.parse(keyWord); System.out.println(query); TopDocs topDocs=indexSearcher.search(query, 100); ScoreDoc[] scoreDocs=topDocs.scoreDocs; System.err.println("totalHits:"+topDocs.totalHits); if(topDocs.totalHits>0){ //2,初始化QueryScorer与Fragmenter QueryScorer queryScorer=new QueryScorer(query); Fragmenter fragmenter=new SimpleSpanFragmenter(queryScorer); //3,初始化highlighter并设置fragmenter highlighter=new Highlighter(formatter, queryScorer); highlighter.setTextFragmenter(fragmenter); for(int i=0;i<scoreDocs.length;i++){ Document doc=indexSearcher.doc(scoreDocs[i].doc); News n=newsService.get(doc.get("id")); //4,获取加入高亮前后缀后的文本 n.setTitle(highlighter.getBestFragment(defaultAnalyzer, "title", doc.get("title"))); news.add(n); } } } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InvalidTokenOffsetsException e) { e.printStackTrace(); } return news; } public NewsService getNewsService() { return newsService; } public void setNewsService(NewsService newsService) { this.newsService = newsService; } @Override protected void finalize() throws Throwable { this.indexSearcher.close(); super.finalize(); } }
测试
上面的代码直接复制是跑不起来的,这里是一个我弄好的Eclipse过程,首先将IndexTestCases.java run as junit,再将SearchTestCases.java run as junit。即可看到效果。
上面的工程中还运用了一些QueryParser的语法,其实也挺简单,但是我实在饿了,今儿到此结束吧,挺好_wawaw
注意:
这是我的项目截图,你需要添加这四个包到lib文件夹,编辑类路径,ok,搞定。