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. 坑与最佳实践
- 高亮只对"参与查询的字段"生效 :
highlight.fields里的字段要和 query 命中的字段对应,否则片段为空。 filter上下文不高亮 :filter 不算分、不记录命中位置;要高亮就把该条件放must/match。keyword高亮意义有限 :整串精确匹配没有"词"的概念,高亮通常给text字段用。number_of_fragments: 0返回整段并高亮(适合短标题);大文本用切片避免返回过长。- 性能 :高亮要重新分析字段内容,字段很大时开销明显;必要时用
fvh(fast vector highlighter)+term_vector加速。
下一篇
28-collapse-字段折叠去重.md(列表去重),随后 29-suggester-自动补全.md。