在 Elasticsearch 中,数据建模与映射(Mapping) 是决定搜索性能、存储效率和功能支持的核心环节。合理的映射设计能让搜索更精准、聚合更高效、存储更节省。
本文将全面详解 Elasticsearch 的 数据建模原则、字段类型、动态映射、自定义分析器、嵌套结构、最佳实践 等关键内容。
一、什么是映射(Mapping)?
映射(Mapping) 是 Elasticsearch 中对索引结构的定义,类似于关系型数据库中的 Schema。它描述了:
- 每个字段的名称和数据类型;
- 是否分词(用于全文搜索);
- 是否索引(可被搜索);
- 如何处理日期、数字、对象等复杂类型。
✅ 每个索引都有一个 mapping,定义其文档结构。
二、映射的核心组成
一个完整的 mapping 包含:
{"mappings": {"properties": { ... }, // 字段定义"dynamic": true, // 动态映射策略"date_detection": true, // 是否自动检测日期"numeric_detection": false, // 是否自动检测数字"_source": { "enabled": true }, // 是否存储原始 JSON"routing": { "required": false }}
}
三、字段类型详解(Field Types)
Elasticsearch 支持丰富的字段类型,选择合适的类型至关重要。
1. 常用字段类型
类型 | 用途 | 是否分词 | 示例 |
---|---|---|---|
text | 全文搜索(会分词) | ✅ | "这是一段中文文本" |
keyword | 精确匹配、聚合、排序 | ❌ | "status:active" |
date | 日期类型 | - | "2024-06-01T10:00:00Z" |
long , integer , short , byte | 整数 | - | 100 , -5 |
double , float | 浮点数 | - | 3.14 |
boolean | 布尔值 | - | true , false |
ip | IP 地址 | - | "192.168.1.1" |
geo_point | 地理位置坐标 | - | {"lat": 39.9, "lon": 116.4} |
object | 嵌套 JSON 对象 | - | {"name": "张三", "age": 30} |
nested | 独立索引的嵌套对象 | - | 数组中的对象列表 |
binary | 二进制数据(Base64) | - | 图片缩略图 |
flattened | 将复杂 JSON 作为 keyword 处理 | ❌ | 动态标签 |
2. text
vs keyword
:最重要的选择
场景 | 推荐类型 |
---|---|
搜索商品标题 | text |
聚合品牌、分类 | keyword |
排序价格、时间 | keyword 或数值类型 |
精确匹配订单号 | keyword |
✅ 最佳实践:对同一字段使用 多字段(multi-fields):
"title": {"type": "text","analyzer": "ik_max_word","fields": {"keyword": { "type": "keyword" }}
}
- 搜索用
title
- 聚合/排序用
title.keyword
四、动态映射(Dynamic Mapping)
1. 什么是动态映射?
当插入一个新字段时,Elasticsearch 自动推断其类型并添加到 mapping 中。
PUT /my-index/_doc/1
{"user": "张三", → 推断为 text + keyword"age": 30, → 推断为 long"created": "2024-06-01" → 推断为 date
}
2. 动态策略(dynamic
)
设置 | 说明 |
---|---|
true (默认) | 自动添加新字段 |
false | 忽略新字段,不报错 |
strict | 拒绝未知字段,抛出异常 |
✅ 生产环境建议设为
strict
,防止字段污染。
PUT /my-index
{"mappings": {"dynamic": "strict","properties": { ... }}
}
五、自定义分析器(Analyzer)
用于控制 text
字段的分词方式,直接影响搜索结果。
1. 分析器组成
- Character Filters:预处理(如 HTML 标签过滤)
- Tokenizer:分词器(如空格、中文分词)
- Token Filters:后处理(小写、停用词)
2. 示例:中文分词配置
PUT /news
{"settings": {"analysis": {"analyzer": {"my_ik_analyzer": {"type": "custom","tokenizer": "ik_max_word","filter": ["lowercase"]}}}},"mappings": {"properties": {"content": {"type": "text","analyzer": "my_ik_analyzer"}}}
}
3. 常见中文分词插件
ik
:最常用,支持自定义词典jieba
:Python 社区流行smartcn
:官方提供,功能有限
六、复杂结构建模
1. object
类型(扁平化对象)
{"address": {"city": "北京","district": "朝阳区"}
}
映射:
"address": {"properties": {"city": { "type": "keyword" },"district": { "type": "keyword" }}
}
⚠️ 内部字段是扁平化的:
address.city
和address.district
是独立字段。
2. nested
类型(独立索引的对象)
适用于数组中的对象,需保持内部关系。
示例:用户评论
{"comments": [{ "user": "A", "content": "好", "likes": 10 },{ "user": "B", "content": "差", "likes": 1 }]
}
错误方式(object
):
"comments": {"properties": { ... }
}
→ 会匹配 "user:A AND content:差"
(跨文档匹配)
正确方式(nested
):
"comments": {"type": "nested","properties": {"user": { "type": "keyword" },"content": { "type": "text" },"likes": { "type": "integer" }}
}
查询:
"query": {"nested": {"path": "comments","query": {"bool": {"must": [{ "match": { "comments.user": "A" } },{ "match": { "comments.content": "好" } }]}}}
}
3. join
类型(父子文档)
用于一对多关系(如文章与评论),但性能较低。
"relation": {"type": "join","relations": {"post": "comment"}
}
⚠️ 7.x 后推荐用
nested
或应用层关联。
七、高级映射设置
1. _source
控制
- 是否存储原始 JSON,默认
true
- 设为
false
可节省空间,但无法:- 获取原始文档
- 使用
update
API - 高亮、脚本字段
"_source": { "enabled": false }
2. doc_values
与 fielddata
doc_values: true
(默认):列式存储,用于排序、聚合(推荐开启)fielddata
:用于text
字段聚合,消耗大,不推荐
"tags": {"type": "text","fielddata": true // 仅在必须时启用
}
3. index
设置
控制字段是否可被搜索:
"internal_id": {"type": "keyword","index": false // 不参与搜索,仅存储
}
八、数据建模最佳实践 ✅
场景 | 建议 |
---|---|
文本搜索 | 使用 text + 中文分词器(如 ik) |
聚合/排序 | 使用 keyword 或数值类型 |
多字段 | 使用 fields 定义 text + keyword |
动态字段 | 生产环境设为 strict |
嵌套对象 | 使用 nested 保持关系 |
日期 | 使用 date 类型,格式统一 |
大字段 | index: false 节省空间 |
冗余设计 | 适当反规范化提升查询性能 |
九、反规范化 vs 正规范化
策略 | 说明 | 适用场景 |
---|---|---|
反规范化(Denormalization) | 将关联数据冗余存储 | 查询频繁、写少 |
正规范化(Normalization) | 拆分为多个索引,通过 join 查询 | 数据一致性要求高 |
✅ Elasticsearch 推荐 适度反规范化,以提升查询性能。
十、总结:映射设计 checklist
项目 | 是否完成 |
---|---|
使用 text 用于全文搜索 | ✅ |
使用 keyword 用于聚合排序 | ✅ |
中文字段配置 IK 分词器 | ✅ |
关键字段启用 doc_values | ✅ |
嵌套对象使用 nested | ✅ |
生产环境 dynamic: strict | ✅ |
_source 根据需求启用 | ✅ |
避免 fielddata on text | ✅ |