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