• 【Elasticsearch教程16】Mapping字段类型之join


    一、前言

    在日常开发中,常遇到父子关系的对象,它们通常是1:N的关系:

    • 一个parent可以有多个child
    • 一个child只能有一个parent

    如果我们用关系型数据库(如MySQL),我们还是挺熟悉如何设计表的。一个主表,一个子表,两个表可以用joinleft joinright join关联查询

    而在ES中,不能像MySQL那样设计表了,ES提供了2种表达父子关系的方案:

    • nested,子属于父的某一个nested字段里,子属于父的一部分,父和子在同一文档里。
    • join,子和父在同一index里,但不在同一文档里,父和子都是独立的文档。
    • join类型父子文档必须在同一分片上

    nested类型,之前的博客Mapping字段类型之nested已经讲过了,本篇博客主要讲join类型。

    二、join类型Mapping

    join类型是一种用来创建父子关系的特殊类型,且父子文档都存放在同一index里。
    父子关系,不能是多对多,也就是说一个父亲可以有多个儿子,但一个儿子只能有一个父亲。

    下面我要创建《王者荣耀》里两个阵营(group)以及其英雄(hero)的信息,举例说明join类型。

    • 稷下学院阵营:老夫子、墨子
    • 长安阵营:李白、李元芳
    • 阵营(group)是父文档,英雄(hero)是子文档
    • 请牢牢记住这边的grouphero,后面的查询都少不了它们
    IDname关系类型父ID
    group1稷下学院group
    group2长安group
    hero1老夫子herogroup1
    hero2墨子herogroup1
    hero3李白herogroup2
    hero4李元芳herogroup2
    PUT pigg_test_join
    {
      "mappings": {
        "properties": {
          "name": {			  	# 第1个字段叫name,是keyword类型
            "type": "keyword"
          },
          "join_field": {     	# 第2个字段叫join_field,是join类型
            "type": "join",
            "relations": {      # relations是固定关键字,表示父子关系
              "group": "hero"   # 冒号:的左边是父类型名称,右边是子类型名称
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    三、插入父和子文档

    1 父文档

    插入id=group1name=稷下学院的父文档:

    PUT pigg_test_join/_doc/group1
    {
      "name": "稷下学院",
      "join_field": {
        "name": "group"  # 指定文档为父类型group
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    插入id=group2name=长安的父文档,指定父子类型可以用如下简化写法:

    PUT pigg_test_join/_doc/group2
    {
      "name": "长安",
      "join_field": "group"  # 简化写法,指定文档为父类型group
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2 子文档

    • 父子文档必须在同一分片上,所以插入子文档时要指定routing为父ID
    • 创建、更新、删除、获取子文档时,都得加上routing参数
    • 子文档需要在关联字段里用parent指定父ID
    • 一个子只能有一个父亲

    插入稷下学院阵营的2个子文档

    PUT pigg_test_join/_doc/hero1?routing=group1  # 指定routing
    {
      "name": "老夫子",
      "join_field": {
        "name": "hero",  	# 指定该文档为子文档
        "parent": "group1"  # 指定父ID
      }
    }
    
    PUT pigg_test_join/_doc/hero2?routing=group1
    {
      "name": "墨子",
      "join_field": {
        "name": "hero", 
        "parent": "group1" 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    插入长安阵营的2个子文档

    PUT pigg_test_join/_doc/hero3?routing=group2
    {
      "name": "李白",
      "join_field": {
        "name": "hero", 
        "parent": "group2" 
      }
    }
    
    PUT pigg_test_join/_doc/hero4?routing=group2
    {
      "name": "李元芳",
      "join_field": {
        "name": "hero", 
        "parent": "group2" 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四、parent_id 根据父ID查询儿子

    • parent_id查询是根据父亲ID查询其儿子文档
    • parent_id.type是指定儿子文档的类型,因为子类型可以有多个,所以要指定下
    GET pigg_test_join/_search
    {
      "query": {
        "parent_id": {		# 关键字
          "type": "hero",   # 子文档的类型
          "id": "group1"    # 父ID
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面查询返回的是老夫子墨子这2个文档

    五、has_parent 根据父查子

    • 查询条件是描述父亲,返回的是子文档
    • has_parent是固定关键字
    • has_parent.parent_type是指定父类型
    • query查询条件体应该是对对父亲的描述

    比如查询name=长安的阵营有哪些英雄?

    GET pigg_test_join/_search
    {
      "query": {
        "has_parent": {
          "parent_type": "group",
          "query": {
            "term": {
              "name": "长安"
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面查询返回的是李元芳李白这两个文档

    六、has_child 根据子查父

    • 查询条件是描述儿子,返回的是父文档
    • has_child.type指定子类型
    • query查询体里面是描述儿子

    比如查询哪些阵营有姓李的英雄?

    GET pigg_test_join/_search
    {
      "query": {
        "has_child": {
          "type": "hero",
          "query": {
            "prefix": {
              "name": "李"
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    七、join VS nested

    nestedjoin
    优点父子文档存储一起,读取性能高父子文档可以独立更新
    缺点更新嵌套的子文档时,更新nested字段的全部文档需要额外的内存维护关系,读取性能相对差
    适用场景子文档偶尔更新,以查询为主子文档更新频繁

    在我实际工作中用nested更多些,join类型设计的相当复杂,对人员的水平要求较高些(你得足够细心),而且在父子文档分别存储时,我们也可以使用nested类型来维护父子关系,而不一定用join
    具体的可以参考我之前的博客:
    ES 存储树形结构 整合Spring Data Elasticsearch
    Elasticsearch工具类 支持树形结构

  • 相关阅读:
    HTML 超链接 a 标签
    基于Git和Nginx搭建自己的私人图床,告别图片404
    云原生-VMware虚拟机安装Kubesphere实战(一)
    数仓的字符截取三胞胎:substrb、substr、substring
    ENVI_IDL: 批量制作专题地图
    20-Redis哨兵和高可用、一致性Hash和ES的简单介绍
    基于图深度学习的自然语言处理方法和应用
    To enable Secure Boots and Flash Encryption using the ESP Flash download tool
    django的csrf跨站请求伪造
    docker删除镜像、容器命令
  • 原文地址:https://blog.csdn.net/winterking3/article/details/126637154