3. 定义商品索引的显式 Mapping 并写入文档

定义商品索引的显式 Mapping 并写入文档 学习卡片

商品索引的字段类型设计

商品数据里,同一个“名称”字段既可能用于全文检索,也可能用于精确聚合;同一个“品牌”字段通常只做精确过滤和聚合。Elasticsearch 默认的动态映射会把字符串同时映射为 text 和 keyword 子字段,看似方便,但字段类型、分词器、日期格式都不可控。显式 Mapping 的价值在于:在写入第一条文档之前,就把每个字段的检索行为固定下来。

本例设计一个 products 索引,字段规划如下:

  • product_id:keyword,商品唯一编号,用于精确查询和去重。
  • name:text,使用 ik_max_word 分词器,用于商品名称全文检索。
  • brand:keyword,品牌名,用于精确过滤与聚合。
  • price:integer,以“分”为单位的整数价格,避免浮点误差。
  • stock:integer,库存数量。
  • on_sale:boolean,是否在售。
  • release_date:date,格式 yyyy-MM-dd。
  • tags:keyword,标签数组,用于多值精确过滤。
若集群未安装 ik 分词插件,可将 name 的 analyzer 改为 standard,其余字段设计不变,仍可完成本例的写入与查询验证。

创建索引与显式 Mapping

使用 PUT /products 一次性提交 settings 与 mappings。显式声明 dynamic: strict 后,写入未定义字段会直接报错,能尽早暴露字段拼写或结构问题。

PUT /products
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "product_id":   { "type": "keyword" },
      "name":         { "type": "text", "analyzer": "ik_max_word" },
      "brand":        { "type": "keyword" },
      "price":        { "type": "integer" },
      "stock":        { "type": "integer" },
      "on_sale":      { "type": "boolean" },
      "release_date": { "type": "date", "format": "yyyy-MM-dd" },
      "tags":         { "type": "keyword" }
    }
  }
}

返回结果中 "acknowledged": true 表示索引与 Mapping 创建成功。随后可用 GET /products/_mapping 确认字段类型与分词器已按预期生效。

写入文档并查询验证

逐条写入 3 条商品文档,注意 price 用整数分、release_date 用 yyyy-MM-dd、tags 用数组:

POST /products/_doc/1
{ "product_id": "P1001", "name": "无线蓝牙降噪耳机",
  "brand": "AudioX", "price": 49900, "stock": 120,
  "on_sale": true, "release_date": "2024-03-15",
  "tags": ["耳机", "蓝牙", "降噪"] }

POST /products/_doc/2
{ "product_id": "P1002", "name": "机械键盘 87 键 青轴",
  "brand": "KeyPro", "price": 32900, "stock": 45,
  "on_sale": true, "release_date": "2023-11-02",
  "tags": ["键盘", "机械", "有线"] }

POST /products/_doc/3
{ "product_id": "P1003", "name": "便携蓝牙音箱 防水",
  "brand": "AudioX", "price": 19900, "stock": 0,
  "on_sale": false, "release_date": "2022-06-20",
  "tags": ["音箱", "蓝牙", "防水"] }

写入后执行 GET /products/_refresh 使文档立即可搜,然后分别验证三类查询:

# 1. 全文检索:name 字段分词后匹配“蓝牙”
GET /products/_search
{ "query": { "match": { "name": "蓝牙" } } }

# 2. 精确过滤 + 范围:品牌为 AudioX 且价格低于 30000 分
GET /products/_search
{ "query": { "bool": { "filter": [
  { "term": { "brand": "AudioX" } },
  { "range": { "price": { "lt": 30000 } } }
] } } }

# 3. 日期范围:2023 年之后发布
GET /products/_search
{ "query": { "range": { "release_date": { "gte": "2023-01-01" } } } }

预期结果:查询 1 命中文档 1 和 3(名称含“蓝牙”);查询 2 只命中文档 3(AudioX 且 19900 < 30000);查询 3 命中文档 1 和 2。若查询 1 未命中,优先检查 name 的 analyzer 是否与写入时一致;若查询 2 报字段类型错误,检查 brand 是否被误建为 text。

products 索引:显式 Mapping 字段类型与查询路径 显式 Mapping 字段定义 product_id : keyword name : text (ik_max_word) brand : keyword price : integer(单位:分) stock : integer on_sale : boolean release_date : date (yyyy-MM-dd) tags : keyword(数组) dynamic : strict(拒绝未定义字段) 写入 写入的 3 条文档 _id: 1 P1001 降噪耳机 AudioX / 49900 2024-03-15 / 在售 tags: 耳机,蓝牙,降噪 _id: 2 P1002 机械键盘 KeyPro / 32900 2023-11-02 / 在售 tags: 键盘,机械,有线 _id: 3 P1003 蓝牙音箱 AudioX / 19900 2022-06-20 / 下架 tags: 音箱,蓝牙,防水 查询 查询验证与命中 match: name=蓝牙 → 命中 _id 1, 3 text 分词后匹配 验证分词器生效 term brand=AudioX + range price<30000 → 命中 _id 3 keyword 精确过滤 range release_date ≥ 2023-01-01 → 命中 _id 1, 2 date 格式解析正确 未命中→查 analyzer / 类型

结果判读与常见问题

现象可能原因处理方式
写入报 strict_dynamic_mapping_exception文档含未在 Mapping 中定义的字段补全 Mapping 字段或修正文档字段名
match 查询无结果name 分词器与预期不符用 _analyze 查看分词结果,确认 analyzer
term 查询 brand 无结果brand 被建为 text 而非 keyword重建索引并修正字段类型
日期范围查询报解析错误写入日期格式与 Mapping 的 format 不一致统一为 yyyy-MM-dd 或扩展 format
验证通过标准:GET /products/_mapping 中 8 个字段类型与设计一致;3 条文档写入均返回 result: created;上述三类查询命中结果与预期完全一致。