干货 | Elasticsearch Nested类型深入详解

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 0、概要在Elasticsearch实战场景中,我们或多或少会遇到嵌套文档的组合形式,反映在ES中称为父子文档。父子文档的实现,至少包含以下两种方式:1)父子文档父子文档在5.X版本中通过parent-child父子type实现,即:1个索引对应多个type;6.X+版本已经不再支持一个索引多个type,6.X+的父子索引的实现改成Join。2)Nested嵌套类型

image.png

链接

本文通过一个例子将Nested类型适合解决的问题、应用场景、使用方法串起来,

文中所有的DSL都在Elasticsearch6.X+验证通过。

1、Elasticsearch 数据类型全景概览

image.png

2、从一个例子说起吧

2.1 问题背景

在elasticsearch中,我们可以将密切相关的实体存储在单个文档中。 例如,我们可以通过传递一系列评论来存储博客文章及其所有评论。

举例:


{

 "title": "Invest Money",

 "body": "Please start investing money as soon...",

 "tags": ["money", "invest"],

 "published_on": "18 Oct 2017",

 "comments": [

   {

     "name": "William",

     "age": 34,

     "rating": 8,

     "comment": "Nice article..",

     "commented_on": "30 Nov 2017"

   },

   {

     "name": "John",

     "age": 38,

     "rating": 9,

     "comment": "I started investing after reading this.",

     "commented_on": "25 Nov 2017"

   },

   {

     "name": "Smith",

     "age": 33,

     "rating": 7,

     "comment": "Very good post",

     "commented_on": "20 Nov 2017"

   }

 ]

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

如上所示,所以我们有一个文档描述了一个帖子和一个包含帖子上所有评论的内部对象评论。

但是Elasticsearch搜索中的内部对象并不像我们期望的那样工作。


2.2 问题出现

现在假设我们想查找用户{name:john,age:34}评论过的所有博客帖子。 让我们再看一下上面的示例文档,找到评论过的用户。


name age

William 34

John 38

Smith 33

从列表中我们可以清楚地看到,没有34岁的用户John。

为简单起见,我们在elasticsearch索引中只有1个文档。

让我们通过查询索引来验证它:


GET /blog/_search?pretty

{

 "query": {

   "bool": {

     "must": [

       {

         "match": {

           "comments.name": "John"

         }

       },

       {

         "match": {

           "comments.age": 34

         }

       }

     ]

   }

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

我们的示例文档作为回复返回。 很惊讶,这是为什么呢?


2.3 原因分析

这就是为什么我说:

elasticsearch中的内部对象无法按预期工作

这里的问题是elasticsearch(lucene)使用的库没有内部对象的概念,因此内部对象被扁平化为一个简单的字段名称和值列表。

我们的文档内部存储为:


{

 "title":                    [ invest, money ],

 "body":                     [ as, investing, money, please, soon, start ],

 "tags":                     [ invest, money ],

 "published_on":             [ 18 Oct 2017 ]

 "comments.name":            [ smith, john, william ],

 "comments.comment":         [ after, article, good, i, investing, nice, post, reading, started, this, very ],

 "comments.age":             [ 33, 34, 38 ],

 "comments.rating":          [ 7, 8, 9 ],

 "comments.commented_on":    [ 20 Nov 2017, 25 Nov 2017, 30 Nov 2017 ]

}

1

2

3

4

5

6

7

8

9

10

11

如上,您可以清楚地看到,comments.name和comments.age之间的关系已丢失。

这就是为什么我们的文档匹配john和34的查询。


2.4 如何解决呢?

要解决这个问题,我们只需要对elasticsearch的映射进行一些小改动。

如果您查看索引的映射,您会发现comments字段的类型是object。

我们需要更新它的类型为nested。

我们可以通过运行以下查询来简单地更新索引的映射:


PUT /blog_new

{

 "mappings": {

   "blog": {

     "properties": {

       "title": {

         "type": "text"

       },

       "body": {

         "type": "text"

       },

       "tags": {

         "type": "keyword"

       },

       "published_on": {

         "type": "keyword"

       },

       "comments": {

         "type": "nested",

         "properties": {

           "name": {

             "type": "text"

           },

           "comment": {

             "type": "text"

           },

           "age": {

             "type": "short"

           },

           "rating": {

             "type": "short"

           },

           "commented_on": {

             "type": "text"

           }

         }

       }

     }

   }

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

将映射更改为Nested类型后,我们可以查询索引的方式略有变化。 我们需要使用Nested查询。

下面给出了Nested查询示例:


GET /blog_new/_search?pretty

{

 "query": {

   "bool": {

     "must": [

       {

         "nested": {

           "path": "comments",

           "query": {

             "bool": {

               "must": [

                 {

                   "match": {

                     "comments.name": "john"

                   }

                 },

                 {

                   "match": {

                     "comments.age": 34

                   }

                 }

               ]

             }

           }

         }

       }

     ]

   }

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

由于用户{name:john,age:34}没有匹配,上面的查询将不返回任何文档。


再次感到惊讶? 只需一个小小的改变即可解决问题。

这可能是我们理解的一个较小的变化,但是在elasticsearch存储我们的文档的方式上有很多变化。

在内部,嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着可以独立于其他对象查询每个嵌套对象。

下面给出了更改映射后样本文档的内部表示:


{

 {

   "comments.name":    [ john ],

   "comments.comment": [ after i investing started reading this ],

   "comments.age":     [ 38 ],

   "comments.rating":  [ 9 ],

   "comments.date":    [ 25 Nov 2017 ]

 },

 {

   "comments.name":    [ william ],

   "comments.comment": [ article, nice ],

   "comments.age":     [ 34 ],

   "comments.rating":   [ 8 ],

   "comments.date":    [ 30 Nov 2017 ]

 },

 {

   "comments.name":    [ smith ],

   "comments.comment": [ good, post, very],

   "comments.age":     [ 33 ],

   "comments.rating":   [ 7 ],

   "comments.date":    [ 20 Nov 2017 ]

 },

 {

   "title":            [ invest, money ],

   "body":             [ as, investing, money, please, soon, start ],

   "tags":             [ invest, money ],

   "published_on":     [ 18 Oct 2017 ]

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

如您所见,每个内部对象都在内部存储为单独的隐藏文档。 这保持了他们的领域之间的关系。


3、Nested类型的作用?

从上一小节,可以清晰的看出nested类型的特别之处。

nested类型是对象数据类型的专用版本,它允许对象数组以可以彼此独立查询的方式进行索引。


4、Nested类型的适用场景image.png

——图片来自:rockybean教程


5、Nested类型的增、删、改、查、聚合操作详解

还是以第2节的blog_new索引示例,Nested类型的增、删、改、查操作。


5.1 Nested类型——增

新增blog和评论


POST blog_new/blog/2

{

 "title": "Hero",

 "body": "Hero test body...",

 "tags": ["Heros", "happy"],

 "published_on": "6 Oct 2018",

 "comments": [

   {

     "name": "steve",

     "age": 24,

     "rating": 18,

     "comment": "Nice article..",

     "commented_on": "3 Nov 2018"

   }

 ]

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

5.2 Nested类型——删

序号为1的评论原来有三条,现在删除John的评论数据,删除后评论数为2条。


POST  blog_new/blog/1/_update

{

"script": {

   "lang": "painless",

   "source": "ctx._source.comments.removeIf(it -> it.name == 'John');"

}

}

1

2

3

4

5

6

7

5.3 Nested类型——改

将steve评论内容中的age值调整为25,同时调整了评论内容。


POST blog_new/blog/2/_update

{

 "script": {

   "source": "for(e in ctx._source.comments){if (e.name == 'steve') {e.age = 25; e.comment= 'very very good article...';}}"

 }

}

1

2

3

4

5

6

5.4 Nested类型——查

如前所述,查询评论字段中评论姓名=William并且评论age=34的blog信息。


GET /blog_new/_search?pretty

{

 "query": {

   "bool": {

     "must": [

       {

         "nested": {

           "path": "comments",

           "query": {

             "bool": {

               "must": [

                 {

                   "match": {

                     "comments.name": "William"

                   }

                 },

                 {

                   "match": {

                     "comments.age": 34

                   }

                 }

               ]

             }

           }

         }

       }

     ]

   }

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

5.5 Nested类型——聚合

认知前提:nested聚合隶属于聚合分类中的Bucket聚合分类。

聚合blog_new 中评论者年龄最小的值。


GET blog_new/_search

{

 "size": 0,

 "aggs": {

   "comm_aggs": {

     "nested": {

       "path": "comments"

     },

     "aggs": {

       "min_age": {

         "min": {

           "field": "comments.age"

         }

       }

     }

   }

 }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

6、小结

如果您在索引中使用内部对象并做查询操作,请验证内部对象的类型是否为nested类型。 否则查询可能会返回无效的结果文档。

更新认知是非常痛苦的,不确定的问题只有亲手实践才能检验真知。


参考:

[1]http://t.cn/Evwh0uW

[2]官网6.x+:http://t.cn/Ehltakr


image.png

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
8月前
|
数据库 索引
elasticsearch中join类型数据如何进行父子文档查询?
elasticsearch中join类型数据如何进行父子文档查询?
|
3天前
|
搜索推荐 JavaScript Java
Elasticsearch 8.X 如何依据 Nested 嵌套类型的某个字段进行排序?
Elasticsearch 8.X 如何依据 Nested 嵌套类型的某个字段进行排序?
22 0
|
3天前
|
存储 SQL 运维
Elasticsearch 查询革新:探索 Wildcard 类型的高效模糊匹配策略
Elasticsearch 查询革新:探索 Wildcard 类型的高效模糊匹配策略
16 0
|
3天前
|
数据建模 索引
干货 | Elasticsearch Nested 数组大小求解,一网打尽!
干货 | Elasticsearch Nested 数组大小求解,一网打尽!
7 0
|
11天前
|
SQL JSON DataWorks
DataWorks产品使用合集之DataWorks 数据集成任务中,将数据同步到 Elasticsearch(ES)中,并指定 NESTED 字段中的 properties 类型如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
20 0
|
9月前
|
存储 JSON 数据建模
Elasticsearch数据建模实战之基于nested object实现博客与评论嵌套关系
Elasticsearch数据建模实战之基于nested object实现博客与评论嵌套关系
|
11月前
|
存储 搜索推荐 大数据
大数据数据存储的搜索引擎Elasticsearch的数据类型的复杂类型
在使用搜索引擎Elasticsearch存储大数据时,了解其数据类型是非常重要的。除了基础数据类型之外,Elasticsearch还支持多种复杂数据类型,这些数据类型通常用于存储结构化数据和关联数据。在本文中,我们将会介绍Elasticsearch的复杂数据类型。
66 0
|
11月前
|
存储 自然语言处理 搜索推荐
大数据数据存储的搜索引擎Elasticsearch的数据类型的基础类型
在使用搜索引擎Elasticsearch存储大数据时,了解其数据类型是非常重要的。Elasticsearch支持多种数据类型,包括基础类型和复合类型。在本文中,我们将会介绍Elasticsearch的基础数据类型。
99 0
|
11月前
|
存储 搜索推荐 大数据
大数据数据存储的搜索引擎Elasticsearch的基础概念的类型
随着大数据时代的到来,如何高效地存储和检索数据成为了企业面临的重要挑战。针对这个问题,搜索引擎Elasticsearch应运而生。作为一名阿里云开发者社区的博主,本文将会从大数据存储的角度,深入探讨Elasticsearch中的数据类型。
74 0
|
12月前
|
数据建模
白话Elasticsearch59-数据建模实战_ Nested Aggregation/ Reverse nested Aggregation对嵌套的博客评论数据进行聚合分析
白话Elasticsearch59-数据建模实战_ Nested Aggregation/ Reverse nested Aggregation对嵌套的博客评论数据进行聚合分析
53 0

热门文章

最新文章