您的位置:首页 > 其它

Lucene实战(二)索引业务bean以及搜索结果高亮

2014-05-05 17:42 435 查看
前言

再写不出那样的程序,看到都会红着脸躲避~~~~~

创建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这三种选项组合时的情形:

StoreIndex
TermVector
适用场景(适合什么样的Field)
YESNOT_ANALYZED_NO_NORMASNObean的ID,***号,电话号码等不可拆分的字段
YESANALYZED
WITH_POSITIONS_OFFSETS
bean的title,摘要等
NOANALYZED
WITH_POSITIONS_OFFSETS
bean的内容部分(内容较多,不适合放到索引文件的情况)
YESNO
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,搞定。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐