Skip to content
鼓励作者:欢迎打赏犒劳

ES的索引

es的字段映射mappings分为三个类型。分别是 true(默认),false,strict

如何查看mapping类型

shell
GET 库名/_mapping

结果如下,如果没看到dynamic字段,则说明是true

json
{
  "test_index" : {
    "mappings" : {
      "dynamic" : "strict"
    }
  }
}

设置mapping

修改 dynamic 不会影响已存在的文档,只影响后续写入的新文档:

  • 改成 true:新字段会被自动添加。
  • 改成 false:新字段会被忽略(不索引)。
  • 改成 strict:新字段会导致写入失败。
shell
PUT test_index
{
  "mappings": {
    "dynamic" : "strict"
  }
}

dynamic 设置可以作用于整个索引,也可以作用于某个 object 内部,修改时路径要对应。例如:

shell
PUT /test_index/_mapping
{
  "properties": {
    "user": {
      "type": "object",
      "dynamic": "false"
    }
  }
}

dynamic=true的使用

dynamic: true(默认行为)时,Elasticsearch 如何自动推断你提供的各种字段类型,并生成 mapping。


✅ 前提:创建索引(使用默认 dynamic: true

json
PUT /test_index
{
  "mappings": {
    "dynamic": "true"   // 可省略,因为是默认值
  }
}

然后插入你提供的文档:

json
PUT /test_index/_doc/1
{
  "string_field": "imooc",
  "int_field": 100,
  "float_field": 3.14,
  "bool_field": true,
  "date_field": "2022/03/16",
  "obj_field": { "key1": "value1", "key2": 100 },
  "array_field1": [1.0, 3.14],
  "array_field2": [100, 200],
  "array_field3": ["100", "200"],
  "array_field4": ["2022/03/16"],
  "null_field": null
}

🔍 插入后,Elasticsearch 自动生成的 mapping 是怎样的?

执行:

json
GET /test_index/_mapping

你会看到类似如下结果(关键部分):

json
{
  "test_index": {
    "mappings": {
      "dynamic": "true",
      "properties": {
        "string_field": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
        "int_field": { "type": "long" },
        "float_field": { "type": "float" },
        "bool_field": { "type": "boolean" },
        "date_field": { "type": "date", "format": "strict_date_optional_time||epoch_millis" },
        "obj_field": {
          "properties": {
            "key1": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
            "key2": { "type": "long" }
          }
        },
        "array_field1": { "type": "float" },
        "array_field2": { "type": "long" },
        "array_field3": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
        "array_field4": { "type": "date", "format": "strict_date_optional_time||epoch_millis" },
        // 注意:null_field 不会出现!
      }
    }
  }
}

📌 逐字段解析 Elasticsearch 的自动类型推断规则

字段推断类型说明
string_field"imooc"text + keyword字符串默认生成 multi-field
text 用于全文搜索
.keyword 用于精确匹配、聚合、排序(长度 ≤256)
int_field100long整数 → long(ES 没有 int,最小整数类型是 long
float_field3.14float小数 → float(不是 double,除非值很大或明确指定)
bool_fieldtrueboolean布尔值 → boolean
date_field"2022/03/16"date字符串若能被默认日期格式解析,则视为 date
默认格式包括:yyyy/MM/ddISO8601
obj_field{...}object对象 → 自动递归处理内部字段
array_field1[1.0, 3.14]float数组类型由第一个非-null 元素决定
array_field2[100, 200]long同上,整数数组 → long
array_field3["100", "200"]text + keyword字符串数组 → 同普通字符串
array_field4["2022/03/16"]date字符串可解析为日期 → date
null_fieldnull不会出现在 mapping 中null不会触发动态映射,字段不会被创建

⚠️ 关键注意事项(覆盖“面”)

1. 数组类型由第一个元素决定

  • 如果你先插入 [1, 2, 3] → 映射为 long
  • 后续再插入 [1.5]会失败! 因为 1.5 是 float,不能存入 long 字段
  • ❗ 动态映射一旦确定类型,就不能更改

2. 日期格式依赖默认规则

  • "2022/03/16" 能被识别是因为 ES 默认支持 yyyy/MM/dd
  • 但如果你写 "16-Mar-2022",可能无法识别 → 会被当成普通字符串(text
  • 可通过 dynamic_date_formats 自定义(需在索引 settings 中配置)

3. 字符串总是生成 multi-field(text + keyword)

  • 这是 7.x+ 的默认行为(得益于 default_fieldindex.mapping.coerce 等设置)
  • 可通过模板关闭,但默认如此

4. null 值不会创建字段

  • null_field: null → mapping 中无此字段
  • 后续如果插入 {"null_field": "hello"} → 此时才会创建为 text

5. 对象(object)会递归应用 dynamic 规则

  • obj_field.key1obj_field.key2 也会被自动映射
  • 如果 dynamic: true 在根级别,对象内部也继承该行为(除非单独设置)

6. 动态映射只发生在第一次遇到字段时

  • 第一个文档决定了所有字段的类型
  • 后续文档必须兼容,否则写入失败

🧪 验证:尝试插入不兼容的数据

json
PUT /test_index/_doc/2
{
  "int_field": "this is not a number"   // 字符串,但 int_field 已是 long 类型
}

🔴 结果:失败!

json
{
  "error": {
    "type": "mapper_parsing_exception",
    "reason": "failed to parse field [int_field] of type [long]"
  }
}

✅ 总结:dynamic: true 的特点

特性说明
✅ 自动创建字段第一次出现的新字段会自动加入 mapping
✅ 类型自动推断根据 JSON 值类型智能判断(string → text/keyword, number → long/float 等)
✅ 支持嵌套对象和数组递归处理,数组类型由首元素决定
⚠️ 类型一旦确定不可变后续数据必须兼容,否则写入失败
⚠️ 日期依赖默认格式非标准格式可能被误判为字符串
❌ null 不触发映射null 字段不会出现在 mapping 中

💡 最佳实践建议

  • 开发/测试环境可用 dynamic: true 快速上手
  • 生产环境强烈建议使用 dynamic: strict + 显式 mapping,避免类型混乱和 mapping explosion
  • 或使用 Index Template 预定义规则,兼顾灵活性与可控性

dynamic=strict的使用

json
PUT /test_index
{
  "mappings": {
    "dynamic": "strict"
  }
}

就意味着:

该索引不允许自动创建新字段。任何写入的文档中,只要包含 mapping 中未预先定义的字段,Elasticsearch 就会拒绝该写入操作,并返回错误。


🔒 “strict” 模式的核心含义

行为说明
✅ 允许写入文档中的所有字段都必须已在 properties 中明确定义
❌ 拒绝写入文档中包含任意一个未定义的新字段(如 new_field: "value"
💡 目的强制数据结构规范,防止“脏数据”或意外字段污染索引

📌 举个例子

1. 创建 strict 索引(无任何字段定义)

json
PUT /test_index
{
  "mappings": {
    "dynamic": "strict"
  }
}

2. 尝试写入一个带字段的文档:

json
POST /test_index/_doc
{
  "name": "Alice",
  "age": 30
}

🔴 结果:失败!报错

json
{
  "error": {
    "type": "strict_dynamic_mapping_exception",
    "reason": "mapping set to strict, dynamic introduction of [name] within [_doc] is not allowed"
  },
  "status": 400
}

因为 nameage 都没在 mapping 的 properties 中定义!


✅ 正确做法:提前定义好字段(类似“建表”)

json
PUT /test_index
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "name": { "type": "text" },
      "age":  { "type": "integer" },
      "email": { "type": "keyword" }
    }
  }
}

现在再写入:

json
POST /test_index/_doc
{
  "name": "Bob",
  "age": 25,
  "email": "bob@example.com"
}

✅ 成功!

但如果多加一个字段:

json
{
  "name": "Bob",
  "age": 25,
  "phone": "123456789"   // ← 未定义!
}

🔴 依然会失败!


🧠 类比关系型数据库

你可以把 dynamic: strict 理解为:

“这个 Elasticsearch 索引就像一张严格定义的 SQL 表,不能随意 INSERT 不存在的列。”

  • 必须先 ALTER TABLE ADD COLUMN ...(相当于 PUT _mapping 添加字段)
  • 否则插入带新字段的数据会报错

🛠 后续还能加字段吗?

可以! 即使设置了 strict,你仍然可以通过 API 手动添加新字段

json
PUT /test_index/_mapping
{
  "properties": {
    "phone": { "type": "keyword" }
  }
}

之后就可以写入包含 phone 的文档了。

⚠️ 但注意:不能修改已有字段的类型(这是 ES 的通用限制,与 dynamic 无关)。也不能删原来的字段!!!!


✅ 总结

问题回答
设置 dynamic: strict 后能否随意新增字段?❌ 不能
是否需要提前定义好所有字段?✅ 是的,必须在 mapping 的 properties 中声明
能否后续添加新字段?✅ 可以,通过 _mapping API 手动添加
适合什么场景?数据结构固定、要求高一致性、避免意外字段(如日志规范、业务核心数据)

dynamic=false的使用


dynamic: false 的核心行为

新字段不会被索引(不会出现在 mapping 中),也不会报错,但原始数据仍会保存在 _source 中。

也就是说:

  • ✅ 文档能成功写入
  • ❌ 新字段无法被搜索、聚合、排序
  • ✅ 原始 JSON 内容(包括新字段)仍可通过 _source 查看

🧪 举个完整例子

1. 创建索引,设置 dynamic: false

json
PUT /test_index
{
  "mappings": {
    "dynamic": "false"
  }
}

2. 写入一个包含新字段的文档

json
POST /test_index/_doc
{
  "name": "Alice",
  "age": 30,
  "email": "alice@example.com"   // ← 这些字段都没在 mapping 中定义!
}

结果:写入成功!没有报错。

3. 查看 mapping

json
GET /test_index/_mapping

返回:

json
{
  "test_index": {
    "mappings": {
      "dynamic": "false",
      "properties": {}   // ← 注意:没有任何字段!
    }
  }
}

4. 搜索这些字段 → 失败!

json
GET /test_index/_search
{
  "query": {
    "match": {
      "name": "Alice"
    }
  }
}

🔴 结果:查不到任何结果!
因为 name 字段根本没有被索引,Elasticsearch 根本不知道它的存在。

5. 但查看 _source能看到原始数据!

json
GET /test_index/_search

返回:

json
{
  "hits": {
    "hits": [
      {
        "_id": "xxx",
        "_source": {
          "name": "Alice",
          "age": 30,
          "email": "alice@example.com"   // ← 数据还在!
        }
      }
    ]
  }
}

🔍 对比三种 dynamic 模式

行为dynamic: true(默认)dynamic: falsedynamic: strict
遇到新字段自动添加到 mapping 并索引不索引,但保存在 _source拒绝写入,报错
能否搜索新字段✅ 能❌ 不能—(根本写不进去)
文档是否写入成功
_source 是否包含新字段

💡 什么时候用 dynamic: false

适合以下场景:

  • 你只关心部分字段的搜索,其他字段仅用于“原始数据存储”(如日志中的上下文信息)
  • 防止 mapping “爆炸”(比如用户随意传字段,导致分片元数据过大)
  • 需要保留原始 JSON,但不想为所有字段建倒排索引(节省资源)

⚠️ 但注意:如果你后续想搜索某个字段,必须手动添加 mapping,且只对新文档生效(旧文档中的该字段仍不可搜)。


🛠 如何让 dynamic: false 下的字段可搜索?

必须提前或事后手动添加字段 mapping

json
PUT /test_index/_mapping
{
  "properties": {
    "name": { "type": "text" }
  }
}

⚠️ 但注意:

  • 已有文档中的 name 字段仍然不可搜索(因为写入时没被索引)
  • 只有之后新写入的文档中的 name 才能被搜索

如果想让历史数据也生效,必须 reindex


✅ 总结

  • dynamic: false ≠ 不能插入数据,而是 “静默忽略新字段的索引”
  • 数据仍在 _source 中,只是不能用于查询
  • 如果你发现“数据插进去了但搜不到”,很可能就是 dynamic: false 导致的!

你可以通过:

json
GET /test_index/_mapping

确认当前是否为 false,并检查 properties 是否为空。

生成级别的设置

下面是一个 生产级推荐的 Index Template 示例,它结合了:

  • 默认关闭动态字段(dynamic: strict)以保证结构安全
  • 但允许通过 特定命名规则(如以 dynamic_ 开头)的字段自动映射为 keyword 类型(可配置)
  • 同时预定义常用字段(如 @timestamp, message 等)
  • 支持日期检测、字符串多字段等合理默认行为

🎯 场景假设

  • 你希望大多数字段必须预先定义(strict
  • 但允许某些“扩展字段”(比如来自用户自定义标签)自动加入,且统一为 keyword 类型(适合聚合/过滤)
  • 索引名以 app- 开头(如 app-user-2025.11.09

✅ Index Template 完整示例

json
PUT _index_template/app_strict_with_dynamic_keyword
{
  "index_patterns": ["app-*"],          // 匹配索引名 所有以 app- 开头的索引,都会自动使用这个模板。
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s"
    },
    "mappings": {
      "dynamic": "strict",             // 👈 核心:默认严格模式
      "date_detection": true,          // 自动识别日期字符串(如 "2025-01-01")
      "numeric_detection": false,      // 不自动把 "123" 当数字(避免类型混乱)
      "dynamic_templates": [
        {
          "dynamic_strings_as_keywords": {
            "match_mapping_type": "string",
            "match": "dynamic_*",      // 只有字段名以 dynamic_ 开头才自动映射
            "mapping": {
              "type": "keyword",
              "ignore_above": 1024     // 超长字符串不索引
            }
          }
        },
        {
          "dynamic_objects_as_flattened": {
            "match_mapping_type": "object",
            "match": "meta_*",         // meta_ 开头的对象用 flattened 类型
            "mapping": {
              "type": "flattened"
            }
          }
        }
      ],
      "properties": {
        "@timestamp": { "type": "date" },
        "message": { "type": "text" },
        "user_id": { "type": "keyword" },
        "status": { "type": "keyword" },
        "request_time_ms": { "type": "integer" }
        // 👆 这里定义你的核心业务字段
      }
    }
  },
  "priority": 100                     // 优先级(数字越大越优先)
}

🔍 关键特性说明

功能说明
dynamic: strict所有未定义字段默认拒绝写入
dynamic_templates白名单机制:只有符合规则的字段才自动映射
match: "dynamic_*"例如 dynamic_env: "prod" 会被自动创建为 keyword
flattened 类型对于结构不确定的 JSON 对象(如 meta_user_agent),用 flattened 避免 mapping explosion
预定义核心字段@timestamp, user_id 等关键字段显式声明,确保类型正确

🧪 使用效果演示

1. 创建符合模板的索引(自动应用)

json
PUT /app-user-2025.11.09/_doc/1
{
  "user_id": "u123",
  "status": "active",
  "dynamic_region": "us-east",       // ✅ 允许!自动映射为 keyword
  "dynamic_tags": ["vip", "beta"],   // ✅ 允许!数组也会被处理
  "meta_request": {                  // ✅ 允许!作为 flattened 字段
    "ip": "1.2.3.4",
    "ua": "Chrome"
  },
  "extra_field": "xxx"               // ❌ 拒绝!因为不是 dynamic_ 或 meta_ 开头
}

第一个文档会成功(只要没有 extra_field),而包含 extra_field 的写入会报错:

json
"reason": "mapping set to strict, dynamic introduction of [extra_field] ... not allowed"

🛠 如何查看模板是否生效?

json
# 查看模板
GET _index_template/app_strict_with_dynamic_keyword

# 查看新索引的 mapping
GET /app-user-2025.11.09/_mapping

你会看到:

  • dynamic_region 出现在 properties 中,类型是 keyword
  • meta_request 类型是 flattened
  • 没有意外字段

✅ 优势总结

优势说明
结构可控核心字段必须预定义,避免脏数据
灵活扩展通过命名约定支持安全的动态字段
防止 mapping explosion限制自动映射范围 + 使用 flattened
生产友好设置分片数、副本、refresh 等参数

💡 定制建议

你可以根据实际需求调整:

  • 修改 index_patterns 匹配你的索引命名规范
  • 增加更多 dynamic_templates(如 metrics_*double
  • 调整 ignore_aboveanalyzer 等参数

如有转载或 CV 的请标注本站原文地址