草庐IT

ES 添加对象nested和object 的区别

いNeil 2023-09-28 原文

Object

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

如下,声明一个对象,新增1条数据:

DELETE /test-index
 
PUT /test-index
{
    "settings": {
        "number_of_shards": 8,
        "number_of_replicas": 1,
        "codec": "best_compression"
    },
    "mappings": {
        "test-type": {
            "dynamic": "true",
            "_routing": {
                "required": false
            },
            "_all": {
                "enabled": false
            },
            "properties": {
                "keywordsWithCount": {
                    "dynamic": "false",
                    "properties": {
                        "keyword": {
                            "type": "keyword"
                        },
                        "count": {
                            "type": "keyword"
                        }
                    }
                },
                "companyName": {
                    "type": "keyword"
                }
            }
        }
    }
}
 
POST /test-index/test-type/1
{
  "companyName": "大富翁",
  "keywordsWithCount": [
    {
      "keyword": "NP0001",
      "count": "5"
    },
    {
      "keyword": "NP0002",
      "count": "15"
    }
  ]
}
 
GET /test-index/_search

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

{
  "companyName" : "大富翁",
  "keywordsWithCount.keyword": ["NP0001", "NP0002"],
  "keywordsWithCount.count": ["1", "2"]
}

就丢失了keyword和count之间的关联关系,就不知道谁是 1谁是2了。所以,这样查询也能查询出结果:

GET /test-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "keywordsWithCount.keyword": "NP0001"
          }
        },
        {
          "term": {
            "keywordsWithCount.count": "2"
          }
        }
      ]
    }
  }
}

返参:
"hits" : [
      {
        "_index" : "test-index",
        "_type" : "test-type",
        "_id" : "1",
        "_source" : {
          "companyName" : "大富翁",
          "keywordsWithCount" : [
            {
              "keyword" : "NP0001",
              "count" : "1"
            },
            {
              "keyword" : "NP0002",
              "count" : "2"
            }
          ]
        }
      }
    ]

可是NP0001是1,NP0002才是2 所以,为解决es object类型的数据扁平化存储问题,引入了nested类型。

Nested

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

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

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

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

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

如果需要索引对象数组而不是单个对象,优先考虑使用嵌套数据类型Nested。

如果不需要对 Nested 子文档精确搜索的就选型 object,需要的选型 Nested。

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

DELETE /test-index-2

PUT test-index-2
{
    "settings": {
        "number_of_shards": 8,
        "number_of_replicas": 1,
        "codec": "best_compression"
    },
    "mappings": {
        "test-type": {
            "dynamic": "true",
            "_routing": {
                "required": false
            },
            "_all": {
                "enabled": false
            },
            "properties": {
                "keywordsWithCount": {
                    "type": "nested",
                    "dynamic": "false",
                    "properties": {
                        "keyword": {
                            "type": "keyword"
                        },
                        "count": {
                            "type": "keyword"
                        }
                    }
                },
                "companyName": {
                    "type": "keyword"
                }
            }
        }
    }
}

POST /test-index-2/test-type/1
{
  "companyName": "大富翁",
  "keywordsWithCount": [
    {
      "keyword": "NP0001",
      "count": "5"
    },
    {
      "keyword": "NP0002",
      "count": "15"
    }
  ]
}
 
GET /test-index-2/_search

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

GET /test-index-2/_search
{
  "query": {
    "nested": {
      "path": "keywordsWithCount",
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "keywordsWithCount.keyword": "NP0001"
              }
            },
            {
              "term": {
                "keywordsWithCount.count": "2"
              }
            }
          ]
        }
      }
    }
  }
}

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

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和object 的区别的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  3. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  4. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  5. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

    我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

  6. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  7. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  8. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  9. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  10. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

随机推荐