es nested object区别

Object

ES原生支持Object类型,也就是任意字段都可以是个对象,而ES又是所有字段都支持多值,即都可以是list。es的object类型虽然是对象类型,但是数据是打平存储的。如下,声明一个对象,新增1条数据:

DELETE /yzh2

PUT /yzh2
{
  "mappings": {
    "properties": {
      "familyName": {
        "type": "keyword"
      },
      "people": {
        "dynamic": "false",
        "properties": {
          "name": {
            "type": "keyword"
          },
          "age": {
            "type": "long"
          }
        }
      }
    }
  }
}

GET /yzh2/_mapping

POST /yzh2/_doc
{
  "familyName": "张三家",
  "people": [
    {
      "name": "狗娃",
      "age": 18
    },
    {
      "name": "狗剩",
      "age": 20
    }
  ]
}

GET /yzh2/_search

但实际存储的时候,是打平这样存储的:

{
  "familyName" : "张三家",
  "people.name": ["狗娃", "狗剩"],
  "people.age": [18, 20]
}

就丢失了name和age之间的关联关系,就不知道谁是18岁谁是20岁了。所以,这样查询也能查询出结果:

GET /yzh2/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "people.name": "狗娃"
          }
        },
        {
          "term": {
            "people.age": "20"
          }
        }
      ]
    }
  }
}
"hits" : [
      {
        "_index" : "yzh2",
        "_type" : "_doc",
        "_id" : "1hFKcoIBaBIqY6rAiFaI",
        "_score" : 1.3616575,
        "_source" : {
          "familyName" : "张三家",
          "people" : [
            {
              "name" : "狗娃",
              "age" : 18
            },
            {
              "name" : "狗剩",
              "age" : 20
            }
          ]
        }
      }
    ]

可是狗娃是18岁,狗剩才是20岁啊! 所以,为解决es object类型的数据扁平化存储问题,引入了nested类型。

nested

nested类型:嵌套文档,对象数组的优先选择类型。Nested将数组中的每个对象作为单独的隐藏文档(hidden separate document)进行索引。

解决问题:对象数组的多字段匹配查询。

在独立索引每一个嵌套对象后,对象中每个字段的相关性得以保留。我们查询时,也仅仅返回那些真正符合条件的文档。

不仅如此,由于嵌套文档直接存储在文档内部,查询时嵌套文档和根文档联合成本很低,速度和单独存储几乎一样。

嵌套文档是隐藏存储的,我们不能直接获取。如果要增删改一个嵌套对象,我们必须把整个文档重新索引才可以。值得注意的是,查询的时候返回的是整个文档,而不是嵌套文档本身。

如果需要索引对象数组而不是单个对象,优先考虑使用嵌套数据类型Nested。
如果不需要对 Nested 子文档精确搜索的就选型 object,需要的选型 Nested。

图片来自:rockybean教程

nested类型的定义在声明时指定 "type": "nested" 即可!

DELETE /yzh3

PUT /yzh3
{
  "mappings": {
    "properties": {
      "familyName": {
        "type": "keyword"
      },
      "people": {
        "type": "nested",
        "dynamic": "false",
        "properties": {
          "name": {
            "type": "keyword"
          },
          "age": {
            "type": "long"
          }
        }
      }
    }
  }
}

GET /yzh3/_mapping

POST /yzh3/_doc
{
  "familyName": "张三家",
  "people": [
    {
      "name": "狗娃",
      "age": 18
    },
    {
      "name": "狗剩",
      "age": 20
    }
  ]
}

GET /yzh3/_search

Nested因为是单独的子文档存储,因此在使用时,直接用 a.b.c 是无法访问的,需要将其套在nested查询里,且需要指定 "path" 。

GET /yzh3/_search
{
  "query": {
    "nested": {
      "path": "people",
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "people.name": "狗娃"
              }
            },
            {
              "term": {
                "people.age": "20"
              }
            }
          ]
        }
      }
    }
  }
}

这时返回的数据为空,满足我们的需求。

Nested注意点

由于单独存储很耗资源,因此默认一个index最多只有50个nested字段。此外,虽然nested是单独存储的,但是其字段数也算入index总字段数,默认最多1000个。

Nested结构是个List结构。Nested Aggregation就是对这个list做agg操作,agg写法和普通的一样,只需要在外面套上nested即可。

能否用Nested做动态kv?

Nested除了存储固定的Object List,还有一种常用的场景就是用来存储动态的KV。虽然ES天然支持dynamic mapping,但是其key都是固化在每一个doc中的,如果存储用户自定义报表数据。每个用户的key差异很大,放在同一张表会出现大量空值。这是很浪费系统资源的行为,并且随着Key的不断增多,最终会超出index的最大key数量。
因此用nested结构来处理这种动态kv就比较合适。 nested的本质就是将 {"tags":{"k1":"v1","k2":"v2"}}=>{"tags":[{"key":"key1","value":"v1"},{"key":"key2","value":"v2"}]}
这样一来就可以轻松处理动态kv。并且查询依旧简单,例如k1:v1 AND k2:v2变为

{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "tags",
            "query": {
              "query_string": {
                "query": "tags.key:k1 AND tags.value:v1"
              }
            }
          }
        },
        {
          "nested": {
            "path": "tags",
            "query": {
              "query_string": {
                "query": "tags.key:k2 AND tags.value:v2"
              }
            }
          }
        }
      ]
    }
  }
}

 Nested 新增或更新子文档操作,为什么需要更新整个文档?

嵌套 Nested 文档在物理上位于根文档旁边的 Lucene 段中。这是为什么当只想更改单个嵌套文档时必须重建根文档和所有嵌套 Nested 文档的原因。

参考文档:

https://www.elastic.co/guide/cn/elasticsearch/guide/current/nested-objects.html

一起来学ES —— 浅谈Nested结构 - Lambda说

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值