ElasticSearch 如何像 MySQL 一样做多表联合查询?

文章介绍了在Elasticsearch中如何处理类似于关系型数据库中的一对多关系,包括使用嵌套文档和父子文档的方式。嵌套文档虽然能保持数据边界,但更新性能不佳;父子文档则解决了更新问题,适合写多读少的场景,但有特定的使用限制,如需在同一分片上。
摘要由CSDN通过智能技术生成

关系型数据库中有表的关联关系,在 es 中,我们也有类似的需求,例如订单表和商品表,在 es 中,这样的一对多一般来说有两种方式:

  • 嵌套文档(nested)

  • 父子文档

18.1 嵌套文档

假设:有一个电影文档,每个电影都有演员信息:

PUT movies{  "mappings": {    "properties": {      "actors":{        "type": "nested"      }    }  }}PUT movies/_doc/1{  "name":"霸王别姬",  "actors":[    {      "name":"张国荣",      "gender":"男"    },    {      "name":"巩俐",      "gender":"女"    }    ]}

注意 actors 类型要是 nested,具体原因参考 10.2.3 小节。

缺点

查看文档数量:

GET _cat/indices?v

查看结果如下:

image-20201119162958456

这是因为 nested 文档在 es 内部其实也是独立的 lucene 文档,只是在我们查询的时候,es 内部帮我们做了 join 处理,所以最终看起来就像一个独立文档一样。因此这种方案性能并不是特别好。

18.2 嵌套查询

这个用来查询嵌套文档:

GET movies/_search{  "query": {    "nested": {      "path": "actors",      "query": {        "bool": {          "must": [            {              "match": {                "actors.name": "张国荣"              }            },            {              "match": {                "actors.gender": "男"              }            }          ]        }      }    }  }}

18.3 父子文档

相比于嵌套文档,父子文档主要有如下优势:

  • 更新父文档时,不会重新索引子文档

  • 创建、修改或者删除子文档时,不会影响父文档或者其他的子文档。

  • 子文档可以作为搜索结果独立返回。

例如学生和班级的关系:

PUT stu_class{  "mappings": {    "properties": {      "name":{        "type": "keyword"      },      "s_c":{        "type": "join",        "relations":{          "class":"student"        }      }    }  }}

s_c

表示父子文档关系的名字,可以自定义。join 表示这是一个父子文档。relations 里边,class 这个位置是 parent,student 这个位置是 child。

接下来,插入两个父文档:

PUT stu_class/_doc/1{  "name":"一班",  "s_c":{    "name":"class"  }}PUT stu_class/_doc/2{  "name":"二班",  "s_c":{    "name":"class"  }}

再来添加三个子文档:

PUT stu_class/_doc/3?routing=1{  "name":"zhangsan",  "s_c":{    "name":"student",    "parent":1  }}PUT stu_class/_doc/4?routing=1{  "name":"lisi",  "s_c":{    "name":"student",    "parent":1  }}PUT stu_class/_doc/5?routing=2{  "name":"wangwu",  "s_c":{    "name":"student",    "parent":2  }}

首先大家可以看到,子文档都是独立的文档。特别需要注意的地方是,子文档需要和父文档在同一个分片上,所以 routing 关键字的值为父文档的 id。另外,name 属性表明这是一个子文档。

父子文档需要注意的地方:

  1. 每个索引只能定义一个 join filed

  1. 父子文档需要在同一个分片上(查询,修改需要routing)

  1. 可以向一个已经存在的 join filed 上新增关系

18.4 has_child query

通过子文档查询父文档使用 has_child

query。

GET stu_class/_search{  "query": {    "has_child": {      "type": "student",      "query": {        "match": {          "name": "wangwu"        }      }    }  }}

查询 wangwu 所属的班级。

18.5 has_parent query

通过父文档查询子文档:

GET stu_class/_search{  "query": {    "has_parent": {      "parent_type": "class",      "query": {        "match": {          "name": "二班"        }      }    }  }}

查询二班的学生。但是大家注意,这种查询没有评分。

可以使用 parent id 查询子文档:

GET stu_class/_search{  "query": {    "parent_id":{      "type":"student",      "id":1    }  }}

通过 parent id 查询,默认情况下使用相关性计算分数。

18.6 小结

整体上来说:

  1. 普通子对象实现一对多,会损失子文档的边界,子对象之间的属性关系丢失。

  1. nested 可以解决第 1 点的问题,但是 nested 有两个缺点:更新主文档的时候要全部更新,不支持子文档属于多个主文档。

  1. 父子文档解决 1、2 点的问题,但是它主要适用于写多读少的场景。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值