排序能看了,体验还差三口气
爆款排序理顺之后,运营对搜索页的挑剔升级了:结果里关键词不高亮,用户得自己找哪里相关;把「拿铁」打成「拿贴」,零结果直接劝退;只搜商品名,藏在详情里的关键词永远搜不到。这三口气分别对应三个查询特性:highlight、suggest、multi_match。
highlight:高亮的本质是再切一遍
高亮不是把原文里的关键词做个字符串替换,而是拿查询词条对原文重新执行一次分词,命中的词条包上标记标签再还给你:
GET /product/_search
{
"query": { "match": { "name": "燕麦拿铁" } },
"highlight": {
"pre_tags": [ "<em class="hl">" ],
"post_tags": [ "</em>" ],
"fields": {
"name": { "number_of_fragments": 0 },
"description": { "fragment_size": 100, "number_of_fragments": 3 }
}
}
}两个字段的配置意图不同:商品名短,number_of_fragments 设 0 返回整句高亮;详情长,按 fragment_size 切出三个最相关的片段返回,避免把两千字详情整个塞给前端。默认标记是 em 标签,用 pre_tags 和 post_tags 换成自己的,前端好识别也防样式串味。两个提醒:高亮字段必须建了倒排(text),keyword 字段没有分词概念高亮意义不大;高亮不参与排序,是纯展示层动作,别指望它影响 _score。
multi_match:一搜多字段,还带加权
只搜 name 太浪费。multi_match 一个查询打多个字段,还支持按字段重要性加权:
{
"multi_match": {
"query": "燕麦拿铁",
"fields": [ "name^3", "tags^2", "description" ],
"type": "best_fields"
}
}name^3 表示标题命中的分数乘三——用户在标题里看到关键词,相关性天然高于在详情页末尾看到。type 有讲究:best_fields 是默认,取得分最高的那个字段;cross_fields 把多个字段当成一个大字段合并算分,适合「品牌在 A 字段、品名在 B 字段」的拆开结构;phrase 要求短语命中。老王最后用 best_fields 加标题权重,运营反馈「搜详情里提到的型号也能出来了」。
suggest:did you mean 的正确打开方式
用户打错字不该是终点。suggest 家族按场景分三种:
| 类型 | 干什么 | 适用场景 |
|---|---|---|
| term suggester | 按编辑距离替换单个词条 | 「拿贴」变「拿铁」 |
| phrase suggester | 整句纠错,考虑词与词搭配 | 整句打错的搜索框输入 |
| completion suggester | 前缀自动补全 | 搜索框边打边出联想词 |
completion suggester 是为补全专门设计的数据结构(基于 FST,第 2 篇见过它的名字),字段类型要配 completion,性能极快,但只认前缀、不做分词。产品化落地有个成熟套路:正常搜索有结果就正常返回;结果为空或过少,才发起一次纠错查询,把 did you mean 挂在结果页顶部,点击纠错词重新搜——而不是每次都纠错,那会显得搜索「自作聪明」。老王把这条路接上之后,「拿贴」的零结果页面第一次变成了「您是不是想找:拿铁」,客诉当场少了一半。
小结
体验三件套一句话:highlight 是展示层再分词,multi_match 是一查多字段加权,suggest 是从纠错到补全的兜底。三件事都不难,难在接入产品的姿势:高亮别动排序,纠错别每次都上,补全别拿普通查询硬扛。
搜索页齐活了,老王接着撞上经典性能墙:运营要翻到第 100 页看数据,from 加 size 翻深了直接报错。下一篇讲深分页:from 加 size 为什么越翻越贵,search_after 与 scroll 的救场姿势。
评论 (0)