• es nested object区别


    Object

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

    1. DELETE /yzh2
    2. PUT /yzh2
    3. {
    4. "mappings": {
    5. "properties": {
    6. "familyName": {
    7. "type": "keyword"
    8. },
    9. "people": {
    10. "dynamic": "false",
    11. "properties": {
    12. "name": {
    13. "type": "keyword"
    14. },
    15. "age": {
    16. "type": "long"
    17. }
    18. }
    19. }
    20. }
    21. }
    22. }
    23. GET /yzh2/_mapping
    24. POST /yzh2/_doc
    25. {
    26. "familyName": "张三家",
    27. "people": [
    28. {
    29. "name": "狗娃",
    30. "age": 18
    31. },
    32. {
    33. "name": "狗剩",
    34. "age": 20
    35. }
    36. ]
    37. }
    38. GET /yzh2/_search

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

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

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

    1. GET /yzh2/_search
    2. {
    3. "query": {
    4. "bool": {
    5. "must": [
    6. {
    7. "term": {
    8. "people.name": "狗娃"
    9. }
    10. },
    11. {
    12. "term": {
    13. "people.age": "20"
    14. }
    15. }
    16. ]
    17. }
    18. }
    19. }
    1. "hits" : [
    2. {
    3. "_index" : "yzh2",
    4. "_type" : "_doc",
    5. "_id" : "1hFKcoIBaBIqY6rAiFaI",
    6. "_score" : 1.3616575,
    7. "_source" : {
    8. "familyName" : "张三家",
    9. "people" : [
    10. {
    11. "name" : "狗娃",
    12. "age" : 18
    13. },
    14. {
    15. "name" : "狗剩",
    16. "age" : 20
    17. }
    18. ]
    19. }
    20. }
    21. ]

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

    nested

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

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

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

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

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

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

    图片来自:rockybean教程

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

    1. DELETE /yzh3
    2. PUT /yzh3
    3. {
    4. "mappings": {
    5. "properties": {
    6. "familyName": {
    7. "type": "keyword"
    8. },
    9. "people": {
    10. "type": "nested",
    11. "dynamic": "false",
    12. "properties": {
    13. "name": {
    14. "type": "keyword"
    15. },
    16. "age": {
    17. "type": "long"
    18. }
    19. }
    20. }
    21. }
    22. }
    23. }
    24. GET /yzh3/_mapping
    25. POST /yzh3/_doc
    26. {
    27. "familyName": "张三家",
    28. "people": [
    29. {
    30. "name": "狗娃",
    31. "age": 18
    32. },
    33. {
    34. "name": "狗剩",
    35. "age": 20
    36. }
    37. ]
    38. }
    39. GET /yzh3/_search

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

    1. GET /yzh3/_search
    2. {
    3. "query": {
    4. "nested": {
    5. "path": "people",
    6. "query": {
    7. "bool": {
    8. "must": [
    9. {
    10. "term": {
    11. "people.name": "狗娃"
    12. }
    13. },
    14. {
    15. "term": {
    16. "people.age": "20"
    17. }
    18. }
    19. ]
    20. }
    21. }
    22. }
    23. }
    24. }

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

    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变为

    1. {
    2. "query": {
    3. "bool": {
    4. "must": [
    5. {
    6. "nested": {
    7. "path": "tags",
    8. "query": {
    9. "query_string": {
    10. "query": "tags.key:k1 AND tags.value:v1"
    11. }
    12. }
    13. }
    14. },
    15. {
    16. "nested": {
    17. "path": "tags",
    18. "query": {
    19. "query_string": {
    20. "query": "tags.key:k2 AND tags.value:v2"
    21. }
    22. }
    23. }
    24. }
    25. ]
    26. }
    27. }
    28. }

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

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

    参考文档:

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

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

  • 相关阅读:
    Explain执行计划字段解释说明---ID字段说明
    JavaEE初阶---JVM
    tictoc例子理解6-9
    浅析Nordic nRF5 SDK例程架构
    mysql为什么用b+树
    mapreduce序列化(Hadoop)
    提升性能的利器:深入解析SectionReader
    高版本tensorflow部分模块缺失
    Java爬虫-爬取疫苗批次信息
    离线语音与IoT结合:智能家居发展新增长点
  • 原文地址:https://blog.csdn.net/yzh_1346983557/article/details/126195967