Elasticsearch RESTful API入门:初识Mapping完全指南
本文专为Elasticsearch初学者设计,将深入浅出地讲解Mapping的核心概念与实操技巧,助你掌握数据结构定义的艺术
一、什么是Mapping?为什么需要它?
1.1 Mapping的核心作用
数据结构定义: 相当于数据库中的表结构设计
数据类型控制: 决定字段如何被存储和索引
搜索行为影响: 直接影响查询的精度和效率
存储优化: 合理设置可减少磁盘空间占用
1.2 类比关系型数据库
Elasticsearch | 关系型数据库 |
---|---|
Index(索引) | Database(数据库) |
Mapping(映射) | Table Schema(表结构) |
Document(文档) | Row(行) |
Field(字段) | Column(列) |
二、Mapping类型详解
2.1 元字段(Meta-Fields)
bash
GET /products/_mapping
// 响应中的元字段示例
{
"_index": "products", // 所属索引
"_id": "1", // 文档ID
"_source": {...}, // 原始JSON文档
"_version": 1, // 版本号
"_score": 1.0 // 相关性评分
}
2.2 核心数据类型
数据类型 | 示例 | 适用场景 |
---|---|---|
text | "商品名称" | 全文搜索字段 |
keyword | "CAT001" | 精确值过滤/聚合 |
numeric | 199.99 | 价格、数量等数值 |
date | "2023-08-15" | 时间日期数据 |
boolean | true/false | 状态标记 |
geo_point | "41.12,-71.34" | 地理位置数据 |
三、Mapping定义实战
3.1 创建索引时定义Mapping
bash
PUT /products
{
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "ik_max_word", // 使用中文分词器
"fields": {
"keyword": { // 多字段定义
"type": "keyword"
}
}
},
"price": {
"type": "scaled_float", // 优化存储的浮点数
"scaling_factor": 100
},
"tags": {
"type": "keyword",
"ignore_above": 256 // 忽略过长字段
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"location": {
"type": "geo_point"
}
}
}
}
3.2 查看Mapping定义
bash
GET /products/_mapping
// 响应示例
{
"products": {
"mappings": {
"properties": {
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"location": {
"type": "geo_point"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
},
// ...其他字段定义
}
}
}
}
3.3 动态Mapping vs 静态Mapping
动态Mapping示例(Elasticsearch自动推断类型)
bash
// 插入文档
POST /dynamic_index/_doc/1
{
"name": "智能手机",
"price": 3999.00,
"stock": 100,
"is_active": true,
"release_date": "2023-06-01"
}
// 查看自动生成的Mapping
GET /dynamic_index/_mapping
// 响应
{
"properties": {
"is_active": {"type": "boolean"},
"name": {"type": "text", "fields": {...}},
"price": {"type": "float"},
"release_date": {"type": "date"},
"stock": {"type": "long"}
}
}
关闭动态Mapping
bash
PUT /strict_index
{
"mappings": {
"dynamic": "strict", // 严格模式
"properties": {
"name": {"type": "text"}
}
}
}
// 尝试插入未定义字段将失败
POST /strict_index/_doc/1
{
"name": "测试商品",
"price": 100 // 将返回错误
}
四、Mapping更新技巧
4.1 添加新字段
bash
PUT /products/_mapping
{
"properties": {
"discount_rate": {
"type": "float",
"index": false // 仅存储不索引
}
}
}
4.2 修改现有字段(重要限制!)
bash
! 警告 !
已存在字段的Mapping类型**无法直接修改**,必须通过以下流程:
1. 创建新索引并定义正确的Mapping
2. 使用_reindex API迁移数据
3. 删除旧索引
4. 使用别名切换新索引
4.3 重命名字段
bash
POST /_reindex
{
"source": {"index": "old_products"},
"dest": {"index": "new_products"},
"script": {
"source": """
ctx._source.product_name = ctx._source.remove('old_name');
"""
}
}
五、Java客户端操作Mapping
java
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
public class MappingOperations {
private final RestHighLevelClient client;
public MappingOperations(RestHighLevelClient client) {
this.client = client;
}
// 创建带Mapping的索引
public boolean createIndexWithMapping() throws Exception {
CreateIndexRequest request = new CreateIndexRequest("products");
// 构建Mapping
XContentBuilder mappingBuilder = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties")
.startObject("product_name")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("price")
.field("type", "scaled_float")
.field("scaling_factor", 100)
.endObject()
.endObject()
.endObject();
request.mapping(mappingBuilder);
CreateIndexResponse response = client.indices()
.create(request, RequestOptions.DEFAULT);
return response.isAcknowledged();
}
// 更新Mapping(添加新字段)
public boolean addNewField() throws Exception {
PutMappingRequest request = new PutMappingRequest("products");
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties")
.startObject("discount_rate")
.field("type", "float")
.field("index", false)
.endObject()
.endObject()
.endObject();
request.source(builder);
AcknowledgedResponse response = client.indices()
.putMapping(request, RequestOptions.DEFAULT);
return response.isAcknowledged();
}
// 获取Mapping
public GetMappingsResponse getMapping() throws Exception {
GetMappingsRequest request = new GetMappingsRequest()
.indices("products");
return client.indices()
.getMapping(request, RequestOptions.DEFAULT);
}
}
六、Mapping设计最佳实践
6.1 字段设计黄金法则
1.文本搜索: 使用text类型 + 合适的分词器
2.精确匹配: 使用keyword类型(如ID、状态码)
3.数值范围: 选择最小够用的类型(byte > short > integer > long)
4.避免多类型: 同名字段必须保持相同数据类型
5.禁用不需要字段: "index": false节省资源
6.2 多字段(Multi-fields)妙用
bash
"product_name": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
},
"pinyin": {
"type": "text",
"analyzer": "pinyin" // 拼音搜索
},
"english": {
"type": "text",
"analyzer": "english" // 英文分词
}
}
}
6.3 动态模板(Dynamic Templates)
bash
PUT /smart_index
{
"mappings": {
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword",
"ignore_above": 256
}
}
},
{
"float_as_scaled": {
"match": "*_price",
"mapping": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
]
}
}
七、常见问题与解决方案
7.1 类型冲突错误
bash
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "mapper [price] cannot be changed from type [float] to [integer]"
}
]
}
}
解决方案:
1.创建新索引
2.使用_reindex迁移数据
3.使用别名切换索引
7.2 字段值超过ignore_above限制
bash
"tags": {
"type": "keyword",
"ignore_above": 256 // 超过256字符不会被索引
}
建议: 根据业务需求调整阈值
7.3 日期格式不匹配
bash
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}
技巧: 使用多格式定义增加容错性
八、总结
通过本文,您应该掌握:
✅ Mapping的核心概念与作用
✅ 数据类型的选择与配置
✅ 动态Mapping与静态Mapping的差异
✅ Mapping的创建、查看与更新方法
✅ Java客户端操作Mapping的技巧
✅ 最佳实践与常见问题解决方案
下期预告:《Elastic Search的RestFul API入门:常见的mapping字段》