连载中 5/20

mapping:字段类型建错,神仙也救不回来

2026-08-12 · 1929 阅读 · 0 评论 · 0 赞

一个排序把老王打回原形

分词搞定,老王美滋滋上线了搜索。第二天运营又找上门:按价格从低到高排序,第一位是 10 元的糖浆,第二位才是 9 元的滤杯——10 怎么会比 9 小?再一查,销量聚合直接报错:Fielddata is disabled on text fields by default。老王低头一看 mapping,price 字段被建成了 text。字段类型建错,排序、聚合、精度全废,而且ES 不允许原地改类型。这一篇就把 mapping 这块地基讲透。

text 与 keyword:一对分身

字符串字段在 ES 里有两种类型,分工完全不同:

text 会分词。「燕麦拿铁(大杯)」被切成「燕麦」「拿铁」「大杯」进倒排,用于全文搜索,不用于排序和聚合。

keyword 不分词。整串原样存进正排,用于精确匹配(term)、排序、聚合。状态码、标签、品牌名这类枚举值,都该是 keyword。

一个字段能不能既要又要?可以,用多字段(fields)配置,一个 text 主字段加一个 keyword 子字段:

"name": {
  "type": "text",
  "analyzer": "ik_max_word",
  "search_analyzer": "ik_smart",
  "fields": {
    "keyword": { "type": "keyword", "ignore_above": 256 }
  }
}

搜「拿铁」走 name,按品牌精确聚合走 name.keyword。ignore_above 超长的串不进子字段,防止正排爆内存。price 这种数值字段则老老实实建 integer 或 long,跟分词毫无关系。

动态 mapping:自作主张的实习生

老王的 price 为什么成了 text?因为他建索引时根本没写 mapping,是 ES 的动态 mapping 自作主张猜的类型。这位实习生的习惯是:

JSON 里的样子实习生猜的类型备注
字符串text + keyword 子字段老王 price 是字符串数字,被猜成 text
像日期的字符串datedate_detection 开着就会猜
数字long 或 double纯数字字符串默认不会猜成数字
true / falseboolean这个猜得最靠谱

实习生靠得住,母猪能上树。生产环境的正确姿势是显式建 mapping,并且关掉自作主张:dynamic 设 false,新字段不进索引(文档能存但搜不到);设 strict 更狠,新字段直接写入报错,逼着走变更流程。宁可写入报错让人看见,也别让字段悄悄丢在索引外。

常用类型速查与两个经典坑

日常够用的类型一张表列完:

类型用在哪常见坑
text全文搜索拿去做排序聚合必报错
keyword精确匹配、排序、聚合不分词,整串才匹配上
integer / long / double价格、数量、分数金额别用 double,精度问题换 long 存分
date下单时间、发布时间格式与时区要写进 format
boolean上下架状态几乎没坑
object扁平 JSON 对象数组对象会被拍平,见下
nested数组内保持独立每个嵌套对象一个隐藏文档,成本更高

object 的坑值得单独演一遍。老王给商品加了评论人列表,两个对象:

"comments": [
  { "first": "张", "last": "王" },
  { "first": "李", "last": "四" }
]

object 类型会把它拍平成 first: 张、李 和 last: 王、四 两组并列数组。这时查「first 是张 且 last 是四」,ES 一看两个字段里都有值,命中——可张王和李四压根不是一个人。要保持对象内部配对关系,字段类型必须是 nested:每个嵌套对象存成独立的隐藏文档,查询时用 nested 查询单独匹配。代价是写入和存储成本更高,所以只给真正需要「配对查询」的字段用。

类型建错了怎么补救

坏消息:字段的类型建了就不能改,改了会报 illegal_argument_exception。好消息:补救路线第 4 篇已经铺好了——

第一步:按正确类型新建 product_v2 的 mapping
第二步:POST _reindex 搬数据
第三步:别名 product 从 v1 切到 v2

数据量大时 _reindex 慢,记得带上 slices 参数并行切分,或者用外部脚本双写迁移。预防永远比补救便宜:mapping 写进代码仓库走评审,上线前用几条真实文档试写试查,别把类型判断交给动态 mapping。

小结

mapping 一句话:类型定生死,text 管搜索,keyword 管排序聚合,动态 mapping 不可信,nested 救数组配对。建错类型的账,迟早连本带利还——reindex 就是那条还债的路。

类型理顺了,老王又撞上新问题:文档刚写进去,搜索却查不到,过一秒才出来。这不是 bug,是 ES 的近实时设计。下一篇讲 refresh 与段:为什么搜不到刚写进去的文档,以及 ES 怎么做到写入不丢

503

10 年全栈工程师 · 503咖啡馆主理人

#Elasticsearch#mapping#text#keyword#nested

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞