ElasticSearch 中的Parent-Child关系和nested模型是相似的, 两个都可以用于复杂的数据结构中,区别是 nested 类型的文档是把所有的实体聚合到一个文档中而Parent-Child现对于比较独立,每个实体即为一个文档

Parent-Child 优点

1、父文档更新时不用重新为子文档建立索引

2、子文档的增加、修改、删除是对父文档和其他子文档没有任何影响的,这非常适用于子文档非常大并且跟新频繁的场景

3、子文档也可以查询结果返回

ElasticSearch 内部维护一个map来保存Parent-Child之间的关系,正是由于这个map,所以关联查询能够做到响应速度很快,但是确实有个限制是Parent 文档和所有的Child 文档都必须保存到同一个shard中

ElasticSearch parent-child ID的映射是存到Doc value 中的,有足够的内存时响 应是很快的。当这个map很大的时候,还是有要有一部分存储在硬盘中的。

Parent-Child Mapping

为了建立Parent-Child 模型我们需要在创建mapping的时候指定父文档和子文档或者在子文档创建之前利用update-index API 来指定

例如:我们有个公司,其子公司分布在全国各地,我要分析员工和子公司的关系

我们使用Parent-Child 机构

我们需要建立employee type 和 branch type 并且指定 branch 为_parent

PUT /company
{
"mappings": {
"branch": {},
"employee": {
"_parent": {
"type": "branch"
}
}
}
}

Indexing Parents and Children

创建父索引和创建其他索引并没有区别,父文档并不需要知道他们的子文档

POST /company/branch/_bulk
{ "index": { "_id": "london" }}
{ "name": "London Westminster", "city": "London", "country": "UK" }
{ "index": { "_id": "liverpool" }}
{ "name": "Liverpool Central", "city": "Liverpool", "country": "UK" }
{ "index": { "_id": "paris" }}
{ "name": "Champs Élysées", "city": "Paris", "country": "France" }

创建子文档的时候你必须指出他们的父文档的id

PUT /company/employee/1?parent=london
{
"name": "Alice Smith",
"dob": "1970-10-24",
"hobby": "hiking"
}

指定parent id 有两个目的:他是父文档和子文档的关联,而且他也保证了父文档和子文档会存储在同一个shard中,

在routing那个章节我们解释了ElasticSearch 如何利用routing的值来决定分配到shard中的,如果文档没有指定routing的值的化,那么默认为_id,公式为

shard = hash(routing) % number_of_primary_shards

但是,如果指定了 parent id 那么routing的值就不是_id 了 而是 parent id,换句话说就是父文档和子文档是具有相同的routing的值来确保他们会分配到同一个shard中的

当我们用GET请求来检索子文档时,我们需要指定parent id,并且创建索引、更新索引、还有删除索引都需要指定parent id,不像搜索的请求,他会分发到所有的shard中,这些single-document请求只会发送到存储它的shard中。如果没有指定parent id 也许请求会发送到一个错误的shard中

当我们使用buk API 时也需要指定parent id

POST /company/employee/_bulk
{ "index": { "_id": 2, "parent": "london" }}
{ "name": "Mark Thomas", "dob": "1982-05-16", "hobby": "diving" }
{ "index": { "_id": 3, "parent": "liverpool" }}
{ "name": "Barry Smith", "dob": "1979-04-01", "hobby": "hiking" }
{ "index": { "_id": 4, "parent": "paris" }}
{ "name": "Adrien Grand", "dob": "1987-05-11", "hobby": "horses" }

Finding Parents by Their Children

has_child 和 filter 可以根据子文档的内容来查询父文档,例如我们可以用这样的语句搜索所有分公司,出生在1980年以后的员工:

GET /company/branch/_search
{
"query": {
"has_child": {
"type": "employee",
"query": {
"range": {
"dob": {
"gte": "1980-01-01"
}
}
}
}
}
}

has_child 查询会匹配到多个子文档,每个文档都会有不同的关联得分。这些得分如何减少父文档的单个得分取决于分数模型的参数。默认参数为none,即会忽略子文档的得分,并且父文档会加1.0.

下面的查询会同时返回london 还有 liverpool 但是london 会得到一个更好的得分,因为Alice Smith 更加匹配london

GET /company/branch/_search
{
"query": {
"has_child": {
"type": "employee",
"score_mode": "max",
"query": {
"match": {
"name": "Alice Smith"
}
}
}
}
}

min_children and max_children

has_child 和 filter 都有min_children 和 max_children 两个参数,作用是返回那些具有子文档个数与之相匹配的父文档数据

下面的查询会返回具有两个员工以上的分公司

GET /company/branch/_search
{
"query": {
"has_child": {
"type": "employee",
"min_children": 2,
"query": {
"match_all": {}
}
}
}
}

Finding Children by Their Parents

和nested 查询只能返回根节点数据不同的是,父文档和子文档都是相对独立的,并且可以被单独查询,has_child 查询可以根据子文档返回父文档 而 has_parent查询会根据父文档返回子文档

和has_child 查询很相似,下面的查询会返回那些工作在uk的员工

GET /company/employee/_search
{
"query": {
"has_parent": {
"type": "branch",
"query": {
"match": {
"country": "UK"
}
}
}
}
}

has_parent 查询也支持score_mode模式,但是它只有两种设置none(默认)和score,每个子文档可以只拥有一个父文档,所以就没有必要将分数分给多个子文档了,这仅仅取决于你使用none还是score模式了

Children Aggregation

Parent-child 支持children aggregation parent aggregation 是不支持的

下面的例子示范了我们分析了员工的兴趣

GET /company/branch/_search
{
"size" : 0,
"aggs": {
"country": {
"terms": {
"field": "country"
},
"aggs": {
"employees": {
"children": {
"type": "employee"
},
"aggs": {
"hobby": {
"terms": {
"field": "hobby"
}
}
}
}
}
}
}
}

Grandparents and Grandchildren

parent-child 关系不仅仅可以有两代,他可以具有多代关系,但是所有关联的数据都必须分到同一个shard中去。

我们稍微修改下之前的列子,叫county 成为branch 的父文档

PUT /company
{
"mappings": {
"country": {},
"branch": {
"_parent": {
"type": "country"
}
},
"employee": {
"_parent": {
"type": "branch"
}
}
}
}

Countries and branches 只是简单的父子关系,所以我们用相同的方式来创建索引数据

POST /company/country/_bulk
{ "index": { "_id": "uk" }}
{ "name": "UK" }
{ "index": { "_id": "france" }}
{ "name": "France" } POST /company/branch/_bulk
{ "index": { "_id": "london", "parent": "uk" }}
{ "name": "London Westmintster" }
{ "index": { "_id": "liverpool", "parent": "uk" }}
{ "name": "Liverpool Central" }
{ "index": { "_id": "paris", "parent": "france" }}
{ "name": "Champs Élysées" }

parent id 保证了每个branch和他们的父文档都被分配到了同一个shard中了,

如果和之前一样,我们来创建employee 数据,会发生什么?

PUT /company/employee/1?parent=london
{
"name": "Alice Smith",
"dob": "1970-10-24",
"hobby": "hiking"
}

shard 会根据文档的parent ID—london 来分配employee 文档,但是这个london 文档会根据他的parent id uk来分配,所以employee文档和country、branch 很有可能被分配到不同的shard中。

所以我们需要一个额外的参数routing保证所有关联的文档被分配到同一个shard中。

PUT /company/employee/1?parent=london&routing=uk
{
"name": "Alice Smith",
"dob": "1970-10-24",
"hobby": "hiking"
}

parent 参数仍然用于子文档和父文档的关联,routing 参数是用于保证文档被分配到哪个shard中去

查询和聚合对于多级的文档也仍然有效,例如:问了找到哪些城市的员工喜欢hiking

GET /company/country/_search
{
"query": {
"has_child": {
"type": "branch",
"query": {
"has_child": {
"type": "employee",
"query": {
"match": {
"hobby": "hiking"
}
}
}
}
}
}
}

elasticsearch 父子关系的更多相关文章

  1. 读《深入理解Elasticsearch》点滴-对象类型、嵌套文档、父子关系

    一.对象类型 1.mapping定义文件 "title":{ "type":"text" }, "edition":{ ...

  2. elasticsearch 基础 —— Jion父子关系

    前言 由于ES6.X版本以后,每个索引下面只支持单一的类型(type),因此不再支持以下形式的父子关系: PUT /company { "mappings": { "br ...

  3. Logstash 父子关系 配置

    最近在使用Lostash的过程中遇到了一个问题:在一个log文件里包含两类数据,而且两类数据之间存在父子关系,那如何使用lostash的configuration实现这个需求呢 思路: 首先定义父事件 ...

  4. elasticsearch父子文档处理(join)

    elasticsearch父子文档处理 join 一.背景 二.需求 三.前置知识 四.实现步骤 1.创建 mapping 2.添加父文档数据 3.添加子文档 4.查询文档 1.根据父文档id查询它下 ...

  5. margin collapse 之父子关系的DIV

    打算花点时间将知识整理一下,虽然平时现用现查都能完成工作,可是当遇到面试这种事情的时候,临时查就来不及了... 关于margin,整理若干知识点如下: 一:父子关系的DIV标签以及未加margin时的 ...

  6. iOS 父子关系

    1.面向对象特征,类的继承 成员变量(实例变量) 子类继承父类所有功能,只能直接(访问)调用父类中的.h中的protect和public成员变量(实例变量)及方法, .h中的私有的成员变量,子类不能直 ...

  7. [转]NHibernate之旅(9):探索父子关系(一对多关系)

    本节内容 引入 NHibernate中的集合类型 建立父子关系 父子关联映射 结语 引入 通过前几篇文章的介绍,基本上了解了NHibernate,但是在NHibernate中映射关系是NHiberna ...

  8. Qt 对象间的父子关系

    C++中只要有一个new就必须要有一个delete与之对应 但是Qt中的对象之间有特殊的关系 Qt 对象间的父子关系 每一个对象都保存有它所有子对象的指针 每一个对象都有一个指向其父对象的指针 par ...

  9. MFC窗口的父子关系和层级关系

    一直对窗口之间的关系有些混乱,遇到需要指定父窗口的函数时常常要考虑很久,究竟父窗口是哪个窗口,遂上网查资料,略有所悟,简记如下: 对话框中的所有控件(比如Button等)都是其子窗口.        ...

随机推荐

  1. .bat 文件调用python脚本

    1.将clearlog.py 脚本放在指定目录 比如 我放在 C:\Users\Administrator\Desktop 上 也就是桌面上 2.创建一个.bat 位后缀名的脚本 3.写入如下脚本 @ ...

  2. Linux系统基于fork()新进程的创建

    作者:严哲璟 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 fork属于系 ...

  3. 1、pip不是内部运行程序 解决方法

    一.方式一 1.切换到pip所在路径: shit+ 右键. 再此处打开运行窗口 2.执行 pip install pytest 脚本即可. 二.方式二,添加环境变量 1.将pip所在的文件路径 添加到 ...

  4. delphi 打开和关闭外部exe

    一.打开外部exe 1.use文件-SHELLAPI 2.ShellExecute(handle,'open','E:\test.exe','-s','',SW_SHOWNORMAL); 二.关闭外部 ...

  5. flask入门到入土

    # Flask ## 0.Flask简介 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用 ...

  6. 比传统事务快10倍?一张图读懂阿里云全局事务服务GTS

    近日,阿里云全局事务服务GTS正式上线,为微服务架构中的分布式事务提供一站式解决方案.GTS的原理是将分布式事务与具体业务分离,在平台层面开发通用的事务中间件GTS,由事务中间件协调各服务的调用一致性 ...

  7. windows下用VMware虚拟机下安装Linux CentOS6.9图文教程

    首先,请在Windows7下安装VMware虚拟机,这个比较简单,直接从官网下载安装即可,这里不再叙述. 接着,从官网直接下载CentOS6.9的iso镜像文件,地址:https://www.cent ...

  8. Delphi界面篇之ListView控件

    //增加项或列(字段) ListView1.Clear; ListView1.Columns.Clear; ListView1.Columns.Add; ListView1.Columns.Add; ...

  9. [NOIP2017]逛公园 题解

    我连D1T3都不会我联赛完蛋了 题目描述 策策同学特别喜欢逛公园.公园可以看成一张 N 个点 M 条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口, N 号点是公园的出口,每条边有一个非负 ...

  10. [CSP-S模拟测试]:chemistry(期望DP+组合数学)

    题目传送门(内部题27) 输入格式 第一行有$4$个整数$n,k,p,q$.第二行有$n$个整数$a_i$.接下来有$n-1$行,每行有两个整数$u,v$,表示$u$与$v$之间通过化学单键连接. 输 ...