一、前言

在实际使用中搜索结果中的关键词前端通常会以特殊形式展示,比如标记为红色使人一目了然。我们可以通过 ES 提供的高亮功能实现此效果。


二、代码实现

前文查询是通过一个继承 ElasticsearchRepository 的接口实现的,但是如果要实现高亮,这种方式就满足不了了,这里我们需要通过 ElasticsearchTemplate 来完成。

2.1 注入 ElasticsearchTemplate

① ElasticsearchTemplate 类简介

public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {...省略其余部分...
}

从上述源码中可以看到 ElasticsearchTemplate 实现了 ApplicationContextAware 接口,表明这个类是被 Spring 管理的,可以直接注入使用。

② 业务实现类注入 ElasticsearchTemplate

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

2.2 查询对象指定高亮字段

在构建查询对象时需要指定高亮字段,通过 withHighlightFields 方法设置。

private SearchQuery getKnowledgeSearchQuery(KnowledgeSearchParam param) {Pageable pageable = PageRequest.of(param.getStart() / param.getSize(), param.getSize());String knowledgeTitleFieldName = "knowledgeTitle";String knowledgeContentFieldName = "knowledgeContent";String preTags = "<span style=\"color:#F56C6C\">";String postTags = "</span>";HighlightBuilder.Field knowledgeTitleField = new HighlightBuilder.Field(knowledgeTitleFieldName).preTags(preTags).postTags(postTags);HighlightBuilder.Field knowledgeContentField = new HighlightBuilder.Field(knowledgeContentFieldName).preTags(preTags).postTags(postTags);BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();queryBuilder.must(QueryBuilders.termQuery("isDeleted", IsDeletedEnum.NO.getKey()));queryBuilder.should(QueryBuilders.matchQuery(knowledgeTitleFieldName, param.getKeyword()));queryBuilder.should(QueryBuilders.matchQuery(knowledgeContentFieldName, param.getKeyword()));return new NativeSearchQueryBuilder().withPageable(pageable).withQuery(queryBuilder).withHighlightFields(knowledgeTitleField, knowledgeContentField).build();
}

2.3 自定义 ResultMapper

ResultMapper 是用于将 ES 文档转换成 Java 对象的映射类,因为 Spring Data Elasticsearch 默认的的映射类 DefaultResultMapper 不支持高亮,因此,我们需要自定义一个 ResultMapper 。

完整代码如下:

@Slf4j
@Component
public class HighlightResultHelper implements SearchResultMapper {private static ObjectMapper objectMapper = new ObjectMapper();static {objectMapper.setVisibility(JsonMethod.FIELD, JsonAutoDetect.Visibility.ANY);objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);}private static final Pattern SUB_FIELD_PATTERN = Pattern.compile("\\..*");private static final String HIGHLIGHT_FIELD_SUFFIX = "Highlight";@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {long totalHits = response.getHits().getTotalHits();List<T> list = Lists.newArrayList();// 获取搜索结果SearchHits hits = response.getHits();for (SearchHit searchHit : hits) {if (hits.getHits().length <= 0) {continue;}// 获取高亮字段MapMap<String, HighlightField> highlightFields = searchHit.getHighlightFields();// 通过jackson将json字符串转化为对象T item = jsonStrToObject(searchHit.getSourceAsString(), clazz);if (Objects.isNull(item)) {continue;}// 遍历高亮字段Map,将高亮字段key转化为原始字段名(title.pinyin -> title),拼接高亮文本并与原始字段名组装为一个MapMap<String, String> highlightFieldMap = Maps.newHashMap();for (Map.Entry<String, HighlightField> highlightField : highlightFields.entrySet()) {String key = SUB_FIELD_PATTERN.matcher(highlightField.getKey()).replaceAll(Constants.BLANK) + HIGHLIGHT_FIELD_SUFFIX;HighlightField value = highlightField.getValue();Text[] fragments = value.getFragments();StringBuilder sb = new StringBuilder();for (Text text : fragments) {sb.append(text);}highlightFieldMap.put(key, sb.toString());}// 通过反射将高亮文本赋值到原始字段对应的高亮字段中try {Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {if (!field.getName().contains(HIGHLIGHT_FIELD_SUFFIX)) {continue;}field.setAccessible(true);if (highlightFieldMap.containsKey(field.getName())) {field.set(item, highlightFieldMap.get(field.getName()));} else {field.set(item, searchHit.getSource().get(field.getName().replace(HIGHLIGHT_FIELD_SUFFIX, Constants.BLANK)));}}} catch (Exception e) {e.printStackTrace();}list.add(item);}return new AggregatedPageImpl<>(list, pageable, totalHits);}private <T> T jsonStrToObject(String json, Class<T> cls) {try {return objectMapper.readValue(json, cls);} catch (IOException e) {log.error("json cant be objectTranslate to object,{}", json);return null;}}
}

2.4 获取返回结果

① 返回对象增加高亮字段

@Data
@Document(indexName = "knowledge", type = "knowledge")
public class KnowledgeDO {...省略其余部分...private String knowledgeTitleHighlight;private String knowledgeContentHighlight;
}

② 业务实现类注入 HighlightResultHelper

@Autowired
private HighlightResultHelper highlightResultHelper;

③ 获取分页结果由前文的 knowledgeRepository.search 改为 elasticsearchTemplate.queryForPage 实现,查询时指定 highlightResultHelper

Page<KnowledgeDO> page = elasticsearchTemplate.queryForPage(searchQuery, KnowledgeDO.class, highlightResultHelper);

注:测试结果展示

[{"id": 850,"knowledgeTitle": "小儿腺样体肥大的孩子宜多吃什么?","knowledgeTitleHighlight": "小儿腺样体肥大的孩子宜多吃什么?","knowledgeContent": "1、饮食中要停掉一切寒凉的食物,只吃性平、性温的食物,如猪肉、鸡肉、牛肉、鸽肉、鹌鹑、鳝鱼、泥鳅、青菜、白菜、包菜、黄豆芽、土豆、韭菜、胡萝卜(一周2次)等,夏天再增加四季豆、豇豆、黄瓜、西红柿、藕、芹菜、花菜、各种菌类(菌类也偏凉适合夏天吃),水果吃新鲜时令的水果,5月份以后,新鲜水果上市了。可以吃草莓、桃子、葡萄、樱桃,秋天可以吃苹果、梨子、桔子等。\n2、每周吃2-3次红烧鳝鱼或喝鳝鱼汤,鳝鱼与其它鱼类不同,补血、补肾、抗过敏的作用明显,但不易上火,补而不燥。每周吃2次海虾,一次10只左右,7岁左右的孩子可以一次半斤,海虾就是鸡尾虾或对虾,补肾阳的作用明显,可以用来治疗慢性扁桃体炎、慢性鼻炎、慢性咽炎,与河虾的功效完全不一样。","knowledgeContentHighlight": "1、饮食中要停掉一切寒凉的食物,只吃性平、性温的食物,如猪肉、鸡肉、牛肉、鸽肉、鹌鹑、鳝鱼、泥鳅、青菜、白菜、包菜、黄豆芽、土豆、韭菜、胡萝卜(一周2次)等,夏天再增加四季豆、豇豆、黄瓜、<span style=\"color:#F56C6C\">西红柿</span>、藕","referenceCount": 0}
]

三、结语

至此搜索结果高亮已经实现完毕,下一篇将介绍相关度排序优化。

转载于:https://www.cnblogs.com/orzlin/p/10496867.html

从零搭建 ES 搜索服务(五)搜索结果高亮相关推荐

  1. No6-6.从零搭建spring-cloud-alibaba微服务框架,添加用户鉴权逻辑,动态数据权限(使用AOP实现)等(六,no6-6)

    代码地址与接口看总目录:[学习笔记]记录冷冷-pig项目的学习过程,大概包括Authorization Server.springcloud.Mybatis Plus~~~_清晨敲代码的博客-CSDN ...

  2. 高德地图-搜索服务-POI搜索

    高德地图-搜索服务-POI搜索 之前公司项目收货地址仿饿了么的收货地址,结果发现自己实现的关键字搜索和周边搜索,搜索到的poi列表跟饿了么的并不完全一样,后来考虑了下,应该是搜索的范围.类型之类的设置 ...

  3. 【高德地图API】从零开始学高德JS API(四)搜索服务——POI搜索|自动完成|输入提示|行政区域|交叉路口|自有数据检索

    原文地址为: [高德地图API]从零开始学高德JS API(四)搜索服务--POI搜索|自动完成|输入提示|行政区域|交叉路口|自有数据检索 摘要:地图服务,大家能想到哪些?POI搜素,输入提示,地址 ...

  4. No6-3.从零搭建spring-cloud-alibaba微服务框架,实现资源端用户认证与授权等(三,no6-3)

    代码地址与接口看总目录:[学习笔记]记录冷冷-pig项目的学习过程,大概包括Authorization Server.springcloud.Mybatis Plus~~~_清晨敲代码的博客-CSDN ...

  5. 从零搭建ES搜索服务(一)基本概念及环境搭建

    一.前言 本系列文章最终目标是为了快速搭建一个简易可用的搜索服务.方案并不一定是最优,但实现难度较低. 二.背景 近期公司在重构老系统,需求是要求知识库支持全文检索. 我们知道普通的数据库 like ...

  6. 从零搭建 ES 搜索服务(四)拼音搜索

    一.前言 上篇介绍了 ES 的同义词搜索,使我们的搜索更强大了,然而这还远远不够,在实际使用中还可能希望搜索「fanqie」能将包含「番茄」的结果也罗列出来,这就涉及到拼音搜索了,本篇将介绍如何具体实 ...

  7. 从零搭建 Spring Cloud 服务(超级详细)

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ ...

  8. 微服务之如何从零搭建(吹牛逼篇)

    [版权申明] 非商业目的注明出处可自由转载 出自:shusheng007 概述 IT世界唯有变化才是永恒的.这不微服务刚兴起没有几年,现如今已经在全力向云原生时代过度了,有人称其为后微服务时代.云原生 ...

  9. VS Code 扩展 WebTS 早期预览版发布;微软开源其搜索服务的 SPTAG 算法

    (给技术最前线加星标,每天看技术热点) 转自:开源中国.solidot.cnBeta.腾讯科技.快科技等 [技术资讯] 0.VS Code 扩展 WebTS 早期预览版发布:帮助创建新 Web 应用 ...

  10. 淘淘商城第43讲——搭建搜索服务工程

    Solr服务配置好之后,接下来我们就要考虑一个问题了,那就是我们要把商品数据导入到索引库里面才行,否则的话,我们是没有办法实现商品搜索这个功能的,可以想见我们势必要搭建一个搜索服务工程了. 我们还是先 ...

最新文章

  1. 南方科技大学唐圆圆组招聘环境相关领域科研人才(年薪33~50万)
  2. 李开复Bengio大咖对话:下一阶段AI最大机遇在这4个领域
  3. 在线python编程编译器-编译器python
  4. 原生html小游戏,原生JS实现别踩白块小游戏(一)
  5. openresty package.path require 报错
  6. (转)B2B2C,从营销的角度,来理解SaaS
  7. Elasticsearch SQL插件
  8. cluster(3)
  9. 268、缺失数字(python)
  10. Python系列之Python-docx生成运行日报Word模板
  11. 19本高并发编程书籍推荐
  12. 【STM32】 JR6001语音播放
  13. Win7局域网内找不到其他电脑怎么解决
  14. VTP技术及相关配置
  15. java 其他文件转pdf_java 其他文件转成pdf java生成pdf
  16. 记录一次使用LKAdoc时出现的很low很low很low的问题
  17. 初次使用Pikachu漏洞平台进行测试实验
  18. 计算机硬件系统维护经验与体会,计算机操作系统维护与优化的实训报告心得体会.docx...
  19. html导出带页码的word,使用js把html导出word,并配置样式和分页功能
  20. (c++)五分制成绩(函数实现)

热门文章

  1. 学习:Ubuntu14.04编译caffe问题记录
  2. Windows 10 开启卓越性能模式
  3. Photoshop 快捷键大全
  4. (XWZ)的Python学习笔记Ⅲ——面向对象高级编程
  5. 究竟云计算就业前景好不 零基础如何学云计算
  6. 关于LinkList和LNode*
  7. 基于 AWS 的一站式分布式数据库测试体系,简单易上手|TiDB Hackathon 2020 优秀项目分享
  8. 一个美国人在上海-American McGee
  9. 可视化滤波器fvtool
  10. 图片转Word文档怎么转