ElasticSearch文档数据关联关系处理

news2024/11/26 22:46:48

文章目录

    • ES如何处理关联关系
    • 对象类型
      • 案例一 适用场景
      • 案例二 不适用场景
    • 嵌套对象nested object
    • 父子关联关系
    • 嵌套文档 VS 父子关系

ES如何处理关联关系

关系型数据库中的范式化:

  • 减少了数据冗余,节省了磁盘空间
  • 减少了不必要的更新操作,因为没有了数据冗余,我更新一个地方的数据就可以了,不用去更新冗余数据
  • 单查询需要join更多的表,范式简化了更新,读取操作可能更多。

反范式化:

  • 不使用join关联关系,而是在文档中保存冗余数据
  • 读取的性能更好,并且ES通过压缩_source字段减少所占磁盘空间
  • 不适合频繁更新的场景

ElasticSearch就是使用的反范式。ES并不擅长处理关联关系,如果出现了一般会采取以下几种方式:

  • 对象类型
  • 嵌套对象
  • 父子关联关系
  • 应用层面自己处理



对象类型

文档中使用一个字段,该字段保存的值value是一个object。这种方式不适用于 value为对象数组的场景,查询会不准确。

如下所示,案例一是适用场景,案例二为不适用场景

案例一 适用场景

博客作者信息变更

对象类型:

  • 在每一博客的文档中都保留作者的信息
  • 如果作者信息发生变化,需要修改相关的博客文档
DELETE /blog
# 创建一个blog索引,其中的user字段保存的是一个object
PUT /blog
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text"
      },
      "create_time": {
        "type": "date",
        "format": ["yyyy-MM-dd HH:mm:ss"]
      },
      "user": {				# user字段中保存的是一个对象,通过properties关键字定义对象中的字段
        "properties": {
          "userid": {
            "type": "long"
          },
          "username": {
            "type": "text"
          },
          "age": {
            "type": "long"
          }
        }
      }
    }
  }
}

# 插入一条 blog信息
PUT /blog/_doc/1
{
  "content": "I like Elasticsearch",
  "create_time": "2024-01-01 00:00:00",
  "user": {			# user字段中保存的是一个对象
    "userid": 1,
    "username": "hushang",
    "age": 24
  }
}

# 查询,可以使用使用user.field 对象内的字段查询
GET /blog/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "content": "Elasticsearch" } },
        { "match": { "user.username": "hushang" } }
      ]
    }
  }
}



案例二 不适用场景

user字段如果保存的是一个对象数组,在搜索时添加两个查询条件,数组中两个对象分别满足一个条件。

我使用bool must 关键字,表示两个查询条件都需要满足,才会显示文档。但是上方中数组中两个对象分别满足一个条件 这也查询出来了。

DELETE /blog
# 创建一个blog索引,其中的user字段保存的是一个object
PUT /blog
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text"
      },
      "user": {			#  user字段保存 姓 和 名 两个字段
        "properties": {
          "first_name": {
            "type": "text"
          },
          "last_name": {
            "type": "text"
          }
        }
      }
    }
  }
}

# 写入一条数据  张三和李四
PUT /blog/_doc/1
{
  "content": "speed",
  "user": [
    {
      "first_name":"zhang",
      "last_name":"san"
    },

    {
      "first_name":"li",
      "last_name":"si"
    }
    ]
}

# 查询,此时我的性查询的是zhang  名查询的是si    而且还是采用的bool must方式。我期望的结果是应该查询不到数据
# 但实际上此时能查询到数据
GET /blog/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first_name": "zhang" } },
        { "match": { "user.last_name": "si" } }
      ]
    }
  }
}

在这里插入图片描述



造成这种情况的原因是

ES在存储文档数据时,内部对象的边界并没有考虑在内,JSON格式被处理成扁平式键值对结构。当对多个字段进行查询时,导致了意外结果。

"content":"speed"
"user".first_name: ["zhang","li"]
"user".last_name: ["san","si"]

可以使用nested data type查询解决这个问题



嵌套对象nested object

  • nested数据类型,它允许对象数组中的对象被独立索引

  • 使用nested和properties关键字,将上方案例中所有user索引到多个分隔的文档。

  • 在内部,Nested文档会被保存在两个Lucene文档中,在查询时做join处理

DELETE /blog
# 创建一个blog索引,其中的user字段保存的是一个object
# 并且使用了type:nested
PUT /blog
{
  "mappings": {
    "properties": {
      "content": {
        "type": "text"
      },
      "user": {
        "type": "nested", 		# 使用了type:nested
        "properties": {
          "first_name": {
            "type": "keyword"
          },
          "last_name": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

# 写入一条数据  张三和李四
PUT /blog/_doc/1
{
  "content": "speed",
  "user": [
    {
      "first_name":"zhang",
      "last_name":"san"
    },

    {
      "first_name":"li",
      "last_name":"si"
    }
    ]
}

# 查询
GET /blog/_search
{
  "query": {
    "nested": {			# 使用nested关键字
      "path": "user",	# user字段是nested类型  这里指定nestred类型的字段,并且下面就是对这个字段进行查询
      "query": {		# 之后就是正常的查询query语句
        "bool": {
          "must": [
            { "match": { "user.first_name": "zhang" } },
            { "match": { "user.last_name": "si" } }
          ]
        }
      }
    }
  }
}

在这里插入图片描述



nested类型的字段,直接进行aggs聚合操作是没有数据的

# user字段是nested类型 直接聚合操作是没有数据的
GET /blog/_search
{
  "size": 0, 
  "aggs": {
    "hs_first_name": {
      "terms": {
        "field": "user.first_name"
      }
    }
  }
}


# 需要添加nestred关键字,并指定user这个字段
GET /blog/_search
{
  "size": 0, 
  "aggs": {
    "hs_agg": {
      "nested": {		# 添加nestred关键字
        "path": "user"
      }, 
      "aggs": {		# 再进行聚合操作
        "hs_first_name": {
          "terms": {
            "field": "user.first_name"
          }
        }
      }
    }
  }
}

在这里插入图片描述

在这里插入图片描述



父子关联关系

使用ES时,大部分的场景都不会频繁更新操作,父子关联关系了解即可。

再更新操作时,对象类型和嵌套对象nested方式有一个问题,因为根对象和嵌套对象本质上它们还是存在一个文档中的,每次更新时就可以需要重新索引整个文档。

ES提供了父子关联关系,通过维护parent/child的关系,分离它们,使父文档和子文档是两个独立的文档,更新其中一个文档不会影响另一个文档。



设定 Parent/Child Mapping

# 设定 Parent/Child Mapping映射关系
# 指定我们定义的hs_blog_comments_relation字段类型为join
# 并在relations下指定两个关联的自定义字符串值 其中hs_blog为parent名称,hs_comment为child名称
PUT /my_blogs
{
  "settings": {
    "number_of_shards": 2
  },
  "mappings": {
    "properties": {
      "hs_blog_comments_relation": {
        "type": "join",
        "relations": {
          "hs_blog": "hs_comment"
        }
      },
      "content": {
        "type": "text"
      },
      "title": {
        "type": "keyword"
      }
    }
  }
}

在这里插入图片描述



索引父文档

# 索引两个父文档 ,指定文档id为blog1 和 blog2
# 同时指定文档的类型为hs_blog父文档
PUT /my_blogs/_doc/blog1
{
  "content": "learning ELK ",
  "title": "Learning Elasticsearch",
  "hs_blog_comments_relation": {
    "name": "hs_blog"
  }
}
PUT /my_blogs/_doc/blog2
{
  "content": "learning Hadoop ",
  "title": "Learning Hadoop",
  "hs_blog_comments_relation": {
    "name": "hs_blog"
  }
}

在这里插入图片描述



索引子文档

创建子文档时,必须通过routing指定父文档id,保证父子文档在一个shard中,提高join查询性能。

当指定子文档时,必须指定父文档id

# 索引三个子文档,指定文档id、同时指定routing 让父子文档在相同的shard中
# 指定文档的类型为子文档,同时必须指定它的父文档id
PUT /my_blogs/_doc/comment1?routing=blog1
{
  "comment":"I am learning ELK",
  "username":"Jack",
  "hs_blog_comments_relation": {
    "name": "hs_comment",
    "parent": "blog1"
  }
}
PUT /my_blogs/_doc/comment2?routing=blog2
{
  "comment":"I like Hadoop!!!!!",
  "username":"Jali",
  "hs_blog_comments_relation": {
    "name": "hs_comment",
    "parent": "blog2"
  }
}
PUT /my_blogs/_doc/comment3?routing=blog2
{
  "comment":"Hello Hadoop",
  "username":"Bob",
  "hs_blog_comments_relation": {
    "name": "hs_comment",
    "parent": "blog2"
  }
}

在这里插入图片描述



测试查询

# 查询所有文档,就是正常的查询,能查询到5个文档,因为父子文档都是独立的文档
POST /my_blogs/_search

#根据父文档ID查看,也就是正常的查询
GET /my_blogs/_doc/blog2


# has_child 查询,返回这个子文档对应的父文档
GET /my_blogs/_search
{
  "query": {
    "has_child": {
      "type": "hs_comment",
      "query": {
        "match": {
          "username": "Jack"
        }
      }
    }
  }
}



# has_parent 查询,返回相关的子文档
GET /my_blogs/_search
{
  "query": {
    "has_parent": {
      "parent_type": "hs_blog",
      "query": {
        "match": {
          "content": "ELK"
        }
      }
    }
  }
}

在这里插入图片描述

在这里插入图片描述



GET /my_blogs/_search
#通过ID ,访问子文档,会发现查询不到数据,返回404
# 但是通过上方直接查询全部是能查询到comment3这个文档的
GET /my_blogs/_doc/comment3
# 通过query语句能查询到到这个子文档
GET /my_blogs/_search
{
  "query": {
    "match": {
      "comment": "Hello"
    }
  }
}
#通过ID和routing ,这种方式也能查询到这个子文档
GET /my_blogs/_doc/comment3?routing=blog2

#更新子文档,因为使用的是PUT 全量更新,所以在文档中还是需要指定子文档类型和父文档id
PUT /my_blogs/_doc/comment3?routing=blog2
{
    "comment": "Hello Hadoop??",
    "blog_comments_relation": {
      "name": "hs_comment",
      "parent": "blog2"
    }
}



嵌套文档 VS 父子关系

Nested ObjectParent / Child
优点文档存储在一起,读取性能高父子文档可以独立更新
缺点更新嵌套的子文档时,需要更新整个文档需要额外的内存维护关系。读取性能相对差
适用场景子文档偶尔更新,以查询为主子文档更新频繁

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

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

相关文章

SQL注入(head、报错、盲注)

目录 【学习目标、重难点知识】 【学习目标】 【重难点知识】 1. 报错注入 1.1 那么什么是报错注入呢? 1.2 报错注入原理 extractvalue函数 updatexml函数 1.3 靶场解析 靶场练习 2. HEAD注入 2.1 相关全局变量 2.2 靶场解析 burp暴力破解 靶场练习 3…

关于LLC知识6

总阻抗容抗感抗R 容抗和感抗可以互相抵消,但无论容抗或者感抗都不可以和R相互抵消。 容抗、感抗和R可以这样表示。 这里就出现了一个字母:j(沿着坐标系逆时针旋转90) 所以,总阻抗-j5Ω 所以,无论怎样&a…

虚拟机哪个软件最好用? 苹果电脑用虚拟机运行Windows程序 Mac电脑怎么玩Windows游戏

虚拟化技术就像是科技界的瑞士军刀,macOS用户就像是在寻找最锋利的那把刀。随着计算机技术的不断发展,虚拟机软件在现代信息技术领域中扮演着越来越重要的角色。虚拟机不仅可以帮助用户在一台物理机器上运行多个操作系统,还能有效隔离不同环境…

Windows Server 2016 Standard 修改远程登录服务 Remote Desktop Service 默认端口号

为什么要改默认端口号? 主要是为了安全,如果该服务持续被攻击,会触发系统的安全机制,锁定账号密码,导致无法远程登录服务器。那可就麻烦大了。。。 1 打开注册表 在CMD命令行窗口输入 “regedit” 2 在注册表中&…

CAD二次开发IFoxCAD框架系列(16)- IFoxCad的架构介绍

主要是提供一个最小化的内核,即 DBTrans、SymbolTable、ResultData、SelectFilter 等基础类,其他的功能都通过扩展方法的方式来实现。 DBTrans类 事务管理器是cad .net二次开发非常重要的一部分,只要涉及到读写cad数据的地方几乎都在事务里…

掌握Selenium爬虫的日志管理:调整–log-level选项的用法

介绍 在使用Selenium进行Web数据采集时,日志管理是一个至关重要的部分。日志不仅帮助开发者监控爬虫的运行状态,还能在出现问题时提供有价值的调试信息。Selenium提供了多种日志级别选项,通过调整–log-level参数,开发者可以控制日…

架起高效工作与持续学习之间的桥梁

程序员如何平衡日常编码工作与提升式学习? 在快速迭代的编程世界中,程序员们不仅需要高效完成日常编码任务,还需不断学习新技术、深化专业知识,以应对日益复杂的项目挑战。然而,如何在繁忙琐碎的编码工作与个人成长之…

Facebook的区块链技术:提升数据安全与隐私保护

去中心化的优势 随着数字化时代的快速发展,数据安全和隐私保护已成为全球范围内备受关注的话题。Facebook作为全球最大的社交平台之一,正在积极探索如何通过区块链技术来提升数据的安全性和用户的隐私保护。区块链技术以其去中心化、不可篡改和透明的特…

网络硬盘录像机NVR解決方案:海思3520D模组与全面的NVR方案支持

随着视频监控技术的不断发展,网络硬盘录像机(NVR)已经成为现代安防系统中不可或缺的一部分。NVR作为视频监控系统的核心设备,不仅负责视频的实时录制和存储,还承担着视频回放、告警触发、远程监控等重要功能。 我们基…

C语言中的⽂件操作

1. 为什么使⽤⽂件? 如果没有⽂件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运⾏程序,是看不到上次程序的数据的,如果要将数据进⾏持久化…

科望医药两年亏损超16亿:大幅收缩成本,核心产品试验数据欠佳

《港湾商业观察》黄懿 6月27日,科望医药集团(下称“科望医药”)向港交所提交上市申请,中信证券为其独家保荐人。 科望医药是一家处于临床阶段的生物医药公司,利用新一代疗法创新肿瘤治疗。 该公司大有来头&#xff…

【VRPCB】Python+Gurobi求解运输问题建模实践三

采用PythonGurobi求解带有集群回程需求的VRPCB问题 目录 1. 模型1.1 VRPB问题介绍1.2 数学模型1.2.1 模型参数1.2.2 数学模型1.2.3 模型分解 2. 数据结构3. Gurobi源码4. 求解结果参考 1. 模型 1.1 VRPB问题介绍 带有回程需求的VRP问题(VRP with Backhauls,VRPB)最…

PTrade常见问题系列23—量化是否支持读写文件?如何实现?

jupyterhub进程异常退出? 1、检查/var/log/jupyterhub.log日志,发现在进程503之前存在QA_DATA的请求URL,该问题是通过终端内帮助文档页面的常见问题说明链接跳转时,存在小概率导致hub进程503的问题; 2、已提交需求202…

高质量翻译对增强游戏对用户情感影响的影响

游戏中的事件往往是游戏中最难忘、最激动人心的时刻。这些事件——无论是戏剧性的情节转折、激烈的战斗,还是发自内心的角色互动——都是为了唤起玩家强烈的情感,让他们深深地投入到游戏中。然而,如果这些事件不能有效地传达给不同语言和文化…

【二分查找】--- 二分模板总结

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: 算法Journey 从本博客开始,博主将开始分享二分查找算法的相关知识。 🏠 朴素二分模板 --- 二分查找 📌 题目内容 二…

【Java日志系列】Log4j日志框架

目录 前言 一、Log4j简介 二、Log4j组件介绍 1. Loggers 2. Appenders 3. Layouts 三、快速入门 1. 入门代码 2. 日志级别 四、配置文件的使用 五、自定义日志输出格式 六、配置不同的Appender 1. 输出到文件 2. 输出到数据库 七、自定义Logger配置 总结 前言…

用 js canvas 做一个优雅的模拟时钟, canvas 教程实例

用 js canvas 做一个优雅的模拟时钟, canvas 教程实例 有很多次,我都想找到一个比较不错的,可以查看模拟时钟的网页。 有时候是想看下距离某个时间点还有多长时间,有时候是想看一下,两个时间点之间的间隔是多少。因为…

迅为i.MX8MM开发板控制GPIO高低电平-使用命令控制GPIO

在文件系统的/sys 目录下给用户提供了许多接口,比如在/sys 下面的 bus 目录,这个目录下放置的都是 和总线相关的,比如 I2C,usb 等。如下图所示: 本章我们需要重点关注下 class 目录,这个目录下放置的是一…

MyBatis-Plus 进阶之条件构造器Wrapper和常用接口

目录 1.wrapper 介绍 1.1什么是 wrapper 2.QueryWrapper 2.1测试前的准备 2.2例1:组装查询条件 2.3例2:组装排序条件 2.4例3:组装删除条件 2.5例4:条件的优先级 2.6例5:组装 select 子句 2.7例6:…

Redis常见的数据类型和应用场景

目录 Redis概述 简介 特点 架构 使用场景 Mermaid图示 Redis数据类型 基础数据类型 扩展数据类型(Redis 3.2及以上版本) Mermaid图示 String类型详解 定义 内部实现 应用场景 Mermaid图示 List类型详解 定义 内部实现 应用场景 Merma…