Elasticsearch RESTful API入门:初识Mapping完全指南

发布于:2025-07-12 ⋅ 阅读:(18) ⋅ 点赞:(0)

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)

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

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定义

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自动推断类型)

// 插入文档
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

PUT /strict_index
{
  "mappings": {
    "dynamic": "strict",  // 严格模式
    "properties": {
      "name": {"type": "text"}
    }
  }
}

// 尝试插入未定义字段将失败
POST /strict_index/_doc/1
{
  "name": "测试商品",
  "price": 100  // 将返回错误
}

四、Mapping更新技巧

4.1 添加新字段

PUT /products/_mapping
{
  "properties": {
    "discount_rate": {
      "type": "float",
      "index": false  // 仅存储不索引
    }
  }
}

4.2 修改现有字段(重要限制!)

! 警告 !
已存在字段的Mapping类型**无法直接修改**,必须通过以下流程:
1. 创建新索引并定义正确的Mapping
2. 使用_reindex API迁移数据
3. 删除旧索引
4. 使用别名切换新索引

4.3 重命名字段

POST /_reindex
{
  "source": {"index": "old_products"},
  "dest": {"index": "new_products"},
  "script": {
    "source": """
      ctx._source.product_name = ctx._source.remove('old_name');
    """
  }
}

五、Java客户端操作Mapping

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)妙用

"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)

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 类型冲突错误

{
  "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限制

"tags": {
  "type": "keyword",
  "ignore_above": 256  // 超过256字符不会被索引
}

建议: 根据业务需求调整阈值

7.3 日期格式不匹配

"created_at": {
  "type": "date",
  "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}

技巧: 使用多格式定义增加容错性

八、总结

通过本文,您应该掌握:

  ✅ Mapping的核心概念与作用

  ✅ 数据类型的选择与配置

  ✅ 动态Mapping与静态Mapping的差异

  ✅ Mapping的创建、查看与更新方法

  ✅ Java客户端操作Mapping的技巧

  ✅ 最佳实践与常见问题解决方案

下期预告:《Elastic Search的RestFul API入门:常见的mapping字段》


网站公告

今日签到

点亮在社区的每一天
去签到