在使用solr的时候,默认的高亮方法使用的是hl.method=original。solr6.4以后出现了一个新的高亮方法unifield,可以直接设置hl.method=unified。官方说法推荐使用这个高亮方法,更加灵活可以支持各类查询。在lucene底层这个方法对应的对象就是这个UnifiedHighlighter,但是没有看到关于这个类的使用方法,自己也不是很看懂api的介绍,试了一下用法可以得到高亮的字段信息。
public static UnifiedHighlighter buildHighlighter(IndexSearcher searcher, Analyzer chAnalyzer) {
PassageFormatter passageFormatter = new DefaultPassageFormatter(LuceneConstant.PRETAG, LuceneConstant.POSTTAG, "...", true);
PassageScorer score = new PassageScorer();
UnifiedHighlighter uniHighlighter = new UnifiedHighlighter(searcher, chAnalyzer);
uniHighlighter.setFormatter(passageFormatter);
uniHighlighter.setScorer(score);
return uniHighlighter;
}
PassageFormatter :设置高亮的一些格式,LuceneConstant.PRETAG与LuceneConstant.POSTTAG是我设置的变量,其实就是高亮的前后缀:<font color='red'>与</font>,其他两个参数没弄明白什么意思,暂且按照默认的设置。
PassageScorer :类似于QueryScore,详细介绍也不是很明白。
将UnifiedHighlighter 设置好后就可以得到highlighter对象。获取高亮字段有几个方法,可以单独获取一个高亮字段。也可以获取一组高亮字段:
获取单一字段:
public String[] highlight(String field, Query query, TopDocs topDocs, int maxPassages) throws IOException {
Map<String, String[]> res = highlightFields(new String[]{field}, query, topDocs, new int[]{maxPassages});
return res.get(field);
}
获取多个字段:
public Map<String, String[]> highlightFields(String[] fields, Query query, TopDocs topDocs, int[] maxPassages)
throws IOException {
final ScoreDoc scoreDocs[] = topDocs.scoreDocs;
int docids[] = new int[scoreDocs.length];
for (int i = 0; i < docids.length; i++) {
docids[i] = scoreDocs[i].doc;
}
return highlightFields(fields, query, docids, maxPassages);
}
我们在使用的时候,可以将需要的高亮字段建立一个数组,直接调用highlighter.highlightFields方法就可以得到高亮字段的Map集合,遍历Map集合就可以获取对应的字段值。但是这个地方需要注意的是Map里面key值存的是字段的名称,value存的是命中条数对应的字段内容的数组集合。也就是说你设置了四个高亮字段,搜索命中了10条数据,返回的高亮Map集合的大小就是4,但是每一个key对应的value数组大小是10,所以在获取每一个命中目标对应的字段内容时,需要对topDocs遍历:
public SearchResult convertResult(TopDocs topDocs,BooleanQuery booleanQuery,IndexSearcher searcher)
throws IOException, InvalidTokenOffsetsException{
Analyzer chAnalyzer = new IndexPinyinAnalyzer(false);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
QueryScorer scorer = new QueryScorer(booleanQuery);
//设定高亮显示的格式
UnifiedHighlighter uniHighlighter = LuceneUtil.buildHighlighter(searcher, chAnalyzer);
String[] highField = LuceneUtil.buildHighFields();
Map<String,String[]> contentHigh = uniHighlighter.highlightFields(highField, booleanQuery, topDocs);
for (int i = 0; i < scoreDocs.length ; i++) {
SearchItem item=new SearchItem();
Document doc = searcher.doc(scoreDocs[i].doc);
//contentHigh.get("content")获取到的是content字段命中的所有条数的value 的数组,
遍历后直接取对应的i项就是这条命中的content字段的value值,不明白可以打个断点看一下Map里面的结构
if(null==contentHigh.get("content")[i]) {
item.setHlText(doc.get("content"));
}else {
item.setHlText(contentHigh.get("content")[i]);
}
//....获取其他字段类似
}
}
但是我在获取到高亮数据后的一个问题时就是,我所有字段设置了FieldType为:
FieldType type = new FieldType(TextField.TYPE_STORED);
type.setStoreTermVectorOffsets(true);//记录相对增量
type.setStoreTermVectorPositions(true);//记录位置信息
type.setStoreTermVectors(true);//存储向量信息
type.freeze();//阻止改动信息
才会返回正确的高亮信息,其他没有设置过得只返回普通数据,没有高亮,这个不知道是什么原因,看官方的介绍:
A Highlighter that can get offsets from either postings (IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS), term vectors (FieldType.setStoreTermVectorOffsets(boolean)), or via re-analyzing text.
好像是必须设置type才可以,但是在solr中使用时好像都可以正常高亮,期待大神解答。