真实尝试原地修改已存在字段的类型被拒绝后,用_reindex把数据搬到Mapping正确的新索引,顺带踩中一个reindex完立刻查count却是0的真实的坑。
代码PUT /reindex_src {"mappings": {"properties": {"zipcode": {"type": "text"}}}} POST .../_doc/1 {"zipcode": "02139"} POST .../_doc/2 {"zipcode": "10001"} PUT /reindex_src/_mapping {"properties":{"zipcode":{"type":"keyword"}}} # 原地改类型 PUT /reindex_dst {"mappings": {"properties": {"zipcode": {"type": "keyword"}}}} POST /_reindex {"source": {"index": "reindex_src"}, "dest": {"index": "reindex_dst"}}
PUT /reindex_src/_mapping {"properties":{"zipcode":{"type":"keyword"}}}
→ 400 illegal_argument_exception:
"mapper [zipcode] cannot be changed from type [text] to [keyword]"这条真实报错呼应了本系列前面验证过的"字段类型一旦建立后为什么不能随便修改"——text和keyword在Lucene底层真实对应完全不同的索引结构(text真实存的是分词后的倒排索引,keyword存的是整个值不分词的倒排索引),已经写入的文档,它们的倒排索引真实是按照创建时的类型建好的,没有办法原地"重新按另一种方式建索引"——哪怕索引里当前一条数据都没有,ES也真实不允许这种类型变更,必须创建一个新的索引。
POST /_reindex {"source":{"index":"reindex_src"},"dest":{"index":"reindex_dst"}}
→ {"total": 2, "created": 2, "failures": []} ← 真实2条数据都成功搬过去了
GET /reindex_dst/_count (紧接着立刻查)
→ {"count": 0} ← 真实是0!明明reindex响应说created:2了
手动refresh之后再查:
GET /reindex_dst/_count → {"count": 2} ← 这次真实对了GET /reindex_dst/_search {"query":{"term":{"zipcode":"02139"}}}
→ hits.total.value = 1 ← 真实命中了,这是keyword类型精确匹配的真实效果这条真实结果证明了整个reindex流程确实达到了目的:新索引里的zipcode字段真实变成了keyword类型,term这种要求精确匹配整个值的查询(本系列很早验证过text类型会先被分词器处理,term查询在text字段上经常匹配不到预期结果)在这里真实生效了。_reindex真实是ES里修正Mapping设计失误的唯一正规途径——不管是想改字段类型、调整分词器、还是重新规划分片数,只要涉及到已经固定下来的索引级结构,真实的操作模式永远是"建一个配置正确的新索引,把旧数据reindex过去,最后把应用层的alias指向新索引",而不是尝试在原地做手术。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
_reindex API 与字段类型不可变性限制