跳至主要內容

Elasticsearch-高亮查询

Zenghr大约 4 分钟

Elasticsearch 高亮查询

日常生活中我们使用搜索工具尝试查询一些信息的时候,常常可以看到返回的结果集中和我们查询条件相符合的字段被特殊的颜色所标记,这就是结果高亮显示。通过高亮显示用户可以明显的发现查询匹配的位置,Elasticsearch 使用 highlight 来实现搜索结果中一个或多个字段突出显示

例子🌰

举个🌰 - 查询 攻略中带上海的数据 - 多字段匹配

映射结构

下面是例子所使用的映射结构

注意:这里使用了 IK 分词器,如需了解 IK 分词器请看 Elasticsearch 中文分词器

{
  "mappings": {
    "_doc": {
      "properties": {
        "id": {
          "type": "keyword"
        },
        "subTitle": {
          "type": "text",
          "store": true,
          "analyzer": "ik_max_word"
        },
        "summary": {
          "type": "text",
          "store": true,
          "analyzer": "ik_max_word"
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

执行如下请求之后会得到返回内容,在 hits 中除了常规数据外,还会有 highlight 对象

{
  "query": {
    "multi_match": {
      "query": "上海",
      "fields": ["title", "subTitle", "summary"]
    }
  },
  "highlight": {
    "fields": {
      "title": {
        "pre_tags": "<b>",
        "post_tags": "</b>"
      },
      "subTitle": {},
      "summary": {
        "pre_tags": "<b>",
        "post_tags": "</b>",
        "number_of_fragments": 2,
        "fragment_size": 10
      }
    }
  }
}

返回的结果里面每个 hit 多了一个类似这样部分:

{
  "hits" : {
    "hits" : [
      {
        "_index" : "strategy_es",
        "_type" : "_doc",
        "_id" : "7",
        "_score" : 2.4005952,
        "_source" : {
          "id" : 7,
          "title" : "懒人吃货指南 | 轻松吃遍地道上海小吃",
          "subTitle" : "轻松吃遍地道上海小吃",
          "summary" : "去一个地方旅行当然要尝尝当地的特色美食,不然总觉得这趟旅行不完整。但是对于不熟悉上海的游客来说,“哪些是老上海小吃?去哪里能吃到地道的?”这些都是头疼的问题。这篇攻略将会给大家介绍老上海小吃,推荐一些老字号,并附上周边游玩小Tips"
        },
        "highlight" : {
          "summary" : [
            "但是对于不熟悉<b>上海</b>的游客来说",
            ",“哪些是老<b>上海</b>小吃?"
          ],
          "subTitle" : [
            "轻松吃遍地道<em>上海</em>小吃"
          ],
          "title" : [
            "懒人吃货指南 | 轻松吃遍地道<b>上海</b>小吃"
          ]
        }
      }
    ]
  }
}

高亮设置

可以看到我们预设了 高亮关键字 的HTML标签进行包裹,下面我们来了解一下高亮 highlight 的参数定义,下面的参数可以设置在 highlight 的下一级此时为 全局设置,也可以设置在字段的下一级,此时为 字段设置

单个字段的设置优先级高于全局设置

number_of_fragments

要返回的最大片段数,如果片段数设置为 0,则不返回任何片段,而是突出显示并返回整个字段内容

当您需要突出显示标题或地址等短文本时,这会很方便,但不需要分段。如果number_of_fragments 为 0,fragment_size则忽略。默认为 5

例如上面👆我们设置了 number_of_fragments: 2 ,那么 summary 字段返回了两段数据

{
    "summary" : [
        "但是对于不熟悉<b>上海</b>的游客来说",
        ",“哪些是老<b>上海</b>小吃?"
    ]
}

如果设置成 0 ,就返回原本数据

{
    "summary" : [
            "去一个地方旅行当然要尝尝当地的特色美食,不然总觉得这趟旅行不完整。但是对于不熟悉<b>上海</b>的游客来说,“哪些是老<b>上海</b>小吃?去哪里能吃到地道的?”这些都是头疼的问题。这篇攻略将会给大家介绍老<b>上海</b>小吃,推荐一些老字号,并附上周边游玩小Tips"
    ]
}

fragment_size

一段 fragment 包含多少个字符(以字符为单位)默认为 100

pre_tags

标记 highlight 的开始标签,例如上面的 <b> 标签

post_tags

标记 highlight 的结束标签,例如上面的 </b> 标签

系统预设的标签为 <em>

order

order控制了返回对象中 highlight 片段的排序。下面例子中返回的高亮片段将会根据分数顺序输出。假如设置了none则是按照顺序输出

提示

最准确的文档还是官网,更多的参数请参考官方文档open in new window

Java 代码实现高亮查询

提示

在这里我们就不介绍 Elasticsearch 和 SpringBoot 的集成,如果不会请看上一篇文章 SpringBoot 集成 Elasticsearch

框架版本
SpringBoot2.5.3
Elasticsearch7.14.0
Spring Data Elasticsearch4.2.3
@Autowired
private ElasticsearchRestTemplate restTemplate;
// ----------------------------------------------
// 构建查询对象
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
// 获取字段
String[] fields = new String[]{"title", "subTitle", "summary"};

// 多字段匹配
MultiMatchQueryBuilder multiMatchQueryBuilder =
    QueryBuilders.multiMatchQuery("上海", fields);

// 设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
for (String field : fields) {
    highlightBuilder.field(field);
}

highlightBuilder.requireFieldMatch(false); // 如果要多个字段高亮,这项要为false
highlightBuilder.preTags("<span style='color:red'>"); // 高亮设置
highlightBuilder.postTags("</span>");
highlightBuilder.numOfFragments(0); // 返回结果最多可以包含几段不连续的文字。默认是5, 这里不分段显示

// 分页
Pageable pageable = PageRequest.of(0, 10,
                                   Sort.Direction.ASC, "_id");// 设置分页参数

// 封装查询
builder.withQuery(multiMatchQueryBuilder)
    .withPageable(pageable) // 封装分页
    .withHighlightBuilder(highlightBuilder);
NativeSearchQuery searchQuery = builder.build();

// 发起检索请求
// 参数:查询条件对象,返回泛型,索引名称
SearchHits<Strategy> searchHits = restTemplate.search(searchQuery, Strategy.class, IndexCoordinates.of("strategy_es"));

// ... 输出高亮内容