【ElasticSearch系列-08】ElasticSearch处理对象间的关联关系

ElasticSearch系列整体栏目


内容链接地址
【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827
【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631
【三】ElasticSearch的高级查询Query DSLhttps://blog.csdn.net/zhenghuishengq/article/details/134159587
【四】ElasticSearch的聚合查询操作https://blog.csdn.net/zhenghuishengq/article/details/134159587
【五】SpringBoot整合elasticSearchhttps://blog.csdn.net/zhenghuishengq/article/details/134212200
【六】Es集群架构的搭建以及集群的核心概念https://blog.csdn.net/zhenghuishengq/article/details/134258577
【七】ES的开发场景和索引分片的设置及优化https://blog.csdn.net/zhenghuishengq/article/details/134302130
【八】ElasticSearch处理对象间的关联关系https://blog.csdn.net/zhenghuishengq/article/details/134327295

Es处理对象间的关联关系

  • 一,Es处理对象间的关联关系
    • 1,对象类型
      • 1.1,对象类型的kibana操作
      • 1.2,对象类型的java操作
    • 2,嵌套类型
      • 2.1,嵌套类型的kibana操作
      • 2.2,嵌套类型的java操作
    • 3,父子类型类型
      • 3.1,父子类型的kibana操作
      • 3.2,父子类型的java代码

一,Es处理对象间的关联关系

es属于是nosql类型的非关系型数据库,而在处理关联关系时,往往是不擅长处理这种关联关系的,不像mysql,通过范式化来处理这种关联关系。

范式化有利于减少数据的冗余,减少数据库的空间,让整体维护更加简单,但是在查询时需要多步查询,join联表查询也会让整个查询时间增加;反范式需要将数据冗余,不需要考虑关联关系,也无需进行这些join的操作,在读取数据时性能更高,但是缺点也很明显,在修改数据时是比较麻烦的,可能就是因为一个字段的修改,就可能会引起多条数据的修改。

在ElasticSearch中,主要也是考虑这种非关系型数据库的走向,内部主要有四种方法处理这种关联数据的场景,分别是对象类型、嵌套类型、父子关系类型、应用端关联

1,对象类型

1.1,对象类型的kibana操作

如在文档中包含对象的数据类型,举例如下,在article文章的索引中,里面有一个属性为一个对象属性user,就是每一篇文章中都包含着一个user用户的信息,创建索引的语句如下

PUT /article
{"mappings": {"properties": {"title":{"type":"text"},"createTime":{"type": "date"},"user":{"properties": {"username":{"type":"keyword"},"age":{"type":"long"},"sex":{"type":"text"}}}}}
}

随后往这个索引中插入一条数据,并且设置用户的信息

PUT /article/_doc/1
{"title":"ElasticSearch学习","createTime":"2023-11-09T00:00:00","user":{"username":"zhenghuisheng","age":"18","sex":"男"}
}

用户的查询如下,通过用户名进行查询,这里可以直接通过 对象.属性 的方式进行数据查询

GET /article/_search
{"query": {"match": {"user.username": "zhenghuisheng"}}
}

1.2,对象类型的java操作

在创建索引之前,需要通过配置获取es的连接,其配置类如下

@Bean
public RestHighLevelClient esRestClient(){RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("xxx.33.xxx.xxx", 9200, "http")));return  client;
}

创建articl索引和插入数据的java代码如下,里面的client参数为springboot整合篇的数据

//插入数据
IndexRequest userIndex = new IndexRequest("article");
User user = new User();
user.setUsername("zhenghuisheng");
user.setAge(18);
user.setSex("男");
//添加数据
userIndex.source(JSON.toJSONString(user), XContentType.JSON);
//client为前面springBoot整合的客户端,通过resource导入
client.index(userIndex, ElasticSearchConfig.COMMON_OPTIONS);

查询数据的方式如下,在设置这个字段时,即使是子字段,也可以直接通过拼接的方式设置即可user.username

SearchRequest request = new SearchRequest("article");
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchQuery("user.username","zhenghuisheng"));
request.source(builder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
System.out.println(search);

2,嵌套类型

2.1,嵌套类型的kibana操作

嵌套对象指的是对象数组的对象可以被独立索引。就是说在es内部会将数据进行分词操作,但是在查询时,可能就是会因这个操作,导致将不正确的数据查询出来,如英文名字,有firstName和lastName,但是因为组合问题,将不必要的字段查询出来,如 zhan san、li si 但是在查询zhan si的时候,是会将这两条数据查询出来的,按理是不存在的.

为了解决这个嵌套类型的问题,可以通过关键字 nested 类型来解决,这个底层就是会将文档保存在两个索引库中,在做查询时,就会有一个join的连接查询

如下面的案例,先创建一个索引数据,依旧是创建一个文章的索引,然后内部有一个author作者的信息

PUT /article
{"mappings": {"properties": {"title":{"type": "text"},"author":{"type": "nested","properties": {"first_name":{"type":"keyword"},"last_name":{"type":"keyword"}}}}}
}

往这个文档中插入一条数据,如下,里面的作者有两个,以数组的形式存储

POST /article/_doc/1
{"title":"ElasticSearch教学","author":[{"first_name":"zheng","last_name":"huisheng"},{"first_name":"li","last_name":"si"}]
}

那么在查询的时候,只需要用nested 进行数据查询即可,后面的path路径就是对应的需要查询的对象,这样在查询时,就不会将不需要的数据给查询出来

GET /article/_search
{"query": {"nested": {				//固定搭配,可以直接进紧跟在query后面"path": "author","query": {"bool": {"must": [{"match": {"author.first_name": "zheng"}},{"match": {"author.last_name": "si"}}]}}}}
}

在聚合查询时,也需要指定这个nested这个属性值,设置路径为查询的对象

GET /article/_search
{"aggs": {"author": {"nested": {"path":"author"}}}
}

2.2,嵌套类型的java操作

嵌套类型对应的java代码如下,首先先创建一个索引,对参数进行设置

    @Testpublic void createIndex() throws Exception{XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").startObject("title").field("type","text").endObject().startObject("author").field("type","nested").startObject("properties").startObject("first_name").field("type","keyword").endObject().startObject("last_name").field("type","keyword").endObject().endObject().endObject().endObject().endObject();CreateIndexRequest request = new CreateIndexRequest("article").settings(Settings.builder().put("number_of_shards", 3)		//设置分片数.put("number_of_replicas", 1)	//设置副本数.build()).mapping(mapping);//执行创建CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);System.out.println("执行结果为" + response);}

插入数据的方式直接如下,和上面用kibana操作的一样,插入两条数据

//插入数据
IndexRequest userIndex = new IndexRequest("article");
List<Author> list = new ArrayList<>();
Author author1 = new Author();
author1.setFirstName("zheng");
author1.setLastName("huisheng");
Author author2 = new Author();
author2.setFirstName("li");
author2.setLastName("si");
list.add(author1);
list.add(author2);
Article article = new Article();
article.setTitle("ElasticSearch教学");
article.setAuthor(list);
//添加数据
userIndex.source(JSON.toJSONString(article), XContentType.JSON);
client.index(userIndex, ElasticSearchConfig.COMMON_OPTIONS);

接下来就是查询数据,直接通过构建这个NestedQueryBuilder即可

//查询数据
SearchRequest request = new SearchRequest("article");
String path = "author";
QueryBuilder builder =new NestedQueryBuilder(path, QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("author.first_name", "zheng")).must(QueryBuilders.matchQuery("author.last_name","si")), ScoreMode.None);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(builder);
request.source(searchSourceBuilder);
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
System.out.println(search);

3,父子类型类型

3.1,父子类型的kibana操作

在ElasticSearch中,也存在着父子关系的文档,其内部通过join的方式连接父子文档的关系,并且在es中,实现了父文档和子文档之间的独立,也就是说当某一个父文档要更新时,可以不需要修改子文档的数据,而使用最上面的对象类型,就是只要某一些数据更新,就可能引起大范围数据的更新。通过文档独立的方式,单个文档间的操作不会互相影响。

但是也有一个缺点,就是大数据的联表,其效率肯定是不高的,但是优点是在更新时效率会更高。

在使用这个父子关系文档时,需要在建立索引的时候,确定父索引和子索引之间的关系。如下,需要通过关键字join来表明是连接关系,在relations中,设置key为父索引的名称,value为子索引名称

"teacher_student_relation": {"type": "join",	//指明类型"relations": {	//确认关系"teacher": "student"	//teacher为父文档、student为子文档}
}

如创建一个用户索引,分别对应的是student学生和teacher老师的信息,设置分片数为3,teacher为父文档,student为子文档

PUT /user
{"settings": {"number_of_shards": 3},"mappings": {"properties": {"relation": {"type": "join","relations": {"teacher": "student"}},"username": {"type": "keyword"},"sex": {"type": "text"}}}
}

接下来往父文档中插入一条数据,依旧需要 relation 这个属性,并且表名为teacher父文档

PUT /user/_doc/1
{"username":"Tom","sex":"男","relation":{"name":"teacher"	//表明为父文档}
}

接下来指定子文档,为了解决这个join查询的性能,需要通过routing路由功能,让子文档和父文档路由到相同的分片上面,其次就是也需要指定这个父子文档的属性,除了设置子文档的名称之外,还需要指定父文档的名称

PUT /user/_doc/student1?routing=1
{"username":"zhenghuisheng","sex":"男","relation":{"name":"student","parent":"teacher"}
}

那么一下就是一些查询的方式,如通过id的方式查询如下

GET /user/_doc/1		//根据父文档id查询
GET /user/_doc/student1?routing=1		//通过子文档查询

还可以查询子文档中是否包含某些数据,里面需要注意使用的是 has_child ,并且为类型为type

//查询子文档中,是否包含某些数据
GET /user/_search
{"query": {"has_child": {"type": "student","query": {"match": {"username": "zhenghusiheng"}}}}
}

同时也存在查询父文档中,是否包含某些数据,这里需要使用 has_parent ,类型为parent_type

GET /user/_search
{"query": {"has_parent": {"parent_type": "teacher","query": {"match": {"username": "Tom"}}}}
}

3.2,父子类型的java代码

首先也是创建索引,设置副本信息这些

XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").startObject("teacher_student_relation").field("type","join").startObject("relations").field("teacher","student").endObject().endObject().startObject("username").field("type","keyword").endObject().startObject("sex").field("type","text").endObject().endObject().endObject();CreateIndexRequest request = new CreateIndexRequest("user").settings(Settings.builder().put("number_of_shards", 3).put("number_of_replicas", 1).build()).mapping(mapping);CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);System.out.println("执行结果为" + response);

随后也是插入数据,首先是父文档插入数据,需要设置索引名,并且最好设置文档id,后面子文档查询时,也是需要通过routing路由指定和父文档一样的id,所以最好自己指定

//指定索引和路由
IndexRequest request = new IndexRequest("user");
request.id("teacher1");
User user = new User();
user.setUsername("Tom");
user.setSex("男");
Relation relation = new Relation();
relation.setName("teacher");
user.setRelation(relation);
request.source(JSON.toJSONString(user), XContentType.JSON);
IndexResponse response = client.index(request, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println(response);

随后是插入子文档的数据,需要指定路由,可以通过该路由使得子文档数据和父文档数据再一个分片上,这样有利于提升join的关联查询。除此之外,还需要设置这个parent的值

//指定索引和路由
IndexRequest request = new IndexRequest("user").routing("teacher1");
User user = new User();
user.setUsername("zhenghuisheng");
user.setSex("男");
Relation relation = new Relation();
relation.setName("student");
relation.setParent("teacher");
user.setRelation(relation);
request.source(JSON.toJSONString(user), XContentType.JSON);
IndexResponse response = client.index(request, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println(response);

嵌套文档和父子文档的主要区别如下:嵌套文档通过nested文档实现,其文档都是以冗余的方式存储在一起,其读取数据的性能相对较高,但是更新性能低;父子文档通过join的方式实现,父子文档数据独立,但是需要额外维护父子关系,读取数据的性能相对来说比较差

嵌套文档的场景主要适用于以查询为主的数据,更新的数据比较少;父子文档的场景主要在于子文档可能会出现频繁更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/141646.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

github私有仓库开发,公开仓库发布版本

文章目录 github私有仓库开发,公开仓库发布版本需求背景实现思路GitHub Releases具体步骤广告 github私有仓库开发,公开仓库发布版本 需求背景 github私有仓库开发,公开仓库发布版本&#xff0c;既可以保护源代码,又可以发布版本给用户使用。许多知名软件项目都采用了这样的开…

K8s-Traefik Ingress Controller

Traefik Ingress Controller Traefik 是一个为了让部署微服务更加便捷而诞生的现代 HTTP 反向代理、负载均衡工具。traefik 本身设计的就能够实时跟 kubernetes api 交互&#xff0c;感知后端 service&#xff0c;pod 等的变化&#xff0c;自动更新配置并重载。 traefik 是一…

多机器人群体的任务状态与机器人状态同步设计思路

背景技术 近年来&#xff0c;随着科学技术的发展需要&#xff0c;机器人技术不断进步。面临任务的日益复杂化&#xff0c;单机器人在很多环境下已经无法满足生产要求&#xff0c;于是国内外科研工作者对多机器人技术投入了大量关注&#xff0c;提出了利用多机器人协作来代替单机…

拍摄视频的时候相机断电导致视频文件损坏,怎么修复

3-4 现在好多人都有自己的相机&#xff0c;但是专业用来录像的机器应该是不太可能都有的&#xff0c;相机的稳定性会比专业的机器差一些&#xff0c;如果用于比较重要的场景&#xff0c;比如婚庆、会议录像、家庭录像使用等&#xff0c;有较少的概率会出现一些奇怪的情况&…

YOLOv8-Seg改进:卷积变体系列篇 | DCNv3可形变卷积基于DCNv2优化 | CVPR2023

🚀🚀🚀本文改进:DCNv3算子,基于DCNv2算子引入共享投射权重、多组机制和采样点调制,引入到YOLOv8,与C2f结合实现二次创新; 🚀🚀🚀DCNv3 亲测在多个数据集能够实现涨点,同样适用于小目标分割 🚀🚀🚀YOLOv8-seg创新专栏:http://t.csdnimg.cn/KLSdv 学姐…

建造者模式 ( Builder Pattern )(4)

建造者模式 ( Builder Pattern ) 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 建造者模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 与工厂模式的区别是&#xff1a;建造者模式更加关注与零件装配…

odoo16前端框架源码阅读——启动、菜单、动作

odoo16前端框架源码阅读——启动、菜单、动作 目录&#xff1a;addons/web/static/src 1、main.js odoo实际上是一个单页应用&#xff0c;从名字看&#xff0c;这是前端的入口文件&#xff0c;文件内容也很简单。 /** odoo-module **/import { startWebClient } from "…

一个关于jdbc操作mysql和java基础练手的通讯录管理系统小项目

首先 : 整个项目的项目结构为 : 1.第一步先导入数据库的驱动&#xff0c;我的mysql数据库是8.0以上版本&#xff0c;然后导入的驱动就是8.0.16版本的jar包&#xff1b; 1.JdbcBase : JDBC基础操作封装成了JdbcBase类,在里面先静态定义了数据库连接对象和DQL查询结果&#x…

CoreByte多云管理平台,国际站自助注册及充值教程

国际云的免实名备案优势为企业及个人提供灵活、可选择的云服务器资源&#xff0c;全球性的覆盖和高度可定制化的服务&#xff0c;使企业和个人可以在全球范围内高效运营&#xff0c;CoreByte独家签署多家云厂商&#xff0c;包含&#xff1a;阿里云、华为云、腾讯云、AWS等&…

Linux 程序开发流程 / 基本开发工具 / Vim / GCC工具链 / Make 工具 / Makefile 模板

编辑整理 by Staok。 本文部分内容摘自 “100ask imx6ull” 开发板的配套资料&#xff08;如 百问网的《嵌入式Linux应用开发完全手册》&#xff0c;在 百问网 imx6ull pro 开发板 页面 中的《2.1 100ASK_IMX6ULL_PRO&#xff1a;开发板资料》或《2.2 全系列Linux教程&#xf…

android 10车载桌面ActivityView触摸事件源码详解分析

hi&#xff0c;粉丝朋友们&#xff1a; 背景 大家好&#xff01;近来居然有好几个粉丝朋友居然问到了一个虚拟屏幕触摸相关的问题&#xff0c;还有老版本android 10上面有个车载桌面使用的ActivityView可以正常触摸的问题。 其实这个ActivityView在最新的版本已经没有了&…

函数模板:C++的神奇之处之一

引言&#xff1a; C函数模板是一种非常强大的编程技术&#xff0c;可以实现通用的算法和数据结构&#xff0c;提高代码的重用性和可维护性。本文将介绍C函数模板的基本概念、语法和使用方法&#xff0c;帮助开发者更好地理解和应用函数模板。 正文&#xff1a; 函数模板的概念…

Node.js 框架 star 星数量排名——NestJs跃居第二

文章目录 什么是NodeJs?什么是NodeJs框架?图表数据框架排名 什么是NodeJs? Node.js是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;它使得我们可以在服务器端使用JavaScript开发高效、可扩展的应用程序。作为一个快速、轻量级的平台&#xff0c;Node.js在Web开发领…

企业计算机中了eking勒索病毒如何解毒,eking勒索病毒文件恢复

网络技术的不断发展&#xff0c;为企业的生产生活提供了极大便利&#xff0c;但随之而来的网络安全威胁也不断增加&#xff0c;近期&#xff0c;很多企业的计算机服务器遭到了eking勒索病毒攻击&#xff0c;导致企业的计算机服务器所有数据被加密&#xff0c;无法正常使用&…

深眸科技聚焦3D机器视觉技术,从技术形态到应用前景实现详细分析

机器视觉技术的不断升级&#xff0c;使得对二维图像的处理逐渐扩展到了更复杂的三维领域&#xff0c;形成了3D机器视觉。3D机器视觉是机器视觉的重要应用领域之一&#xff0c;通过计算机能够在短时间内处理视觉传感器采集的图像信号&#xff0c;从而获得目标对象的三维信息。 …

playwright在vscode+jupyter中出现NotImplementedError问题

近期因个人需要接触playwright&#xff0c;由于playwright新接触&#xff0c;想用jupyter进行API测试学习。刚开始使用sync_playwright&#xff0c;在playwright的Conda运行环境中&#xff0c;以console模式和单文件直接运行模式&#xff0c;都能正常运行。但是进入jupyter中后…

C++ 11 新特性

目录 1. 支持特性的编译器版本2. 模板表达式中空格3. 空指针4. auto5. 统一初始化6. explict7. 范围for8. default&#xff0c;delete9. 化名模板&#xff08;alias template&#xff09;10. using11. noexcept12. override13. final14. decltype15. lambda16. Variadic Templa…

SpringBoot整合Activiti7——定时器事件(九)

文章目录 定时器事件时间定义时间固定时间段时间周期 1.开始事件2.中间事件3.边界事件代码实现xml文件自定义服务任务监听器自定义用户任务监听器测试流程流程执行步骤 定时器事件 可以用在开始事件、中间事件、边界事件上&#xff0c;边界事件可以是中断和非中断边界事件 需要…

基于springboot实现小学家校一体“作业帮”系统项目【项目源码】计算机毕业设计

基于springboot实现小学家校一体“作业帮”系统演示 Java语言简介 Java是由SUN公司推出&#xff0c;该公司于2010年被oracle公司收购。Java本是印度尼西亚的一个叫做爪洼岛的英文名称&#xff0c;也因此得来java是一杯正冒着热气咖啡的标识。Java语言在移动互联网的大背景下具…

Scala---数据基础

一、数据类型 二、变量和常量的声明 定义变量或者常量的时候&#xff0c;也可以写上返回的类型&#xff0c;一般省略&#xff0c;如&#xff1a;val a:Int 10常量不可再赋值 1./** 2. * 定义变量和常量 3. * 变量 :用 var 定义 &#xff0c;可修改 4. * 常量 :用 val 定…