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只要出现检索词"香肠"的地方 都被套上了前置后置的标签,展示在前端页面也就又了高亮显示的效果。

相关推荐
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸4 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象4 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了5 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·5 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王5 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康5 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神6 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
qq_327342736 小时前
Java实现离线身份证号码OCR识别
java·开发语言