highlighter
html
highlighter 高亮也叫plain高亮,该方式有必定的优势也有必定的缺点,先说说缺点。highlighter方式高亮是个实时分析处理高亮器。即用户在查询的时候,es取到了符合条件的docid后,将须要高亮的字段数据提取到内存,再调用该字段的分析器进行分词,分词完毕后采用类似度算法计算得分最高的前n组并高亮段返回数据。以ansj分析器为例,官方给出的性能在60-80万字/每秒,但实际上中服务器运行效率会小于该值(服务器主频都比较低),在生产环境下,ansj分词效率大多在 40-50万字/秒。假设用户搜索的都是比较大的文档同时须要进行高亮。按照一页查询40条(每条数据20k)的方式进行显示,即便类似度计算以及搜索排序不耗时,整个查询也会被高亮拖累到接近两秒,这种查询就有点没法忍受了。
java
缺点:
算法
(1)fast-vector-highlighter 高亮方式须要存储词向量,而在词库丰富的系统中,存储词向量每每要比不存储词向量多占用一倍的空间。apache
(2)fast-vector-highlighter 高亮会比plain高亮多出至少一倍的io操做次数,读取的字节大小也多出至少一倍,大量的io请求会让搜索引擎并发能力下降。 默认plain高亮方式占用空间小,可是对大字段高亮慢,fvh对大字段高亮快,但占用空间过大,有没有一种高亮方式能够折中一些,即不要占用太大空间,对大字段分词也会太慢?固然有,lucene还提供了postings-highlighter(postings)高亮方式,postings-highlighter 高亮方式也是采用词量向量的方式进行高亮,与fvh高亮不一样的是postings高亮只存储了词向量的位置信息,并未存储词向量的偏移量,故中大字段存储中,其比fvh节省约20-30%的存储空间。在实际使用中,postings高亮的优势和缺点都不突出,故高亮时候对小字段采用highlighter高亮方式,大字段采用fast-vector-highlighter便可知足需求。
缓存
2. 最优化返回(须要计算最符合或者高亮词数最多的钱n段)服务器
3. 高亮时候容许不区分大小写匹配,不区分全角半角匹配高亮
并发
代码测试。app
测试样本:elasticsearch
(1)单条约10K的文本数据索引(索引总大小1.5g)。ide
(2)检索文本中包含 “国美电器” 关键词文章并高亮返回40条
采用fast-vector-highlighter 高亮方式耗时336毫秒:
采用fast--highlighter 高亮耗时132毫秒:
{
从结果能够看到,本身实现的高亮器能够比fast高亮器性能提升一倍以上,以下为fast-highlighter 的核心代码。
package org.elasticsearch.search.highlight; import com.google.common.collect.Maps; import org.apache.lucene.search.highlight.*; import org.apache.lucene.search.vectorhighlight.BoundaryScanner; import org.apache.lucene.search.vectorhighlight.CustomFieldQuery; import org.apache.lucene.search.vectorhighlight.FieldQuery; import org.apache.lucene.search.vectorhighlight.SimpleBoundaryScanner; import org.apache.lucene.search.vectorhighlight.FieldQuery.Phrase; import org.apache.lucene.util.BytesRefHash; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.text.Text; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.fetch.FetchPhaseExecutionException; import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.internal.SearchContext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * * @author jkuang.nj * */ public class FastPlainHighlighter implements Highlighter { private static final String CACHE_KEY = "highlight-fast"; public static final char mark = 0; private static final SimpleBoundaryScanner DEFAULT_BOUNDARY_SCANNER = new SimpleBoundaryScanner(); @Override public HighlightField highlight(HighlighterContext highlighterContext) { SearchContextHighlight.Field field = highlighterContext.field; SearchContext context = highlighterContext.context; FetchSubPhase.HitContext hitContext = highlighterContext.hitContext; FieldMapper mapper = highlighterContext.mapper; Encoder encoder = field.fieldOptions().encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT; if (!hitContext.cache().containsKey(CACHE_KEY)) { hitContext.cache().put(CACHE_KEY, new HighlighterEntry()); } HighlighterEntry cache = (HighlighterEntry) hitContext.cache().get(CACHE_KEY); try { FieldQuery fieldQuery; if (field.fieldOptions().requireFieldMatch()) { if (cache.fieldMatchFieldQuery == null) { cache.fieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query, hitContext.topLevelReader(), true, field.fieldOptions().requireFieldMatch()); } fieldQuery = cache.fieldMatchFieldQuery; } else { if (cache.noFieldMatchFieldQuery == null) { cache.noFieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query, hitContext.topLevelReader(), true, field.fieldOptions().requireFieldMatch()); } fieldQuery = cache.noFieldMatchFieldQuery; } if (!cache.analysises.containsKey(field.field())) { cache.setPhrases(field.field(), fieldQuery.getPhrases(field.field())); cache.setWords(field.field(), fieldQuery.getTermSet(field.field())); } FastHighlighter entry = cache.mappers.get(mapper); if (entry == null) { BoundaryScanner boundaryScanner = DEFAULT_BOUNDARY_SCANNER; if (field.fieldOptions().boundaryMaxScan() != SimpleBoundaryScanner.DEFAULT_MAX_SCAN || field.fieldOptions().boundaryChars() != SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS) { boundaryScanner = new SimpleBoundaryScanner(field.fieldOptions().boundaryMaxScan(), field.fieldOptions().boundaryChars()); } CustomFieldQuery.highlightFilters.set(field.fieldOptions().highlightFilter()); entry = new FastHighlighter(encoder, boundaryScanner); cache.mappers.put(mapper, entry); } String[] fragments; int numberOfFragments = field.fieldOptions().numberOfFragments() == 0 ? 1 : field.fieldOptions().numberOfFragments(); int fragmentCharSize = field.fieldOptions().numberOfFragments() == 0 ? 50 : field.fieldOptions().fragmentCharSize(); List