bigywater.tech
Elasticsearch · 46

Search As You Type

真实用search_as_you_type字段类型观察其自动生成的_2gram、_3gram、_index_prefix隐藏子字段,并用bool_prefix查询验证随用户输入字符增多,相关性判断如何从无法区分逐步精确到多词级别的差异。

title._2gram/_3gram
真实自动生成相邻词组的shingle子字段
bool_prefix搜多词前缀
真实让多词命中的文档得分高出十倍以上
打字早期阶段'qui'
真实两篇候选文档得分完全相同

做的事:真实用search_as_you_type类型字段索引文档,看它背后真实生成了什么样的隐藏子字段

代码PUT /sayt_test {"mappings":{"properties":{"title":{"type":"search_as_you_type"}}}} 真实索引2篇文档: "quick brown fox" / "quick blue jay" POST /sayt_test/_analyze {"field":"title._2gram","text":"quick brown fox"} POST /sayt_test/_analyze {"field":"title._3gram","text":"quick brown fox"}

验证:_2gram真实产出相邻两词组,_3gram真实产出相邻三词组,而不是字符级的ngram

实测 · 本机 Elasticsearch 8.15.0 执行输出
title._2gram(相邻2个词的组合):
→ ['quick brown', 'brown fox']

title._3gram(相邻3个词的组合):
→ ['quick brown fox']

这个结果真实揭示了search_as_you_type这个字段类型背后的真实机制:它真实不是单纯对单个词做前缀展开(那是edge_ngram的做法,本系列第28篇验证过),而是基于shingle(词组滑窗)的思路,真实把相邻的2个、3个连续词组合成独立的token存起来。这意味着用户搜索"quick brown"这种跨越2个完整词的查询词,能真实直接在title._2gram这个子字段里找到完全匹配的token,而不需要依赖短语查询的位置约束机制。

真实用multi_match的bool_prefix类型,跨title/_2gram/_3gram三个子字段搜用户正在输入的"quick br"

代码POST /sayt_test/_search {"query":{"multi_match":{ "query": "quick br", "type": "bool_prefix", "fields": ["title","title._2gram","title._3gram"] }}}

验证:命中了quick和brown两个完整词的文档真实得分远高于只命中quick一个词的文档

实测 · 本机执行输出
搜"quick br"(用户输入到一半,br是brown的前缀):
→ "quick brown fox" score=2.1823215   ← 真实quick和brown两个词都命中了
→ "quick blue jay" score=0.18232156   ← 真实只有quick这一个词命中,分数低了一个数量级
这组真实得分差异直接体现了search_as_you_type相对简单前缀匹配(比如本系列第38篇验证过的match_phrase_prefix)的真实优势:bool_prefix类型真实会把查询词"quick br"拆开,前面完整的词"quick"去跟各个子字段做常规匹配,最后一个不完整的词"br"真实被当成前缀去匹配。因为title._2gram这个子字段里真实存着"quick brown"这个完整的词对token,"quick br"这个查询真实能在这个token上同时命中"quick"和以"br"开头的"brown",这种"多个连续词都对上了"的匹配真实会被判定为远比"只有第一个词对上"更相关,所以两篇文档的得分真实相差了超过10倍。

真实验证用户只打了3个字母"qui"(还没打完第一个词)时的表现

代码POST /sayt_test/_search {"query":{"multi_match":{"query":"qui","type":"bool_prefix","fields":["title","title._2gram","title._3gram"]}}}

验证:两篇文档真实都以相同的1.0分命中,因为都是以quick开头

实测 · 本机执行输出
搜"qui"(用户刚打了3个字母):
→ "quick brown fox" score=1.0
→ "quick blue jay" score=1.0
  ← 真实两篇得分完全一样,因为此时唯一能判断的信息就是都以qui开头

这组结果真实验证了search_as_you_type在"用户刚开始打字"这个最早期阶段的真实行为:这时候真实没有任何多词信息可以用来区分相关性高低,title._index_prefix这个专门处理单词前缀匹配的子字段真实接管了这次查询,两篇文档因为都以"qui"开头而真实得到相同的得分。随着用户继续输入更多字符(从"qui"到"quick"再到"quick br"),真实能利用的匹配信息越来越丰富,_2gram、_3gram这些多词子字段才真实开始发挥区分作用——这正是search_as_you_type这个类型设计出来专门服务"搜索框随打字逐步精确"这种交互场景的真实原因。

分步演示:search_as_you_type从隐藏子字段结构到随输入逐步精确的真实打分过程

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

search_as_you_type的shingle子字段真实让搜索框随打字逐步精确
这一步新验证的子字段匹配

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 search_as_you_type 字段类型与 bool_prefix 查询
    京ICP备2026067502号-1京公网安备11010502063487号