Elasticsearch Join字段类型简单快速上手教程

 更新时间:2022年09月20日 10:19:30   作者:醉鱼  
这篇文章主要为大家介绍了Elasticsearch Join字段类型简单快速上手教程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

阅读本文需要一定的Elasticsearch基础哦,本文深度有,但是不深

概述

Elasticsearch中Join数据类型的字段相信大家也都用过,也就是口中常谈的父子文档。在Elasticsearch中Join不能跨索引和分片,所以保存文档信息时要保证父子文档使用相同的路由参数来保证父文档与子文档保存在同一个索引的同一个分片,那么都有哪些限制呢?

父子关系的限制

  • 每个索引中只能有一个关系字段
  • 父文档与子文档必须在同一个索引分片中,所以我们在对父子文档增加、删除、修改时要设置路由值,保证数据都在同一分片
  • 一个父文档可以包含多个子文档,但是一个子文档只能有一个父文档
  • 只能在Join类型的字段上建立关系
  • 在保证当前文档是父文档的前提下可以增加子文档

Global ordinals

翻译过来就是全局序数。什么是全局序数呢,官方文档中说明了,这就是一个加速查询的一个东西,使用了全局序数之后可以让数据更紧凑;详细的就不展开了,后面有机会再详细说明一下全局序数,具体的目前可以查看一下官方文档

对于我们本章节内容来说,我们知道父子文档Join类型是使用全局序数来加速查询的就可以了。默认情况下,全局序数基本是实时构建的,当索引发生变化,全局序数会重新构建。这个过程会增加refresh的时间,当然这个配置也是可以关闭的,但是关闭之后会在我们接下来遇到的第一个父连接或者聚合的查询时重新构建全局序数,这样这一部分的时间就反馈给了用户,官方也是不建议我们这样做的,感觉对用户来说不是那么的友好,主要还是在一个权衡。最坏的情况就是同时有多个写入,也就是同时有多个全局序数需要重新构建,也就会造成在单个refresh的时间间隔内要重新构建多个全局序数

当然如果关联字段使用的不是很频繁并且写入事件很多,禁用掉是值得推荐的,禁用方式如下

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "join_field": {
        "type": "join",
        "relations": {
           "goods": ["details","evaluate"],
           "evaluate":"vote"
        },
        "eager_global_ordinals": false
      }
    }
  }
}

当然,对于全局序数占用的堆大小情况可以使用如下语句查看

# Per-index
GET my-index-000001/_stats/fielddata?human&fields=join_field#goods
# Per-node per-index
GET _nodes/stats/indices/fielddata?human&fields=join_field#goods

父子文档

首先我们还是创建一个正常的父子关系索引,商品作为父文档,详情作为子文档

DELETE my-index-000001
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "join_field": { 
        "type": "join",
        "relations": {
          "goods": "details" 
        }
      }
    }
  }
}
  • my-index-000001:索引名称
  • id:文档主键
  • join_field:父子关系字段,type标记为Join为父子文档
  • relations: 定义父子关系,goods为父文档类型名称,details为子文档类型名称,后面插入数据,查询都会使用

插入几条测试数据,商品有iphonmac,详情为颜色外观与内存配置等

PUT my-index-000001/_doc/1?refresh
{
  "id": "1",
  "text": "iphone 14 pro max",
  "join_field": {
    "name": "goods" 
  }
}
PUT my-index-000001/_doc/2?refresh
{
  "id": "2",
  "text": "macbook pro ",
  "join_field": {
    "name": "goods"
  }
}
PUT my-index-000001/_doc/3?routing=1&refresh 
{
  "id": "3",
  "text": "512G 16核",
  "join_field": {
    "name": "details", 
    "parent": "1" 
  }
}
PUT my-index-000001/_doc/4?routing=1&refresh
{
  "id": "4",
  "text": "粉/银/黑/抹茶绿",
  "join_field": {
    "name": "details",
    "parent": "1"
  }
}
PUT my-index-000001/_doc/5?routing=1&refresh 
{
  "id": "5",
  "text": "1T 32G",
  "join_field": {
    "name": "details", 
    "parent": "2" 
  }
}
PUT my-index-000001/_doc/6?routing=1&refresh
{
  "id": "6",
  "text": "银/黑",
  "join_field": {
    "name": "details",
    "parent": "2"
  }
}

使用parent_id查询父子文档,以上面插入的测试数据查询,查找mac的详情信息语句如下,前提是知道父文档的id

GET my-index-000001/_search
{
  "query": {
    "parent_id": {
      "type": "details",
      "id":"2"
    }
  },
  "sort":["id"]
}
  • 大部分情况上面是不能满足我们的查询请求的,所以我们还可以使用has_parent或者has_child查询

使用has_parent查询:父文档goods中所有包含macbook的子文档(后文的孙子文档也可以查询)

GET my-index-000001/_search
{
  "query": {
    "has_parent": {
      "parent_type": "goods",
      "query": {
        "match": {
          "text": "macbook"
        }
      }
    }
  }
}

使用hash_child查看details子文档中有1T关键字的所有父文档

GET my-index-000001/_search
{
  "query": {
    "has_child": {
      "type": "details",
      "query": {
        "match": {
          "text": "1T"
        }
      }
    }
  }
}

使用parent-join 查询或者聚合

Elasticsearch在使用Join类型数据类型时,会自动创建一个附加的字段,结构为Join的字段名加#号加父类型,以上文为例,创建一个附加字段(join_field#goods),如下是使用parent-join字段查询聚合的一个例子,参考自官网,应用了8.1版本的新特性运行时字段

GET my-index-000001/_search
{
  "query": {
    "parent_id": { 
      "type": "details",
      "id": "1"
    }
  },
  "aggs": {
    "parents": {
      "terms": {
        "field": "join_field#goods", 
        "size": 10
      }
    }
  },
  "runtime_mappings": {
    "my_parent_field": {
      "type": "long",
      "script": """
        emit(Integer.parseInt(doc['join_field#goods'].value)) 
      """
    }
  },
  "fields": [
    { "field": "my_parent_field" }
  ]
}

Join类型的父子文档,上面我们演示了一个父文档对应一种子文档类型的例子,Join类型也支持一个父类型有多个子类型,以上文为基础,加入下面语句测试

DELETE my-index-000001
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "join_field": { 
        "type": "join",
        "relations": {
          "goods": ["details","evaluate"] 
        }
      }
    }
  }
}
PUT my-index-000001/_doc/7?routing=1&refresh
{
  "id": "7",
  "text": "运行流程,无卡顿,待机时间长",
  "join_field": {
    "name": "evaluate",
    "parent": "1"
  }
}
PUT my-index-000001/_doc/8?routing=1&refresh
{
  "id": "8",
  "text": "体重轻,携带方便,编码利器",
  "join_field": {
    "name": "evaluate",
    "parent": "2"
  }
}
  • 同样的,细心的同学已经看到了,上文已经标记了孙子文档,对的,你没看错就是孙子文档,三级的层级,级别可以更深,但是Elasticsearch不建议很深的层次,毕竟Join很消耗性能的,层级再深点没法用了,下面就是多级别的语句测试,此时他们三者的关系就如下所示

DELETE my-index-000001
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "join_field": { 
        "type": "join",
        "relations": {
          "goods": ["details","evaluate"],
          "evaluate":"vote"
        }
      }
    }
  }
}
PUT my-index-000001/_doc/9?routing=1&refresh
{
  "id": "9",
  "text": "这是投票信息:我买iphone是因为性价比高,保值",
  "join_field": {
    "name": "vote",
    "parent": "1"
  }
}
PUT my-index-000001/_doc/10?routing=1&refresh
{
  "id": "10",
  "text": "这是投票信息:我买mac是因为轻,携带方便,没有流氓软件",
  "join_field": {
    "name": "vote",
    "parent": "2"
  }
}

总结

相信大家也看出来了,官方都不建议使用父子文档的,毕竟性能是一大问题,相信大家用Elasticsearch肯定大部分都是图速度快,用了Join字段变慢了,这谁能同意呢是吧,有利有弊吧,看大家选择,下一篇带给大家的算是Elasticsearch推荐Join字段替代类型Nested,更多关于Elasticsearch Join字段类型的资料请关注脚本之家其它相关文章!

相关文章

  • servlet过滤器(Filter)详解(九)

    servlet过滤器(Filter)详解(九)

    这篇文章主要为大家详细介绍了servlet过滤器Filter的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • SpringBoot对静态资源的映射规则详解解读

    SpringBoot对静态资源的映射规则详解解读

    这篇文章主要介绍了SpringBoot对静态资源的映射规则详解解读,在Spring Boot中,映射规则是用来定义URL与控制器方法之间的映射关系的,通过映射规则,可以将特定的URL请求映射到相应的控制器方法上,从而实现请求的处理和响应的返回,需要的朋友可以参考下
    2023-10-10
  • 解决jmap命令打印JVM堆信息异常的问题

    解决jmap命令打印JVM堆信息异常的问题

    这篇文章主要介绍了解决jmap命令打印JVM堆信息异常的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 基于java SSM springboot实现抗疫物质信息管理系统

    基于java SSM springboot实现抗疫物质信息管理系统

    这篇文章主要介绍了基于JAVA SSM springboot实现的抗疫物质信息管理系统,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • Java CompletableFuture 异步超时实现深入研究

    Java CompletableFuture 异步超时实现深入研究

    这篇文章主要为大家介绍了Java CompletableFuture 异步超时实现深入研究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的操作方法

    IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的操作方法

    这篇文章主要介绍了IntelliJ IDEA 编辑器的全局搜索中使用正则表达式的相关知识,补充介绍了IDEA查找和替换实用正则,感兴趣的朋友跟随小编一起看看吧
    2024-01-01
  • Java中多线程Reactor模式的实现

    Java中多线程Reactor模式的实现

    多线程Reactor模式旨在分配多个reactor每一个reactor独立拥有一个selector,本文就详细的来介绍一下Java中多线程Reactor模式的实现,需要的朋友可以参考下
    2021-12-12
  • Java三目运算中隐藏的自动拆装箱

    Java三目运算中隐藏的自动拆装箱

    这篇文章主要介绍了Java三目运算中隐藏的自动拆装箱,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-11-11
  • Java实现常用的三种加密算法详解

    Java实现常用的三种加密算法详解

    编程中常见的加密算法有以下几种:信息摘要算法、对称加密算法以及非对称加密算法。本文将利用Java实现这几种常见的加密算法,需要的可以参考一下
    2022-03-03
  • springboot验证码的生成与验证的两种方法

    springboot验证码的生成与验证的两种方法

    本文主要介绍了springboot验证码的生成与验证的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06

最新评论