Elasticsearch 8.13.4 关键词如何高亮显示

在构建搜索功能时,"高亮显示"绝对是提升用户体验的"神来之笔"。当用户在海量数据中搜索关键词时,如果结果列表里密密麻麻全是黑字,用户需要逐行阅读去寻找匹配项,这简直是一场灾难。

在 Elasticsearch 8.13.4 中,高亮(Highlighting)机制已经非常成熟,但也隐藏着许多容易被忽视的细节和性能陷阱。本文将带你从入门到精通,玩转 ES 8.13.4 的高亮功能。


一、 核心原理:不仅仅是"标红"

首先要明白,ES 的高亮不是在返回结果后由应用层做的字符串替换,而是在倒排索引检索阶段就已经介入的分析过程。

当你的查询命中文档后,ES 会从原始存储的 _source 或专门的高亮存储中提取文本片段,根据你的查询词进行标记。

基础配置(Hello World 版)

json 复制代码
GET /my-index-8.13/_search
{
  "query": {
    "match": {
      "content": "elasticsearch 高亮"
    }
  },
  "highlight": {
    "fields": {
      "content": {} 
    }
  }
}

默认返回结果

ES 会默认使用 <em></em> 包裹关键词,并在结果中增加一个 highlight 字段:

json 复制代码
"hits": {
  "hits": [
    {
      "_source": { "content": "学习 Elasticsearch 的高亮显示功能..." },
      "highlight": {
        "content": [
          "学习 <em>Elasticsearch</em> 的<em>高亮</em>显示功能..."
        ]
      }
    }
  ]
}

二、 进阶玩法:定制你的"荧光笔"

默认的 <em> 标签可能不符合你的前端 UI 框架(比如 Vue 或 React 可能需要 <span class="red">)。在 8.13.4 中,我们可以深度定制。

1. 自定义标签与样式

如果你不喜欢斜体,想要加粗并带背景色:

json 复制代码
"highlight": {
  "fields": {
    "content": {
      "pre_tags": ["<mark style='background:#ffeb3b; font-weight:bold;'>"],
      "post_tags": ["</mark>"],
      "fragment_size": 100 
    }
  }
}
  • pre_tags / post_tags:支持数组,可定义多组标签实现复杂逻辑(如不同关键词不同颜色,需配合 Encoder,较复杂)。
  • fragment_size:片段长度。默认 100 个字符。太短看不全上下文,太长影响性能。
2. 碎片数量控制 (number_of_fragments)

默认情况下,ES 会返回 5 个高亮片段。如果你只需要最精彩的那一段,或者需要全部展示:

json 复制代码
"content": {
  "number_of_fragments": 1,  // 只返回最相关的 1 个片段
  "fragment_size": 200
}
3. 边界扫描器 (boundary_scanner) ------ 8.x 推荐

这是 ES 8.x 非常实用的功能。默认按字符截断(chars)可能会切断 HTML 标签或把单词切成两半。使用 sentence 可以按句子截断,体验更好:

json 复制代码
"content": {
  "boundary_scanner": "sentence", // 按句子边界截断
  "boundary_max_scan": 10,        // 最多向前扫描 10 个字符找边界
  "fragment_offset": 0            // 从匹配词开头处开始截取
}

三、 避坑指南:性能与安全的博弈

高亮虽好,但它是内存和 CPU 杀手。因为高亮需要访问原始文本并进行分析,在大文本字段上开启高亮极易引发 OOM。

1. 必须开启 require_field_match

默认情况下,只要 Query 命中了文档,highlight 会对你指定的所有字段(如 content, title, summary)都做高亮处理,哪怕 Query 只查了 content
这是巨大的浪费!

json 复制代码
"highlight": {
  "require_field_match": true, // 强烈建议开启
  "fields": {
    "content": {}, // 只有 query 涉及 content 时才高亮它
    "title": {}
  }
}
2. 警惕 max_analyzed_offset

在 8.13.4 中,为了防止深度分页时的内存爆炸,默认限制了分析的字符数(通常是 1MB)。如果你的文本非常长(如长文章、日志),且关键词在很后面,可能会因为超过限制而导致高亮失败(返回空)。
对策 :如果确实需要对超长文本高亮,需在索引 Mapping 中调整 index.highlight.max_analyzed_offset(但这会增加内存风险)。

3. 统一高亮器 (Unified Highlighter)

从 7.0 开始,ES 废弃了旧的 fast-vector-highlighter,统一使用 Unified Highlighter 。它在索引时不需要额外存储 term_vectors(除非你用 plain 模式),直接利用 _source 或 doc values。

  • 优势:索引体积更小。
  • 注意 :如果你没存 _source 且没开 store: true,高亮将无法工作,因为它没原始文本可分析!
4. XSS 安全漏洞

如果你允许用户自定义搜索词,且直接将高亮结果渲染到页面,务必小心 XSS 攻击。

虽然 ES 默认会转义 HTML,但如果你为了显示富文本而设置了 "escape_html": false,攻击者输入 <script>alert(1)</script> 作为搜索词,高亮结果就会执行脚本。
原则:除非万不得已,不要关闭 HTML 转义。


四、 实战案例:电商商品搜索高亮优化

假设我们有一个商品索引,需要对"标题"和"描述"进行高亮。

需求

  1. 标题高亮加粗。
  2. 描述只取一段,且按句子截断,防止截断到标签中间。
  3. 只有查询命中的字段才高亮。

请求 DSL

json 复制代码
GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "智能降噪耳机",
      "fields": ["title^3", "description"]
    }
  },
  "highlight": {
    "require_field_match": true,
    "fields": {
      "title": {
        "pre_tags": ["<b>"],
        "post_tags": ["</b>"],
        "number_of_fragments": 1
      },
      "description": {
        "type": "unified",
        "boundary_scanner": "sentence",
        "fragment_size": 150,
        "number_of_fragments": 1
      }
    }
  }
}

前端处理逻辑

拿到 highlight.title[0]highlight.description[0] 后,直接使用 v-html (Vue) 或 dangerouslySetInnerHTML (React) 渲染即可。如果高亮字段不存在,则回退显示 _source 中的原始内容(不带高亮)。


总结

Elasticsearch 8.13.4 的高亮功能强大而精细。要用好它,请记住这三句口诀:

  1. 按需高亮 :开启 require_field_match,别做无用功。
  2. 保护体验 :长文本用 boundary_scanner: sentence,别把单词切两半。
  3. 守住底线 :除非你是内部系统且完全信任输入,否则别关 escape_html

掌握了这些,你的搜索结果就能像黑夜中的萤火虫一样,精准地把用户想要的信息"亮"出来!

相关推荐
字节跳动数据平台1 小时前
代码量减少 70%、GPU 利用率达 95%:火山引擎多模态数据湖如何释放模思智能的算法生产力
大数据
得物技术2 小时前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
武子康4 小时前
大数据-238 离线数仓 - 广告业务 Hive分析实战:ADS 点击率、购买率与 Top100 排名避坑
大数据·后端·apache hive
武子康1 天前
大数据-237 离线数仓 - Hive 广告业务实战:ODS→DWD 事件解析、广告明细与转化分析落地
大数据·后端·apache hive
大大大大晴天1 天前
Flink生产问题排障-Kryo serializer scala extensions are not available
大数据·flink
Elasticsearch2 天前
如何使用 Agent Builder 排查 Kubernetes Pod 重启和 OOMKilled 事件
elasticsearch
Elasticsearch3 天前
通用表达式语言 ( CEL ): CEL 输入如何改进 Elastic Agent 集成中的数据收集
elasticsearch
武子康3 天前
大数据-236 离线数仓 - 会员指标验证、DataX 导出与广告业务 ODS/DWD/ADS 全流程
大数据·后端·apache hive
武子康4 天前
大数据-235 离线数仓 - 实战:Flume+HDFS+Hive 搭建 ODS/DWD/DWS/ADS 会员分析链路
大数据·后端·apache hive
DianSan_ERP5 天前
电商API接口全链路监控:构建坚不可摧的线上运维防线
大数据·运维·网络·人工智能·git·servlet