<返回目录     Powered by claude/xia兄

第6课: Mapping和字段类型

什么是Mapping?

Mapping是Elasticsearch中定义文档及其字段如何存储和索引的过程,类似于关系数据库中的表结构定义。Mapping决定了字段的数据类型、是否索引、如何分词等重要属性。

动态映射 vs 显式映射

类型 说明 优点 缺点
动态映射 自动推断字段类型 快速开始,无需预定义 可能推断错误,不够精确
显式映射 手动定义字段类型 精确控制,性能更好 需要提前规划

动态映射规则

# 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的索引

# 创建完整的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"
      }
    }
  }
}

常用字段类型

1. 文本类型

类型 说明 使用场景
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"
      }
    }
  }
}

2. 数值类型

类型 范围 使用场景
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"
      }
    }
  }
}

3. 日期类型

# 日期类型示例
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
}

4. 布尔类型

# 布尔类型示例
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"
}

5. 数组类型

# Elasticsearch没有专门的数组类型,任何字段都可以包含多个值
PUT /products
{
  "mappings": {
    "properties": {
      "tags": {
        "type": "keyword"
      },
      "images": {
        "type": "keyword"
      }
    }
  }
}

POST /products/_doc/1
{
  "tags": ["电子产品", "手机", "5G"],
  "images": ["img1.jpg", "img2.jpg", "img3.jpg"]
}

6. 对象类型

# 对象类型(嵌套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允许对同一个字段使用不同的方式进行索引,这是一个非常强大的特性。

# 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"
    }
  }
}

重要的Mapping参数

1. index参数

# 控制字段是否被索引
PUT /users
{
  "mappings": {
    "properties": {
      "username": {
        "type": "keyword",
        "index": true  # 可以被搜索(默认)
      },
      "password": {
        "type": "keyword",
        "index": false  # 不能被搜索,只能存储
      }
    }
  }
}

2. store参数

# 控制字段是否单独存储
PUT /articles
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "store": true  # 单独存储,可以直接获取
      },
      "content": {
        "type": "text",
        "store": false  # 不单独存储(默认)
      }
    }
  }
}

3. null_value参数

# 为null值设置默认值
PUT /products
{
  "mappings": {
    "properties": {
      "stock": {
        "type": "integer",
        "null_value": 0  # null值会被替换为0
      }
    }
  }
}

4. copy_to参数

# 将多个字段的值复制到一个字段
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": "手机"
    }
  }
}

更新Mapping

注意:已存在的字段类型不能修改,只能添加新字段。

# 添加新字段
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"
      }
    }
  ]
}
Mapping最佳实践:

练习题

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。这样可以节省存储空间和提高性能。