Skip to content
鼓励作者:欢迎打赏犒劳

ES动态字段

这里有坑,大家注意,我们都知道es会自动建表建映射关系,但是在真正的生产环境中,没有人这样干,都是提前建好表,并且"dynamic": "strict", 那这就意味着,es的动态字段的优势荡然无存,但是我们可以采用曲线救国的路线来实现,下面我给大家演示下常见的三种方法的利弊。

  • flattened 的优势,可以无限新增字段,支持精确匹配,但是无法模糊匹配。不建议
  • object 的类型,定义表的时候必须要定义好字段,不能新增未定义的字段,也有很大的局限性,不建议
  • Nested 可以采用对象数组的形式来实现 动态字段的新增,不过缺点是写法比较麻烦 一般用它

你就记住

Nested 类型就是专门为「对象数组」设计的!

object/flattened 就是专门为普通对象设计的!

  object 需要提前定义好里面的字段,可以利用text/keyword来实现 精确/模糊/分词查询

  flattened 不需要定义,可以随便扩展,但是只能精确匹配

Nested 扩展属性(最灵活,但复杂)

shell
PUT /product_item
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "product_id": { "type": "keyword" },
      "product_name": { "type": "text" },
      "price": { "type": "double" },
      
      // 🔥 所有扩展属性用 Nested 存储
      "ext_attrs": {
        "type": "nested",
        "properties": {
          "key": { "type": "keyword" },
          "text_value": { "type": "text", "fields": { "keyword": { "type": "keyword" } } },
          "int_value": { "type": "integer" },
          "double_value": { "type": "double" },
          "date_value": { "type": "date" },
          "bool_value": { "type": "boolean" }
        }
      }
    }
  }
}

使用方式:

shell
POST /product_item/_doc/1
{
  "product_id": "P10001",
  "product_name": "iPhone 15",
  "price": 1199.99,
  "ext_attrs": [
    { "key": "brand", "text_value": "Apple" },
    { "key": "color", "text_value": "Titanium" },
    { "key": "storage", "text_value": "256GB" },
    { "key": "warranty_months", "int_value": 12 },
    { "key": "rating", "double_value": 4.8 },
    { "key": "release_date", "date_value": "2026-01-15" },
    { "key": "is_waterproof", "bool_value": true }
  ]
}

// 查询:brand = "Apple" warranty_months >= 12
GET /product_item/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "ext_attrs",
            "query": {
              "bool": {
                "must": [
                  { "term": { "ext_attrs.key": "brand" } },
                  { "term": { "ext_attrs.text_value": "Apple" } }
                ]
              }
            }
          }
        },
        {
          "nested": {
            "path": "ext_attrs",
            "query": {
              "bool": {
                "must": [
                  { "term": { "ext_attrs.key": "warranty_months" } },
                  { "range": { "ext_attrs.int_value": { "gte": 12 } } }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

优点:

  • 无限扩展
  • 支持所有查询类型
  • 可以做聚合统计
  • 每个属性都有明确类型

缺点:

  • Nested 查询性能比普通字段慢 2-3 倍
  • 写入性能下降(每个属性是独立文档)
  • 查询语法复杂

object数组类型的坑

我们都知道,如果字段类型是 "type": "object" 的话,其实是可以存数组对象的,但是这里有一个巨坑。

shell
# 建表
PUT /test_index
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "product_name": { "type": "text" },
      
      "specs": {
        "type": "object",
        "properties": {
          "name": { "type": "keyword" },
          "value": { "type": "keyword" }
        }
      }
    }
  }
}
# 插入数据
PUT /test_index/_doc/1
{
  "product_name": "iPhone 15",
  "specs": [
    { "name": "Color", "value": "Black" },
    { "name": "Storage", "value": "256GB" }
  ]
}
PUT /test_index/_doc/2
{
  "product_name": "iPhone 16",
  "specs": [
    { "name": "Color", "value": "256GB" },
    { "name": "Storage", "value": "Black" }
  ]
}
# 问题复现,竟然能查询出来
GET /test_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "term": { "specs.name": "Color" } },
        { "term": { "specs.value": "Black" } }
      ]
    }
  }
}

原因是因为object类型,对于值是数组对象的时候,它底层会变成这样的储存,丢失了kv的关联关系

shell
specs.name = ["Color", "Storage"]
specs.value = ["Black", "256GB"]

如有转载或 CV 的请标注本站原文地址