在处理大量数据时,关系数据库存在很多问题。 无论是速度,高效处理,有效并行化,可扩展性还是成本,当数据量开始增长时,关系数据库都会失败。该关系数据库的另一个挑战是必须预先定义关系和模式。Elasticsearch 也是一个 NoSQL 文档数据存储。 但是,尽管是一个 NoSQL 数据存储,Elasticsearch 在一定程度上提供了很多帮助来管理关系数据。 它支持类似 SQL 的连接,并且在嵌套和相关的数据实体上工作得非常棒。

比如,对于一个像下面的 blog 形式的文档:

一个 blog 可能对应于很多的 comments,或者一个员工对应于很多的经验。这种数据就是关系数据。使用 Elasticsearch,你可以通过保留轻松地工作与不同实体的关联以及强大的全文搜索和分析。 Elasticsearch 通过引入两种类型的文档关系模型使这成为可能:

  • nested 关系: 在这种关系中,这种一对多的关系存在于同一个文档之中
  • parent-child 关系:在这种关系中,它们存在于不同的文档之中。我将在另外一个文章中描述

这两种关系在同一个模式下工作,即一对多个的关系。一个 root 或 parent 可以有一个及多个子 object。

如上图所示,在嵌套关系中,有一个根对象(object),它是我们拥有的主文档,它包含一个称为嵌套文档的子文档数组。 根对象内的文档嵌套级别没有限制。 例如,查看以下 JSON 以进行多级嵌套:

 {"location_id": "axdbyu","location_name": "gurgaon","company": [{"name": "honda","modelName": [{ "name": "honda cr-v", "price": "2 million" }]}, {"name": "bmw","modelName": [{ "name": "BMW 3 Series", "price": "2 million"},{ "name": "BMW 1 Series", "price": "3 million" }]} ]
}

下面,我们来做一个例子来展示一下为什么 nested 对象可以解决我们的问题。

Object 数据类型

JSON 文档本质上是分层的:文档可能包含内部对象,而内部对象又可能包含内部对象本身:

1)外部文档也是一个 JSON 对象。

2)它包含一个名为 manager 的内部对象。

3)其中又包含一个名为 name 的内部对象。

在 Elasticsearch 内部,该文档被索引为一个简单的键值对列表,如下所示:

{"region":             "US","manager.age":        30,"manager.name.first": "John","manager.name.last":  "Smith"
}

接下来,我们首先创建一个叫做 developer 的索引,并输入如下的两个数据:

POST developer/_doc/101
{"name": "zhang san","skills": [{"language": "ruby","level": "expert"},{"language": "javascript","level": "beginner"}]
}POST developer/_doc/102
{"name": "li si","skills": [{"language": "ruby","level": "beginner"}]
}

上面显示是一对多的一个 index。

Object Query

这个时候我们想搜一个 skills: language 是 ruby,并且 level 是 biginner 的文档。我们可能想到的方法是:

GET developer/_search
{"query": {"bool": {"filter": [{"match": {"skills.language": "ruby"}},{"match": {"skills.level": "beginner"}}]}}
}

通过上面的搜寻,我们得到的结果是:

{"took" : 0,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 0.0,"hits" : [{"_index" : "developer","_type" : "_doc","_id" : "101","_score" : 0.0,"_source" : {"name" : "zhang san","skills" : [{"language" : "ruby","level" : "expert"},{"language" : "javascript","level" : "beginner"}]}},{"_index" : "developer","_type" : "_doc","_id" : "102","_score" : 0.0,"_source" : {"name" : "li si","skills" : [{"language" : "ruby","level" : "beginner"}]}}]}
}

我们可以看到,我们得到两个结果。但是我们仔细查看一下发现得到的结果并不是我们想得到的。从我们的原意来说,我们想得到的是 li si,因为只有 li si 这个人的 language 是 ruby,并且他的 level 是 beginner。zhang san 这个文档,应该不在搜寻之列。这是为什么呢?

原来,langauge 及 level 是 skills 的 JSON 内部数组项。当 JSON 对象被 Lucene 扁平化后,我们失去了 language 和 level 之间的对应关系。取而代之的是如下的这种关系:

{"name": "zhang san","skills.language" :["ruby", "javascript"],"skills.level": ["expert", "beginner"]
}

如上所示,我们看到的是一个扁平化的数组。之前的那种 language 和 level 之间的那种对应关系已经不存在了。

Object aggregation

同样的问题也存在于 aggregation 中,比如我们想做一下的 aggregation:

GET developer/_search
{"size": 0,"aggs": {"languages": {"terms": {"field": "skills.language.keyword"},"aggs": {"level": {"terms": {"field": "skills.level.keyword"}}}}}
}

显示的结果是:

{"took" : 2,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : null,"hits" : [ ]},"aggregations" : {"languages" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "ruby","doc_count" : 2,"level" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "beginner","doc_count" : 2},{"key" : "expert","doc_count" : 1}]}},{"key" : "javascript","doc_count" : 1,"level" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "beginner","doc_count" : 1},{"key" : "expert","doc_count" : 1}]}}]}}
}

显然,对于 key javascript 来说,它并没有 expert 对应的 level,但是在我们的 aggregation 里显示出来了。这个结果显然是错误的。

nested 数据类型

Nested 类型是 object 数据类型的特殊版本,它允许对象数组以一种可以彼此独立查询的方式进行索引。在内部,嵌套对象将数组中的每个对象索引为单独的隐藏文档,这意味着每个嵌套对象都可以使用 nested query 独立于其他对象进行查询。每个 nested 对象都被索引为一个单独的 Lucene 文档。nested 数据类型能够让我们对 object 数组建立索引,并且分别进行查询。

如果需要维护数组中每个对象的关系,请使用 nested 数据类型

为了能够把我们的数据定义为nested,我们必须修改之前的索引 mapping 为:

DELETE developerPUT developer
{"mappings": {"properties": {"name": {"type": "text"},"skills": {"type": "nested","properties": {"language": {"type": "keyword"},"level": {"type": "keyword"}}}}}
}

经过这样的改造之后,重新把我们之前的数据输入到 index 里:

POST developer/_doc/101
{"name": "zhang san","skills": [{"language": "ruby","level": "expert"},{"language": "javascript","level": "beginner"}]
}POST developer/_doc/102
{"name": "li si","skills": [{"language": "ruby","level": "beginner"}]
}

针对 101,在 Lucence 中的数据结构变为:

{"name": "zhang san",{"skills.language": "ruby","skills.level": "expert"},{"skills.language": "javascript","skills.level", "beginner"}
}

nested query

我们来重新做我们之前的搜索:

GET developer/_search
{"query": {"nested": {"path": "skills","query": {"bool": {"filter": [{"match": {"skills.language": "ruby"}},{"match": {"skills.level": "beginner"}}]}}}}
}

注意上面的 nested 字段。显示的结果是:

{"took" : 5,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.0,"hits" : [{"_index" : "developer","_type" : "_doc","_id" : "102","_score" : 0.0,"_source" : {"name" : "li si","skills" : [{"language" : "ruby","level" : "beginner"}]}}]}
}

显然,我们只得到了一个我们想要的结果。

获取 inner hits

parent-join 和 nested 功能允许返回在不同范围内具有匹配项的文档。 在父/子情况下,根据子文档中的匹配返回父文档,或者根据父文档中的匹配返回子文档。 在 nested 的情况下,基于嵌套内部对象中的匹配返回文档。

在许多情况下,了解哪些内部嵌套对象(对于嵌套的情况)或子/父文档(对于父/子的情况)导致某些信息返回非常有用。 内部点击功能可用于此目的。 此功能会在搜索响应中为每个搜索命中返回附加的嵌套命中,这些嵌套命中会导致搜索命中在不同范围内匹配。

我们可以尝试如下的查询:

GET developer/_search
{"query": {"nested": {"path": "skills","query": {"bool": {"filter": [{"match": {"skills.language": "ruby"}},{"match": {"skills.level": "beginner"}}]}},"inner_hits": {}}}
}

在上面,我们添加了 inner_hits。上面写的命令返回的结果为:

{"took" : 1,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.0,"hits" : [{"_index" : "developer","_type" : "_doc","_id" : "102","_score" : 0.0,"_source" : {"name" : "li si","skills" : [{"language" : "ruby","level" : "beginner"}]},"inner_hits" : {"skills" : {"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.0,"hits" : [{"_index" : "developer","_type" : "_doc","_id" : "102","_nested" : {"field" : "skills","offset" : 0},"_score" : 0.0,"_source" : {"language" : "ruby","level" : "beginner"}}]}}}}]}
}

nested aggregation

同样,我们可以对我们的索引来做一个 aggregation:

GET developer/_search
{"size": 0,"aggs": {"nested_skills": {"nested": {"path": "skills"},"aggs": {"languages": {"terms": {"field": "skills.language"},"aggs": {"levels": {"terms": {"field": "skills.level"}}}}}}}
}

显示的结果是:

{"took" : 1,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : null,"hits" : [ ]},"aggregations" : {"nested_skills" : {"doc_count" : 3,"languages" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "ruby","doc_count" : 2,"levels" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "beginner","doc_count" : 1},{"key" : "expert","doc_count" : 1}]}},{"key" : "javascript","doc_count" : 1,"levels" : {"doc_count_error_upper_bound" : 0,"sum_other_doc_count" : 0,"buckets" : [{"key" : "beginner","doc_count" : 1}]}}]}}}
}

从上面显示的结果,可以看出来对于 ruby 来说,它分别对应于一个 bigginer 及一个 expert。这个和我们之前的数据是一样的。对于 javascript 来说,它只有一个 beginner 的 level。

Nested 数据类型更新

在使用 Elasticsearch 时,为了系统的效率,我们并不建议经常修改文档,但是在有些时候,我们还必须对已经索引过的文档进行修改。针对 nested 类型的字段,我该如何进行更新及删除呢?请阅读我的另外一篇文章 “Elasticsearch:如何修改 nested 字段的值”。

Elasticsearch: object 及 nested 数据类型相关推荐

  1. Elasticsearch 对象及 Nested 对象【1】

    文章目录 1. 数据的关联关系 2. 关系型数据库的范式化设计 3. Denormalization 4. 在 Elasticsearch 中处理关联关系 5. 案例 1:博客和其作者信息 5.1 设 ...

  2. (五)ElasticSearch 6.1.1数据类型

    1.elasticsearch的数据类型 1.1.核心数据类型 1.1.1.字符串类型(string不再支持) 当一个字段需要用于全文搜索(会被分词), 比如产品名称.产品描述信息, 就应该使用tex ...

  3. 【Elasticsearch】 es nested 嵌套类型 详解

    1.概述 数据类型,大合集参考 [Elasticsearch]Elasticsearch的数据类型 (text.keyword.date.object.geo等) 2 嵌套类型 - nested 嵌套 ...

  4. Elasticsearch:理解 Elasticsearch 中的 Percolator 数据类型及 Percolate 查询

    Elasticsearch 是功能强大的功能丰富的工具.在今天的文章中,我来介绍一下 Percolator 数据类型.同时也介绍一下 Percolate query. 您需要基本了解 Elastics ...

  5. python中object是什么数据类型_自学Python2.1-基本数据类型-字符串str(object) 上

    自学Python2.1-基本数据类型-字符串str(object) 上 字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串. 创建字符串很简单,只要为变量分配 ...

  6. Elasticsearch:constant keyword 数据类型

    Constant keyword 是 keyword 字段的特例,用于索引中所有文档具有相同值的情况.比如, PUT logs-debug {"mappings": {" ...

  7. Elasticsearch:高级数据类型介绍

    在我之前的文章 "Elasticsearch:一些有趣的数据类型",我已经介绍了一下很有趣的数据类型.在今天的文章中,我再进一步介绍一下高级的数据类型,虽然这里的数据类型可能和之前 ...

  8. Elasticsearch如何创建索引,添加,删除,更新文档

    文章目录 准备工作 检查 es 及 Kibana 是否运行正常 创建索引及文档 创建文档相关知识点 mulit-field 字段 关于两个type的解释 关于两个keyword的解释 mulit-fi ...

  9. Nested嵌套对象类型还挺实用

    上一篇文章中,我们学习了Join类型的父子文档,今天继续学习一下嵌套文档,毕竟嵌套文档也是Elasticsearch推荐的,首先我们看下面这条语句 PUT word_document/_doc/1 { ...

最新文章

  1. VRRP的虚拟MAC地址高位为
  2. C/C++结构体字节对齐详解
  3. 深入浅出解释FFT(一)——用fft求频谱
  4. 皮一皮:追忆我那随风逝去的阔腿裤...
  5. 【Kotlin】Kotlin 委托 ( 使用 by 关键字进行接口委托 )
  6. 2017 数据驱动大会赠票赠书名单出啦!
  7. python设置文件编码_python批量修改文件编码格式的方法
  8. 找回Google Reader丢失的笔记(notes)
  9. 遍历进程并获取进程路径 - 回复 编程少年 的问题
  10. python语言是 创造的_1.python简介
  11. 关于我2021腾讯暑期实习一面一波流这件事以及反思
  12. li序号 ul_ul ol li的数字序号编号样式
  13. 安卓百度地图,隐藏左下角Logo
  14. 学计算机白头,关于「一夜白头」的科学解释!90%的人不知道!
  15. unity3d 鼠标点击事件处理 处理鼠标点击
  16. 回声问题和回声消除技术及在可视监控对讲、楼宇对讲等领域的应用综述
  17. thinkphp6 框架源码分析
  18. 快速打开电脑计算机的快捷键,电脑自带的计算器如何快速打开和快捷键的添加...
  19. linux关闭触摸板
  20. not marked as ignorable

热门文章

  1. 二叉树遍历(先序,中序,后序,层序)递归和非递归形式
  2. 二叉树的Morris遍历:先序遍历和中序遍历
  3. 不只有docker:可选择的容器化工具还有很多……
  4. shell 删除simatic_请教Simatic net安装好后,硬盘目录下的Simatic shell是做什么的??...
  5. html 评论和浏览图标,多说自定义CSS动感头像跟多说评论显示User Agent的那些小事...
  6. 地图已知两点坐标画一条曲线
  7. 蘑菇街校招-possible sentences(搜索(深搜)、双指针)
  8. 目录网站网址导航网源码 帝国cms内核
  9. app下载 - app版本更新 (实测可行版)
  10. Abaqus 三维多面体骨料 随机多面体3D 无干涉多面体骨料模型