ES检索结果高亮显示JAVA以及Kibana实现

java 复制代码
/**
 * 查询接口
 *
 * @param searchReqVO
 */
public EsSearchPageInfoResVO guessYouWantListForClient(EsSearchRequestVO searchReqVO) {
    BaseInfo baseInfo = getApp();
    List<Long> catalogues = getAccesses();
    EsSearchPageInfoResVO result = new EsSearchPageInfoResVO();
    SearchRequest request = new SearchRequest();
    CountRequest countRequest = new CountRequest();
    countRequest.indices(INDEX_NAME);
    request.indices(INDEX_NAME);
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags("<em style='color: red'>");
    highlightBuilder.postTags("</em>");
    highlightBuilder.field("question_info");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    boolQueryBuilder.must(QueryBuilders.matchQuery("base_info_id", baseInfo.getId()));
    int shouldCount = 0;
    if (!StringUtils.isEmpty(searchReqVO.getSearchText())) {
        shouldCount++;
        boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("question_info", searchReqVO.getSearchText()));
        boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("answer_info", searchReqVO.getSearchText()));
        boolQueryBuilder.should(QueryBuilders.matchPhraseQuery("keyword", searchReqVO.getSearchText()));
        boolQueryBuilder.should(QueryBuilders.matchQuery("question_info", searchReqVO.getSearchText()));
        boolQueryBuilder.should(QueryBuilders.matchQuery("answer_info", searchReqVO.getSearchText()));
        boolQueryBuilder.should(QueryBuilders.matchQuery("keyword", searchReqVO.getSearchText()));
    }
    boolQueryBuilder.minimumShouldMatch(shouldCount);
    countRequest.query(boolQueryBuilder);
    //设置分页 from:页码,(当前页-1)*每页条数
    searchSourceBuilder.from(searchReqVO.getRows() * (searchReqVO.getPage() - 1));
    searchSourceBuilder.size(searchReqVO.getRows());
    searchSourceBuilder.query(boolQueryBuilder);
    searchSourceBuilder.highlighter(highlightBuilder);


    //未输入模糊搜索内容时默认按更新时间排序、输入则默认按es相似度分值排序
    if (StringUtils.isEmpty(searchReqVO.getSearchText())) {
        searchSourceBuilder.sort("update_timestamp", SortOrder.DESC);
    }
    request.source(searchSourceBuilder);
    SearchResponse searchResponse = null;
    CountResponse countResponse = null;
    List<EsSearchResponseVO> resultList = new ArrayList<>();
    try {
        countResponse = highLevelClient.count(countRequest, RequestOptions.DEFAULT);
        Long totalCount = countResponse.getCount();
        result.setTotal(totalCount);
        searchResponse = highLevelClient.search(request, RequestOptions.DEFAULT);
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        for (SearchHit searchHit : searchHits) {
            //原理就是用es自动查找出来的hightlight字段值替换正常检索出来的值
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            HighlightField highlightTitle = highlightFields.get("question_info");//注意是数组
            Map<String, Object> sourceMap = searchHit.getSourceAsMap();
            if(highlightTitle != null){
                Text[] fragments = highlightTitle.getFragments();
                if(fragments != null && fragments.length > 0){
                    //替换(fargment[0]是Text类型的)
                    sourceMap.replace("question_info", fragments[0].toString());
                }
            }
            ESQuestionAnswerVersionDTO esResult = JSON.parseObject(JSON.toJSONString(sourceMap), ESQuestionAnswerVersionDTO.class);
            EsSearchResponseVO vo = new EsSearchResponseVO();
            vo.setQuestionInfo(esResult.getQuestion_info());
            vo.setKnowledgeId(esResult.getKnowledge_id());
            vo.setId(esResult.getId());
            vo.setBaseInfoId(esResult.getBase_info_id());
            resultList.add(vo);
        }
    } catch (Exception e) {
        log.info("联想搜索知识失败,搜索条件: ", JSONUtil.toJsonStr(searchReqVO));
        Traces.recordException(e);
    }
    result.setRows(resultList);
    return result;
}

对比做了高亮前后的结果返回:

高亮前:

高亮后:

可以看到加入高亮的代码之后返回的json串命中的关键字被套了一层<em style='color: red'>xxx</em>标签,也就是我们前置设置的preTags与postTags;

当然hightlight本身支持多个字段高亮,java代码实现只要设置多个

java 复制代码
highlightBuilder.field("aaaa");

highlightBuilder.field("bbb");

...

后续查询出结果之后挨个全部替换成hightlight的结果即可。


翻译成es的kibana语句如下:

bash 复制代码
{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 22.00881,
    "hits" : [
      {
        "_index" : "knowledge_question_answer",
        "_type" : "_doc",
        "_id" : "12494",
        "_score" : 22.00881,
        "_source" : {
          "id" : 12494,
          "question_info" : "香肠/腊肠/金字火腿常见问题",
          "answer_info" : "万有全广式香肠蒸出来口感很粉,&nbsp; 是面粉放多了吗",
          "keyword" : "香肠发酸,香肠,腊肠,金字火腿,火腿,金字金华香肠,腊肠发酸,万有全广式香肠"
        },
        "highlight" : {
          "question_info" : [
            "<em style='color: red'>香肠</em>/腊肠/金字火腿常见问题"
          ]
        }
      }
    ]
  }
}

这里只设置了一个字段高亮,只要该字段有匹配到的关键字就会被放到结果集的高亮那一栏中。结果如下:

bash 复制代码
GET /knowledge_question_answer/_doc/_search
{
  "from": 0,
  "size": 20,
  "query": {
    "bool": {
      "should": [
        //查询条件忽略
        ...
      ],
      "adjust_pure_negative": true,
      "minimum_should_match": "1",
      "boost": 1
    }
  },
  "highlight": {
    "pre_tags": [
      "<em style='color: red'>"
    ],
    "post_tags": [
      "</em>"
    ],
    "fields": {
      "question_info": {},
      "answer_info": {},
      "keyword": {}
    }
  }
}

多个高亮查询结果如下:

bash 复制代码
{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 22.00881,
    "hits" : [
      {
        "_index" : "knowledge_question_answer",
        "_type" : "_doc",
        "_id" : "12494",
        "_score" : 22.00881,
        "_source" : {
          "id" : 12494,
          "question_info" : "香肠/腊肠/金字火腿常见问题",
          "answer_info" : "万有全广式香肠蒸出来口感很粉,&nbsp; 是面粉放多了吗",
          "keyword" : "香肠发酸,香肠,腊肠,金字火腿,火腿,金字金华香肠,腊肠发酸,万有全广式香肠"
        },
        "highlight" : {
          "answer_info" : [
"万有全广式<em style='color: red'>香肠</em>蒸出来口感很粉,&nbsp; 是面粉放多了吗?"
          ],
          "question_info" : [
            "<em style='color: red'>香肠</em>/腊肠/金字火腿常见问题"
          ],
          "keyword" : [
            "<em style='color: red'>香肠</em>发酸,<em style='color: red'>香肠</em>,腊肠,金字火腿,火腿,金字金华<em style='color: red'>香肠</em>,腊肠发酸,万有全广式<em style='color: red'>香肠</em>"
          ]
        }
      }
    ]
  }
}

可以看到,多个field只要出现检索词"香肠"的地方 都被套上了前置后置的标签,展示在前端页面也就又了高亮显示的效果。

相关推荐
陌殇殇15 分钟前
002 SpringCloudAlibaba整合 - Feign远程调用、Loadbalancer负载均衡
java·spring cloud·微服务
猎人everest1 小时前
SpringBoot应用开发入门
java·spring boot·后端
山猪打不过家猪3 小时前
ASP.NET Core Clean Architecture
java·数据库·asp.net
AllowM3 小时前
【LeetCode Hot100】除自身以外数组的乘积|左右乘积列表,Java实现!图解+代码,小白也能秒懂!
java·算法·leetcode
不会Hello World的小苗4 小时前
Java——列表(List)
java·python·list
二十七剑5 小时前
jvm中各个参数的理解
java·jvm
东阳马生架构6 小时前
JUC并发—9.并发安全集合四
java·juc并发·并发安全的集合
Elastic 中国社区官方博客6 小时前
Elasticsearch Open Inference API 增加了对 Jina AI 嵌入和 Rerank 模型的支持
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索·jina
计算机小白一个6 小时前
蓝桥杯 Java B 组之岛屿数量、二叉树路径和(区分DFS与回溯)
java·数据结构·算法·蓝桥杯
隔壁老王1567 小时前
mysql实时同步到es
数据库·mysql·elasticsearch