代码编织梦想

原文地址:Elasticsearch6.X 新类型Join深入详解_铭毅天下的博客-CSDN博客_elasticsearch join

0、ES6.X 一对多、多对多的数据该如何存储和实现呢?
引出问题:

“某头条新闻APP”新闻内容和新闻评论是1对多的关系?

在ES6.X该如何存储、如何进行高效检索、聚合操作呢?

相信阅读本文,你就能得到答案!

1、ES6.X 新类型Join 产生背景
Mysql中多表关联,我们可以通过left join 或者Join等实现;

ES5.X版本,借助父子文档实现多表关联,类似数据库中Join的功能;实现的核心是借助于ES5.X支持1个索引(index)下多个类型(type)。

ES6.X版本,由于每个索引下面只支持单一的类型(type)。

所以,ES6.X版本如何实现Join成为大家关注的问题。

幸好,ES6.X新推出了Join类型,主要解决类似Mysql中多表关联的问题。

2、ES6.X Join类型介绍
仍然是一个索引下,借助父子关系,实现类似Mysql中多表关联的操作。

3、ES6.X Join类型实战
3.1 ES6.X Join类型 Mapping定义
Join类型的Mapping如下:

核心

1) "my_join_field"为join的名称。

2)“question”: “answer” 指:qustion为answer的父类。

PUT my_join_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": { 
          "type": "join",
          "relations": {
            "question": "answer" 
          }
        }
      }
    }
  }
}



3.2 ES6.X join类型定义父文档
直接上以下简化的形式,更好理解些。

如下,定义了两篇父文档。
文档类型为父类型:“question”。

PUT my_join_index/_doc/1?refresh
{
  "text": "This is a question",
  "my_join_field": "question" 
}

PUT my_join_index/_doc/2?refresh
{
  "text": "This is another question",
  "my_join_field": "question"
}

3.3 ES6.X join类型定义子文档
路由值是强制性的,因为父文件和子文件必须在相同的分片上建立索引。
"answer"是此子文档的加入名称。
指定此子文档的父文档ID:1。
PUT my_join_index/_doc/3?routing=1&refresh 
{
  "text": "This is an answer",
  "my_join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

PUT my_join_index/_doc/4?routing=1&refresh
{
  "text": "This is another answer",
  "my_join_field": {
    "name": "answer",
    "parent": "1"
  }
}


4、ES6.X Join类型约束
每个索引只允许一个Join类型Mapping定义;
父文档和子文档必须在同一个分片上编入索引;这意味着,当进行删除、更新、查找子文档时候需要提供相同的路由值。
一个文档可以有多个子文档,但只能有一个父文档。
可以为已经存在的Join类型添加新的关系。
当一个文档已经成为父文档后,可以为该文档添加子文档。
5、ES6.X Join类型检索与聚合
5.1 ES6.X Join全量检索
GET my_join_index/_search
{
  "query": {
    "match_all": {}
  },
  "sort": ["_id"]
}

返回结果如下:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 4,
    "max_score": null,
    "hits": [
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "1",
        "_score": null,
        "_source": {
          "text": "This is a question",
          "my_join_field": "question"
        },
        "sort": [
          "1"
        ]
      },
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "2",
        "_score": null,
        "_source": {
          "text": "This is another question",
          "my_join_field": "question"
        },
        "sort": [
          "2"
        ]
      },
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "3",
        "_score": null,
        "_routing": "1",
        "_source": {
          "text": "This is an answer",
          "my_join_field": {
            "name": "answer",
            "parent": "1"
          }
        },
        "sort": [
          "3"
        ]
      },
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "4",
        "_score": null,
        "_routing": "1",
        "_source": {
          "text": "This is another answer",
          "my_join_field": {
            "name": "answer",
            "parent": "1"
          }
        },
        "sort": [
          "4"
        ]
      }
    ]
  }
}





5.2 ES6.X 基于父文档查找子文档
GET my_join_index/_search
{
    "query": {
        "has_parent" : {
            "parent_type" : "question",
            "query" : {
                "match" : {
                    "text" : "This is"
                }
            }
        }
    }
}

返回结果:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "3",
        "_score": 1,
        "_routing": "1",
        "_source": {
          "text": "This is an answer",
          "my_join_field": {
            "name": "answer",
            "parent": "1"
          }
        }
      },
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "4",
        "_score": 1,
        "_routing": "1",
        "_source": {
          "text": "This is another answer",
          "my_join_field": {
            "name": "answer",
            "parent": "1"
          }
        }
      }
    ]
  }
}



5.3 ES6.X 基于子文档查找父文档
GET my_join_index/_search
{
"query": {
        "has_child" : {
            "type" : "answer",
            "query" : {
                "match" : {
                    "text" : "This is question"
                }
            }
        }
    }
}

返回结果:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "1",
        "_score": 1,
        "_source": {
          "text": "This is a question",
          "my_join_field": "question"
        }
      }
    ]
  }
}


5.4 ES6.X Join聚合操作实战
以下操作含义如下:

1)parent_id是特定的检索方式,用于检索属于特定父文档id=1的,子文档类型为answer的文档的个数。
2)基于父文档类型question进行聚合;
3)基于指定的field处理。
 


返回结果:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.13353139,
    "hits": [
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "3",
        "_score": 0.13353139,
        "_routing": "1",
        "fields": {
          "parent": [
            "1"
          ]
        }
      },
      {
        "_index": "my_join_index",
        "_type": "_doc",
        "_id": "4",
        "_score": 0.13353139,
        "_routing": "1",
        "fields": {
          "parent": [
            "1"
          ]
        }
      }
    ]
  },
  "aggregations": {
    "parents": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "1",
          "doc_count": 2
        }
      ]
    }
  }
}


6、ES6.X Join 一对多实战
6.1 一对多定义
如下,一个父文档question与多个子文档answer,comment的映射定义。

PUT join_ext_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": {
          "type": "join",
          "relations": {
            "question": ["answer", "comment"]  
          }
        }
      }
    }
  }
}

6.2 一对多对多定义
实现如下图的祖孙三代关联关系的定义。

question
    /    \
   /      \
comment  answer
           |
           |
          vote

PUT join_multi_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "my_join_field": {
          "type": "join",
          "relations": {
            "question": ["answer", "comment"],  
            "answer": "vote" 
          }
        }
      }
    }
  }
}

孙子文档导入数据,如下所示:

PUT join_multi_index/_doc/3?routing=1&refresh 
{
  "text": "This is a vote",
  "my_join_field": {
    "name": "vote",
    "parent": "2" 
  }
}

注意:

- 孙子文档所在分片必须与其父母和祖父母相同
- 孙子文档的父代号(必须指向其父亲answer文档)

7、小结
虽然ES官方文档已经很详细了,详见:
http://t.cn/RnBBLgp

但手敲一遍,翻译一遍,的的确确会更新认知,加深理解。

和你一起,死磕ELK Stack!
 

18.elasticsearch6.x 新类型join深入详解_zkf541076398的博客-爱代码爱编程

0、ES6.X 一对多、多对多的数据该如何存储和实现呢? 引出问题: “某头条新闻APP”新闻内容和新闻评论是1对多的关系? 在ES6.X该如何存储、如何进行高效检索、聚合操作呢? 相信阅读本文,你就能得到答案! 1、ES6.X 新类型Join 产生背景 Mysql中多表关联,我们可以通过left join 或者Join等实现;ES5.X版本,借助

es6 object.assgin()详解_garrettzxd的博客-爱代码爱编程_assgin

关于assgin()基本的这里就不讲解了,详细请参考阮一峰老师的ES6教程Object.assgin()文档 大致是合并对象所用,但是合并有一下几点需要注意 1.只有一个参数时返回原对象本身 具体请参考如下代码 let source = {a:1};     //原对象 let target = {};        //目标对象 target = Ob

elasticsearch前沿:es 5.x改进详解与es6展望_dragonwar%的博客-爱代码爱编程

曾勇(Medcl),Elastic 工程师与布道师,2015 年加入 Elastic 公司。加入 Elastic 之前,在搜索和运维等方面积累了超过七年的经验。Elasticsearch 国内首批用户,自 2010 年起就开始接触 Elasticsearch,是 ES 中文社区发起人,也是 Elastic 在中国的首位员工。 我最早是

elasticsearch6 x 新类型join深入详解_两个白杯子的博客-爱代码爱编程

                        0、ES6.X 一对多、多对多的数据该如何存储和实现呢? 引出问题:   “某头条新闻APP”新闻内容和新闻评论是1对多的关系?    在ES6.X该如何存储、如何进行高效检索、聚合操作呢? 相信阅读本文,你就能得到答案! 1、ES6.X 新类型Join 产生背景 Mysql中多表关联,我们可以通过

webpack 4.x.x 搭建项目完整详解步骤-爱代码爱编程

前言 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 从 webpack v4.0.0 开始

详解es6 扩展运算符(...)的各种妙用-爱代码爱编程

数组的扩展运算符 将数组变成参数化序列 function plus(x, y) { return x + y; } const numbers = [1, 2]; console.log(...numbers) // 1 2 console.log(plus(...numbers)) //3 复制数组 // ES5 const a1 = [

Elasticsearch 6.X 新类型Join深入详解-爱代码爱编程

0、ES6.X 一对多、多对多的数据该如何存储和实现呢? 引出问题: “某头条新闻APP”新闻内容和新闻评论是1对多的关系? 在ES6.X该如何存储、如何进行高效检索、聚合操作呢? 相信阅读本文,你就能得到答案! 1、ES6.X 新类型Join 产生背景 Mysql中多表关联,我们可以通过left join 或者Join等实现;

学习ES6(十七) -- Arrays 详解-爱代码爱编程

数组是值的同质集合。为简化起见,数组是相同数据类型的值的集合。它是用户定义的类型。 数组的特征 数组声明分配顺序的内存块。 数组是静态的。这意味着数组一旦初始化就无法调整大小。 每个存储块代表一个数组元素。 数组元素由唯一的整数(称为元素的下标/索引)标识。 像变量一样,数组也应在使用前声明。 数组初始化是指填充数组元素。 数组元素值可以

ElasticSearch6.X Join(父子查询)深入详解-爱代码爱编程

0、ES6.X 一对多、多对多的数据该如何存储和实现呢? 引出问题: “某头条新闻APP”新闻内容和新闻评论是1对多的关系? 在ES6.X该如何存储、如何进行高效检索、聚合操作呢? 相信阅读本文,你就能得到答案! 1、ES6.X 新类型Join 产生背景 Mysql中多表关联,我们可以通过left join 或者Join等实现;

ES6--Iterator和for......of详解-爱代码爱编程

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。 或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。 默认了部署了Iterator的数据有: Array Map Set String 类数组对象 TypedArray arguments Node

ES6黄金搭档Proxy与Reflect,详解Proxy-爱代码爱编程

黄金搭档Proxy与Reflect 鼎鼎大名Proxy的出现解决了对属性拦截的全方位操作,Reflect又是对Object上的API一次优雅的实现,当两者在又会有怎样的神奇效果呢?或许可以解决令你头疼的JS需求实现。 引用一下之前提到的观察者模式: 关于上面代码的解析,请看这里 寥寥几行代码就可以实现一个简易的观察者模式,这还不够嘛。 实际项目中面对复

【ES6基础】Array详解-爱代码爱编程

除了Object,Array应该就是ECMAScript中最常用的类型了。ECMAScript 数组与其他语言相同,是一组有序数据,不同的是数组的每个槽位可以存储任意类型的数据。ECMAScript 数组是动态大小的,会随着数据添加而自动增长。 文章目录 1. 创建数组from()of()2. 数组空位3. 数组索引4. 检测数组5.

vue中vue.config.js文件配置详解_蜡笔大帅的博客-爱代码爱编程

// const path = require('path'); module.exports = { /** 区分打包环境与开发环境 * process.env.NODE_ENV==='production'

【vue.js】使用高德地图选择省市区后,再点击确认当前选择的位置_萌村村花杨小花的博客-爱代码爱编程

成品展示 前期准备 先去高德开放平台申请一个web端的key。 2022年后申请的key,必须和它生成的secret一起使用。 可使用服务选择web端 在vue项目中,可以通过直接引入js文件,也可以安装vu

【c++】noexcept关键字_long~w的博客-爱代码爱编程

该关键字告诉编译器,函数中不会发生异常,这有利于编译器对程序做更多的优化。 如果在运行时,noexecpt函数向外抛出了异常(如果函数内部捕捉了异常并完成处理,这种情况不算抛出异常),程序会直接终止,调用std::term

[前端基础] javascript 进阶篇-爱代码爱编程

封装 Ajax ajax 可以无需刷新页面与服务器进行通讯,允许根据用户事件来更新部分页面内容。 readyStatus 的值: 0:未初始化1:启动;已经调用open()方法,但尚未调用send()方法。2:发送;已