Nestedb
简介Nested 和 Object 是什么关系?Nested 查询要注意什么?inner_hitNested Aggregation是什么?能否用Nested做动态kv?动态kv如何做agg呢?怎么在kibana里做agg呢?参考资料
ElasticSearch中可以将数据以对象的方式存储并查询,但是ES底层的Lucene 没有内部对象的概念,因此如果通过默认的方式往ES中插入对象,ES会将对象层次结构扁平化为字段名称和值的简单列表。
nested类型是object数据类型的特殊版本,它允许对象数组以一种可以相互独立查询的方式进行索引。
在Nested内部,每个对象索引其实是一个单独的隐藏文档,这意味着每个嵌套对象都可以独立于其他对象进行查询。
查询nested对象时,只要查询条件符合这个nested对象里的某一个条件,整个nested对象都会被检索出来。如果只想要nested中里的一个对象,就可以使用inner_hits。使用比较简单,只需要在查询语句之后加上inner_hits即可。
简介
Nested是什么?
- 直观的说,Nested实际上就是Object的数组。如下,这个user就是个nested结构
Nested 和 Object 是什么关系?
- ES原生支持Object类型,也就是任意字段都可以是个对象,而ES又是所有字段都是多值,也就是都可以是list。那么在ES中Nested和Object List又是什么关系呢?
- 这就要从Object说起了。Object虽然是个对象,但是实际存储时是在当前文档里打平存储的。如上那个例子,如果只有一个user,那么在真实索引中实际上是下面这样的
- 而如果是个list,那么就成了
- 因为建索引时打平,因此检索时ES就无法知道到底是
John Smith
还是John White
了。因此引入了Nested结构。
- Nested将list里的每个doc单独变成子文档进行存储,因此在查询时就可以知道具体的结构信息了。
Nested 查询要注意什么?
inner_hit
elasticsearch提供了nested数据类型来处理主子文档的问题,可以解决子文档字段被分裂平铺导致字段之间失去了整体的关联性;
parent-join 和 nested 功能允许返回在不同范围的匹配文档。在父/子情况下,根据子文档中的匹配返回父文档,或者根据父文档中的匹配返回子文档。在嵌套的情况下,将根据嵌套内部对象中的匹配返回文档。
elasticsearch提供的inner hits主要完成在通过子文档进行匹配查询的时候,可以方便控制匹配的子文档的返回;
- Nested因为是单独的子文档存储,因此在使用时,直接用
a.b.c
是无法访问的,需要将其套在nested查询里。除此之外,和其他的查询并无差异。
- 如上所示,用一个
nested
套住真实query即可。默认的hit是返回父文档,也就是大的doc。如果加上inner_hits
会在父文档的source中多一个inner_hits
的字段,返回真实命中的object,其中有个offset表明list数组下标。
- 需要注意的是,由于单独存储很耗资源,因此默认一个index最多只有50个nested字段。此外,虽然nested是单独存储的,但是其字段数也算入index总字段数,默认最多1000个。
Nested Aggregation是什么?
- 对于Nested结构,有一点需要谨记的,就是他是个List结构。Nested Agg就是对这个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
变为
动态kv如何做agg呢?
- 普通查询的确很简单,但是agg就并不简单了。原来的模式可以直接用真实字段
tags.k1
做agg,但是在nested里k1已经变成了一个字段的值,因此没法直接做agg了。
- 这时就需要引入script大法了。其实agg的本质就是从每个doc的正排里取一个值,用这个值做聚合。因此我们只需要用script遍历list,找到对应的key然后返回其value即可。
- 简单写了个如下所示,如果有更好的方法欢迎留言。
- 注意!!! 由于nested单独存储,因此doc里并没有nested数据,需要用params从source中拿。性能很差,仅可用于少量数据场景!
- 在使用中我们还可以把script存在来,来加速运算,减少缓存。(注意:5.6以后将code改为了source字段,具体写法参阅文档)
- 这样用起来就简单多了
怎么在kibana里做agg呢?
- kibana其实和上面的一样,也是用script.不过只支持inline的,在script field配置。不过注意一定不能太多,因为每一个inline script都是一个单独的script都需要消耗存储资源。
参考资料
Loading...