Mapping是Elasticsearch中定义文档及其字段如何存储和索引的过程,类似于关系数据库中的表结构定义。Mapping决定了字段的数据类型、是否索引、如何分词等重要属性。
| 类型 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 动态映射 | 自动推断字段类型 | 快速开始,无需预定义 | 可能推断错误,不够精确 |
| 显式映射 | 手动定义字段类型 | 精确控制,性能更好 | 需要提前规划 |
# Elasticsearch自动推断字段类型的规则
JSON数据类型 -> Elasticsearch字段类型
true/false -> boolean
123 -> long
123.45 -> float
"2024-01-01" -> date (如果符合日期格式)
"hello" -> text + keyword (默认)
# 示例:动态映射
POST /products/_doc/1
{
"name": "iPhone 15",
"price": 5999,
"in_stock": true,
"release_date": "2023-09-22"
}
# 查看自动生成的mapping
GET /products/_mapping
# 响应:
{
"products": {
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"price": {
"type": "long"
},
"in_stock": {
"type": "boolean"
},
"release_date": {
"type": "date"
}
}
}
}
}
生产环境建议使用显式映射,可以精确控制字段类型和索引行为。
# 创建完整的mapping
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"brand": {
"type": "keyword"
},
"price": {
"type": "float"
},
"stock": {
"type": "integer"
},
"description": {
"type": "text",
"analyzer": "ik_smart"
},
"category": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"release_date": {
"type": "date",
"format": "yyyy-MM-dd"
},
"is_active": {
"type": "boolean"
},
"rating": {
"type": "float"
}
}
}
}
| 类型 | 说明 | 使用场景 |
|---|---|---|
| text | 全文本,会被分词 | 商品描述、文章内容 |
| keyword | 精确值,不分词 | 品牌、分类、标签 |
# text类型示例
PUT /articles
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "standard"
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
# keyword类型示例
PUT /users
{
"mappings": {
"properties": {
"username": {
"type": "keyword"
},
"email": {
"type": "keyword"
},
"status": {
"type": "keyword"
}
}
}
}
| 类型 | 范围 | 使用场景 |
|---|---|---|
| byte | -128 ~ 127 | 年龄、状态码 |
| short | -32768 ~ 32767 | 端口号 |
| integer | -2^31 ~ 2^31-1 | 数量、ID |
| long | -2^63 ~ 2^63-1 | 时间戳、大数值 |
| float | 32位浮点数 | 评分、比率 |
| double | 64位浮点数 | 精确计算 |
# 数值类型示例
PUT /products
{
"mappings": {
"properties": {
"price": {
"type": "float"
},
"stock": {
"type": "integer"
},
"sales": {
"type": "long"
},
"rating": {
"type": "float"
}
}
}
}
# 日期类型示例
PUT /orders
{
"mappings": {
"properties": {
"order_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
},
"created_at": {
"type": "date"
},
"updated_at": {
"type": "date",
"format": "strict_date_optional_time"
}
}
}
}
# 插入不同格式的日期
POST /orders/_doc/1
{
"order_date": "2024-01-15 10:30:00"
}
POST /orders/_doc/2
{
"order_date": "2024-01-15"
}
POST /orders/_doc/3
{
"order_date": 1705305000000
}
# 布尔类型示例
PUT /products
{
"mappings": {
"properties": {
"is_active": {
"type": "boolean"
},
"in_stock": {
"type": "boolean"
}
}
}
}
# 布尔值可以是:true, false, "true", "false"
POST /products/_doc/1
{
"is_active": true,
"in_stock": "false"
}
# Elasticsearch没有专门的数组类型,任何字段都可以包含多个值
PUT /products
{
"mappings": {
"properties": {
"tags": {
"type": "keyword"
},
"images": {
"type": "keyword"
}
}
}
}
POST /products/_doc/1
{
"tags": ["电子产品", "手机", "5G"],
"images": ["img1.jpg", "img2.jpg", "img3.jpg"]
}
# 对象类型(嵌套JSON)
PUT /users
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"address": {
"properties": {
"province": {
"type": "keyword"
},
"city": {
"type": "keyword"
},
"street": {
"type": "text"
},
"zipcode": {
"type": "keyword"
}
}
}
}
}
}
POST /users/_doc/1
{
"name": "张三",
"address": {
"province": "北京",
"city": "北京市",
"street": "朝阳区某某街道",
"zipcode": "100000"
}
}
Multi-fields允许对同一个字段使用不同的方式进行索引,这是一个非常强大的特性。
# Multi-fields示例
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
},
"pinyin": {
"type": "text",
"analyzer": "pinyin"
}
}
}
}
}
}
# 使用不同的字段进行查询
# 全文搜索
GET /products/_search
{
"query": {
"match": {
"name": "手机"
}
}
}
# 精确匹配
GET /products/_search
{
"query": {
"term": {
"name.keyword": "iPhone 15 Pro"
}
}
}
# 拼音搜索
GET /products/_search
{
"query": {
"match": {
"name.pinyin": "shouji"
}
}
}
# 控制字段是否被索引
PUT /users
{
"mappings": {
"properties": {
"username": {
"type": "keyword",
"index": true # 可以被搜索(默认)
},
"password": {
"type": "keyword",
"index": false # 不能被搜索,只能存储
}
}
}
}
# 控制字段是否单独存储
PUT /articles
{
"mappings": {
"properties": {
"title": {
"type": "text",
"store": true # 单独存储,可以直接获取
},
"content": {
"type": "text",
"store": false # 不单独存储(默认)
}
}
}
}
# 为null值设置默认值
PUT /products
{
"mappings": {
"properties": {
"stock": {
"type": "integer",
"null_value": 0 # null值会被替换为0
}
}
}
}
# 将多个字段的值复制到一个字段
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"copy_to": "full_text"
},
"description": {
"type": "text",
"copy_to": "full_text"
},
"full_text": {
"type": "text"
}
}
}
}
# 可以直接搜索full_text字段
GET /products/_search
{
"query": {
"match": {
"full_text": "手机"
}
}
}
注意:已存在的字段类型不能修改,只能添加新字段。
# 添加新字段
PUT /products/_mapping
{
"properties": {
"discount": {
"type": "float"
},
"supplier": {
"type": "keyword"
}
}
}
# 如果需要修改字段类型,必须重建索引
# 1. 创建新索引
PUT /products_v2
{
"mappings": {
"properties": {
"price": {
"type": "double" # 修改类型
}
}
}
}
# 2. 使用reindex迁移数据
POST /_reindex
{
"source": {
"index": "products"
},
"dest": {
"index": "products_v2"
}
}
# 3. 删除旧索引,重命名新索引
DELETE /products
POST /_aliases
{
"actions": [
{
"add": {
"index": "products_v2",
"alias": "products"
}
}
]
}
1. 基础Mapping创建
2. Multi-fields练习
3. 数值类型选择
4. 日期格式
5. 对象类型
6. Mapping参数
7. 更新Mapping
8. 实战场景
Q: text和keyword有什么区别?
A: text会被分词,适合全文搜索;keyword不分词,适合精确匹配、排序和聚合。例如商品名称用text,品牌用keyword。
Q: 为什么不能修改已存在字段的类型?
A: 因为字段类型决定了数据的存储和索引方式,修改类型会导致已有数据无法正确解析。必须通过reindex重建索引。
Q: 什么时候使用multi-fields?
A: 当同一个字段需要支持多种查询方式时。例如商品名称既要支持全文搜索,又要支持精确匹配和排序。
Q: 如何选择合适的数值类型?
A: 根据数据范围选择最小的类型。例如年龄用byte,价格用float,时间戳用long。这样可以节省存储空间和提高性能。