在进行检索时展示时,不能避免的问题就是高亮,下面提供了普通高亮方法和使用fast进行高亮的方法,可以方便大家的选择。
注意:在使用fast高亮方法查询显示效率会比上一种高,但是会使用相对较大的存储空间,这是一种以空间换时间的方法。如果使用,需要将内容存储到索引中,并且使用如下的方法存储Field到document中才能是查询结果生效。
FieldType type=new FieldType(TextField.TYPE_STORED);
type.setStoreTermVectorOffsets(true);//记录相对增量
type.setStoreTermVectorPositions(true);//记录位置信息
type.setStoreTermVectors(true);//存储向量信息
type.freeze();//阻止改动信息
Field content = new Field("content", handler.toString(), type);
doc.add(content);
package com.johnny.lucene05.lucene_plugin.highlighter;
import java.io.IOException;
import java.io.StringReader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.queries.mlt.MoreLikeThis;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.search.highlight.TokenSources;
import org.apache.lucene.search.vectorhighlight.BaseFragmentsBuilder;
import org.apache.lucene.search.vectorhighlight.FastVectorHighlighter;
import org.apache.lucene.search.vectorhighlight.FieldQuery;
import org.apache.lucene.search.vectorhighlight.FragListBuilder;
import org.apache.lucene.search.vectorhighlight.FragmentsBuilder;
import org.apache.lucene.search.vectorhighlight.ScoreOrderFragmentsBuilder;
import org.apache.lucene.search.vectorhighlight.SimpleFragListBuilder;
import org.junit.Test;
import com.chenlb.mmseg4j.analysis.MMSegAnalyzer;
import com.johnny.lucene05.lucene_plugin.tika.FileIndexUtils;
/**
* 文件高亮
* 推荐使用快速高亮以获取更高性能,
* 高亮还可以在前台借助js直接高亮,可以参见:http://qindongliang.iteye.com/blog/1953409
* @author Johnny
*
*/
public class HighLighterTest {
/**
* 类 释义
SimpleHTMLFormatter 常用的格式化Html标签器,提供一个构造函数传入高亮颜色标签,默认使用黑色
TokenSources 提供静态方法,支持从数据源中获取TokenStream,进行token处理
Highlighter 负责获取匹配上的高亮片段
QueryScorer 对命中结果进行评分操作
Fragmenter 将原始字符串拆分成独立的片段
NullFragmenter 对较短的域进行整体高亮
FastVectorHighlighter 基于快速高亮
Encoder 提供一些实现类,对html文本操作,如,去掉一些特殊匹配符号<,> and so on,及一些其他的非ASCII特殊字符。
* @throws Exception
*/
/**
* 普通高亮
* @throws Exception
*/
@Test
public void testHighlighter() throws Exception{
String txt = "我爱北京天安门,天安门上彩旗飞,伟大领袖毛主席,指引我们向前进,向前进!!!想起身离开东京法律思考的机会那个上的讲话那伟大的个圣诞sadfsadnfl.sajdfl;aksjdf;lsadfsadfm.asd那是肯定激发了深刻的机会拉萨宽带计费了那个傻大姐华纳公司的机会节贺卡就是对话框那是国天安门际 北京电话卡开始觉啊 北京得人们大会堂 北京!!!!";
QueryParser parser = new QueryParser("content", new MMSegAnalyzer());
Query query = parser.parse("北京 伟大");
QueryScorer queryScorer = new QueryScorer(query);//如果有需要,可以传入评分
//设置高亮标签
Formatter formatter = new SimpleHTMLFormatter("<span style='color:red;'>", "</span>");
//高亮分析器
Highlighter hl = new Highlighter(formatter, queryScorer);
Fragmenter fragmenter = new SimpleSpanFragmenter(queryScorer);
hl.setTextFragmenter(fragmenter);
//获取返回结果
String str = hl.getBestFragment(new MMSegAnalyzer(), "content",txt);
System.out.println(str);
}
/**测试文件生成索引**/
@Test
public void createIndex(){
FileIndexUtils.index(true);//每次都重新生成索引
}
/**
* 根据制定字段从标题和内容中匹配,并高亮显示
*/
@Test
public void testSearchByHighlighter(){
try{
Analyzer a = new StandardAnalyzer();
String fieldName = "title";
String queryTxt = "java";
FileIndexUtils fiu = new FileIndexUtils();
IndexSearcher search = fiu.getSearcher();
/**查询单个域 start **/
/** QueryParser parser = new QueryParser(fieldName, a);
Query q = parser.parse(queryTxt);
TopDocs tds = search.search(q,20);
for(ScoreDoc sd :tds.scoreDocs){
Document doc = search.doc(sd.doc);
String title = doc.get(fieldName);
title = lighterStr(q, a, fieldName, title);
System.out.println(title);
}
**/
/**查询单个域 end **/
/**设定多个查询域 start
*
* 如果要实现根据文章的title来推荐类似文章的话,使用Lucene的MoreLikeThis可以很方便的实现。
MoreLikeThis类在org.apache.lucene.search.similar包下,通过分词器将给定的文本进行分词、记录词频,并根据词频信息转换为查询的Query对象,再根据Query对象查询出相似的结果。
所以,Lucene的MoreLikeThis功能并没有考虑用户协同过滤的因素,应付一般的需求还是够用了。
* **/
String[] fieldNames = new String[]{"title","content"};
String searchTxt = "java";
MultiFieldQueryParser parser = new MultiFieldQueryParser(fieldNames, a);
Query q = parser.parse(searchTxt);
TopDocs tds = search.search(q, 20);
MoreLikeThis mlt = new MoreLikeThis(search.getIndexReader());//匹配相似
mlt.setFieldNames(fieldNames);
mlt.setMinDocFreq(1);//至少命中频率
mlt.setMinTermFreq(1);//至少在命中term的频率
mlt.setAnalyzer(a);
for(ScoreDoc sd:tds.scoreDocs) {
Document doc = search.doc(sd.doc);
String title = doc.get(fieldNames[0]);
title = lighterStr(q, a, fieldNames[0], title);
System.out.println(title);
System.out.println("-------------------------");
//Return a query that will return docs like the passed lucene document ID
Query moreLike = mlt.like(sd.doc);
TopDocs sds = search.search(moreLike, 10);
for(ScoreDoc sdd:sds.scoreDocs) {
Document d = search.doc(sdd.doc);
System.out.println("--->"+d.get("title"));
}
}
/**设定多个查询域 end **/
}catch(Exception e){
e.printStackTrace();
}
}
/**
*
* @param query Lucene Query
* @param a 分词器
* @param fieldName 查询的字段域
* @param txt 从txt中进行内容查询
* @return
* @throws IOException
* @throws InvalidTokenOffsetsException
*/
private String lighterStr(Query query,Analyzer a,String fieldName,String txt) throws IOException, InvalidTokenOffsetsException {
String str = null;//设定放回结果
QueryScorer queryScorer = new QueryScorer(query);//如果有需要,可以传入评分
//设置高亮标签
Formatter formatter = new SimpleHTMLFormatter("<span style='color:red;'>", "</span>");
//高亮分析器
Highlighter hl = new Highlighter(formatter, queryScorer);
Fragmenter fragmenter = new SimpleSpanFragmenter(queryScorer);
hl.setTextFragmenter(fragmenter);
//获取返回结果
str = hl.getBestFragment(a, fieldName,txt);
if(str == null){
return txt;
}
return str;
}
/**
* 快速高亮,FastVectorHighlighter,这个类可能会消耗更多的存储空间,来换取更好的性能,当然除了性能上提升外,它还有一个非常炫的功能,支持多种颜色标记,高亮关键字,除此之外还支持Ngram的域,以及智能合并相邻高亮短语.
*/
@Test
public void testFastHighlighter(){
Analyzer a = new StandardAnalyzer();
FileIndexUtils fiu = new FileIndexUtils();
IndexSearcher search = fiu.getSearcher();
String[] fields = new String[]{"title","content"};
MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, a);
try {
Query query = parser.parse("java");
TopDocs tds = search.search(query, 20);
FragListBuilder fragListBuilder = new SimpleFragListBuilder();
//注意下面的构造函数里,使用的是颜色数组,用来支持多种颜色高亮
// 可以在这里定义高亮关键词的样式,比如:
// 改BaseFragmentsBuilder.COLORED_PRE_TAGS为new String[]{"<EM>"}
// 改BaseFragmentsBuilder.COLORED_POST_TAGS为new String[]{"</EM>"}
FragmentsBuilder fragmentsBuilder = new ScoreOrderFragmentsBuilder(BaseFragmentsBuilder.COLORED_PRE_TAGS,BaseFragmentsBuilder.COLORED_POST_TAGS);
FastVectorHighlighter fvh = new FastVectorHighlighter(true,true, fragListBuilder, fragmentsBuilder);
FieldQuery fq = fvh.getFieldQuery(query);
System.out.println("命中--》"+tds.totalHits);
for(ScoreDoc sd:tds.scoreDocs){
//当查询不到高亮信息时,返回内容为Null
IndexReader reader = search.getIndexReader();
String highContent = fvh.getBestFragment(fq, reader, sd.doc,"content", 100);
System.out.println("highContent-->"+highContent);
String highTitle = fvh.getBestFragment(fq, reader, sd.doc,"title", 100);
if(highTitle==null){
Document doc = search.doc(sd.doc);
/**
* 如果高亮内容为null,那么表示标题没有需要高亮的内容,那么赋值为原有标题
*/
highTitle = doc.get("title");
}
System.out.println("highTitle-->"+highTitle);
System.out.println("------------------------");
}
} catch (ParseException e) {
e.printStackTrace();
} catch(IOException ioe){
ioe.printStackTrace();
}
}
}