第三阶段 26 · highlight 高亮(返回命中片段)

26 · highlight 高亮(返回命中片段)

阶段:第三阶段补充 / 查询结果增强

ES:highlight | PostgreSQL:ts_headline()(全文检索高亮)


1. 概念

搜索结果里把命中的关键词 用标签标出来(默认 <em>...</em>),

前端渲染成高亮,就是 highlight。它作用在参与打分的查询 命中的字段上,

返回的是「带标记的片段(fragment)」,而不是整段原文。


2. PostgreSQL 对照

sql 复制代码
-- PG 全文检索的高亮:ts_headline
SELECT ts_headline('english', description,
                   to_tsquery('english', 'carbon'))
FROM salesdata
WHERE to_tsvector('english', description) @@ to_tsquery('english', 'carbon');

ts_headline 就是 PG 版的 highlight:把匹配词包成 <b>...</b> 并截取片段。


3. ES DSL

基本高亮

复制代码
GET salesdata_idx/_search
{
  "query": { "match": { "description": "carbon laptop" } },
  "highlight": {
    "fields": { "description": {} }
  }
}

返回里每个 hit 多一个 highlight 段:

json 复制代码
"highlight": {
  "description": [ "super light <em>carbon</em> <em>laptop</em>" ]
}

自定义标签 + 片段控制

复制代码
GET salesdata_idx/_search
{
  "query": { "match": { "description": "carbon" } },
  "highlight": {
    "pre_tags":  ["<mark>"],
    "post_tags": ["</mark>"],
    "fragment_size": 120,          // 每个片段长度
    "number_of_fragments": 3,      // 最多返回几个片段
    "fields": { "description": {} }
  }
}

多字段 + 整字段返回(不截断)

复制代码
"highlight": {
  "fields": {
    "title":       { "number_of_fragments": 0 },   // 0 = 返回整段并高亮,不切片
    "description": { "fragment_size": 100 }
  }
}

4. Spring Boot 实现

java 复制代码
@Component
public class Doc26Highlight {

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /** 返回每条命中的高亮片段:Map<文档source, 高亮片段列表> */
    public List<HighlightHit> search(String indexName, String field, String keyword)
            throws IOException {
        SearchResponse<Map> resp = elasticsearchClient.search(s -> s
                .index(indexName)
                .query(q -> q.match(m -> m.field(field).query(keyword)))
                .highlight(h -> h
                        .preTags("<mark>")
                        .postTags("</mark>")
                        .fields(field, hf -> hf.fragmentSize(120).numberOfFragments(3))),
                Map.class);

        List<HighlightHit> result = new ArrayList<>();
        for (Hit<Map> hit : resp.hits().hits()) {
            // 命中的高亮片段在 hit.highlight() 里,key 是字段名
            List<String> fragments = hit.highlight().getOrDefault(field, List.of());
            result.add(new HighlightHit(hit.source(), fragments));
        }
        return result;
    }

    public record HighlightHit(Map<String, Object> source, List<String> fragments) {}
}

高亮片段不在 _source 里,而在 hit.highlight()Map<String, List<String>>,key=字段名)。

import:co.elastic.clients.elasticsearch.core.search.Hit


5. 坑与最佳实践

  1. 高亮只对"参与查询的字段"生效highlight.fields 里的字段要和 query 命中的字段对应,否则片段为空。
  2. filter 上下文不高亮 :filter 不算分、不记录命中位置;要高亮就把该条件放 must/match
  3. keyword 高亮意义有限 :整串精确匹配没有"词"的概念,高亮通常给 text 字段用。
  4. number_of_fragments: 0 返回整段并高亮(适合短标题);大文本用切片避免返回过长。
  5. 性能 :高亮要重新分析字段内容,字段很大时开销明显;必要时用 fvh(fast vector highlighter)+ term_vector 加速。

下一篇

28-collapse-字段折叠去重.md(列表去重),随后 29-suggester-自动补全.md


← 上一篇:25-nested-嵌套对象与查询总览下一篇:27-pipeline-管道聚合 →

相关推荐
lpfasd1231 小时前
编程语言榜单变迁分析
开发语言·后端·scala
Shawn_Shawn1 小时前
google ADK VS Langchain-ai
后端·llm·agent
名字还没想好☜1 小时前
Go 用 bufio.Scanner 读大文件踩坑:默认 64KB 行上限、Buffer 扩容与按 Token 切分
开发语言·后端·golang·go
用户6083089290472 小时前
SpringBoot自定义注解校验
后端
董员外2 小时前
RAG 系统进化论(六):GraphRAG(基于知识图谱的 RAG),从相似文本走向实体关系
人工智能·后端·设计模式
Conan在掘金2 小时前
ArkTS 进阶之道(31):状态联动深水区收官边界——为啥四级状态容器 + V1/V2 双轨是状态联动深水区收官根因
后端
Conan在掘金2 小时前
鸿蒙 7.0 空间美学开篇:沉浸式毛玻璃 + 底部 Sheet 面板——一行 backgroundBlurStyle 玩出物理材质感
后端
Java编程爱好者2 小时前
R2DBC vs JDBC:Spring Boot 响应式项目该怎么选
后端
AskHarries2 小时前
Sitemap 怎么自动生成
后端