
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_field | 100 | long | 整数 → long(ES 没有 int,最小整数类型是 long) |
float_field | 3.14 | float | 小数 → float(不是 double,除非值很大或明确指定) |
bool_field | true | boolean | 布尔值 → boolean |
date_field | "2022/03/16" | date | 字符串若能被默认日期格式解析,则视为 date 默认格式包括: yyyy/MM/dd、ISO8601 等 |
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_field | null | 不会出现在 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_field和index.mapping.coerce等设置) - 可通过模板关闭,但默认如此
4. null 值不会创建字段
null_field: null→ mapping 中无此字段- 后续如果插入
{"null_field": "hello"}→ 此时才会创建为text
5. 对象(object)会递归应用 dynamic 规则
obj_field.key1和obj_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
}因为 name 和 age 都没在 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: false | dynamic: 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中,类型是keywordmeta_request类型是flattened- 没有意外字段
✅ 优势总结
| 优势 | 说明 |
|---|---|
| 结构可控 | 核心字段必须预定义,避免脏数据 |
| 灵活扩展 | 通过命名约定支持安全的动态字段 |
| 防止 mapping explosion | 限制自动映射范围 + 使用 flattened |
| 生产友好 | 设置分片数、副本、refresh 等参数 |
💡 定制建议
你可以根据实际需求调整:
- 修改
index_patterns匹配你的索引命名规范 - 增加更多
dynamic_templates(如metrics_*→double) - 调整
ignore_above、analyzer等参数

