Elasticsearch 数据建模与映射(Mapping)详解_建模中的mapping
在 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
\"192.168.1.1\"
geo_point
{\"lat\": 39.9, \"lon\": 116.4}
object
{\"name\": \"张三\", \"age\": 30}
nested
binary
flattened
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 正规范化
✅ Elasticsearch 推荐 适度反规范化,以提升查询性能。
十、总结:映射设计 checklist
text
用于全文搜索keyword
用于聚合排序doc_values
nested
dynamic: strict
_source
根据需求启用fielddata
on text