需求背景
很多时候mysql的表之间是一对多的关系,比如库信息表(元数据信息),表信息表(元数据信息),字段信息表(元数据信息)。一个库可以包含多个表,一个表可以包含多个字段。他们的关系:库—(1:n)->表—(1:n)->字段。
ElasticsSearch(以下简称ES)处理这种关系虽然不是特别擅长(相对于关系型数据库),因为ES和大多数 NoSQL 数据库类似,是扁平化的存储结构。索引是独立文档的集合体。不同的索引之间一般是没有关系的。
不过ES目前毕竟发展到8.x版本了, 已经有几种可选的方式能够高效的支持这种一对多关系的映射。
比较常用的方案是嵌套对象,嵌套文档和父子文档。后两种是我们本文要讲的内容。
表结构
为了便于描述下面的demo内容,现在先介绍一下表结构demo内容(表名称:字段1,字段2,字段3......)
database: database_id, name, desc
table:table_id,name,desc,address
column:column_id,name,desc,address
嵌套文档查询实例
#建立索引元数据:两层嵌套 database->table->column
put http://localhost:9200/test_nested
{
"mappings": {
"properties": {
"table": {
"type": "nested",
"properties": {
"column": {
"type": "nested"
}
}
}
}
}
}
#创建1个库数据database1
PUT http://localhost:9200/test_nested/_doc/database1
{
"database_id": 1,
"name" : "database1",
"des" : "This is a database!",
"table" : [
{
"table_id":1,
"name" : "John",
"des" : "This is a table!",
"address":"hangzhou",
"column":[
{
"column_id":1,
"name" :"zhangsan",
"des" : "This is a column!",
"address":"wuhan"
},
{
"column_id":2,
"name" :"Alice",
"des" : "This is a column!",
"address":"changchun"
}
]
},
{
"table_id":2,
"name" : "Alice",
"des" : "This is a table!",
"address":"changchun",
"column":[
{
"column_id":3,
"name" :"zhangsan",
"des" : "This is a column!",
"address":"hangzhou"
},
{
"column_id":4,
"name" :"John",
"des" : "This is a column!",
"address":"zhengzhou"
}
]
}
]
}
#创建1个库数据database2
PUT http://localhost:9200/test_nested/_doc/database2
{
"database_id": 2,
"name" : "database2",
"des" : "This is a database!",
"table" : [
{
"table_id":3,
"name" : "zhangsan",
"des" : "This is a table!",
"address":"wuhan",
"column":[
{
"column_id":5,
"name" :"John",
"des" : "This is a column!",
"address":"hangzhou"
},
{
"column_id":6,
"name" :"Alice",
"des" : "This is a column!",
"address":"changchun"
}
]
},
{
"table_id":4,
"name" : "Alice",
"des" : "This is a table!",
"address":"changchun",
"column":[
{
"column_id":7,
"name" :"zhangsan",
"des" : "This is a column!",
"address":"hangzhou"
},
{
"column_id":8,
"name" :"John",
"des" : "This is a column!",
"address":"zhengzhou"
}
]
}
]
}
#嵌套查询例子,查询column匹配指定内容,且table匹配指定内容的文档
POST http://localhost:9200/test_nested/_search
{
"query" : {
"bool": {
"must": [
{
"nested": {
"path": "table",
"query": {
"bool": {
"must": [
{
"match": {
"table.address": "hangzhou"
}
},
{
"match": {
"table.name": "John"
}
}
]
}
}
}
},
{
"nested": {
"path": "table.column",
"query" : {
"bool": {
"must": [
{
"match": {
"table.column.address": "wuhan"
}
},
{
"match": {
"table.column.name": "zhangsan"
}
}
]
}
}
}
}
]
}
}
}
#实现类似"三表关联查询+条件过滤",查询cloumn匹配指定内容,或table匹配指定内容,或database匹配指定内容的文档
POST http://localhost:9200/test_nested/_search
{
"query" : {
"bool": {
"should": [
{
"nested": {
"path": "table",
"query": {
"bool": {
"must": [
{
"match": {
"table.address": "hangzhou"
}
},
{
"match": {
"table.name": "John"
}
}
]
}
}
}
},
{
"nested": {
"path": "table.column",
"query" : {
"bool": {
"must": [
{
"match": {
"table.column.address": "hangzhou"
}
},
{
"match": {
"table.column.name": "John"
}
}
]
}
}
}
},
{
"match" :{
"name":"hangzhou"
}
}
]
}
}
}
父子文档查询实例
#创建索引元数据
put http://localhost:9200/metadata1
{
"mappings": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"database": ["table"],
"table": ["column"]
}
}
}
}
}
#创建1个父文档
put http://localhost:9200/metadata1/_doc/1
{
"database_id": "1",
"des": "This is a database!",
"name":"zhangsan",
"address":"hangzhou",
"my_join_field": {
"name": "database"
}
}
#创建1个子文档
put http://localhost:9200/metadata1/_doc/2?routing=1
{
"table_id": "1",
"des": "This is a table!",
"name":"lisi",
"address":"hangzhou",
"my_join_field": {
"name": "table",
"parent":1
}
}
#创建1个孙子文档
put http://localhost:9200/metadata1/_doc/3?routing=2
{
"column_id": "1",
"des": "This is a column!",
"name":"wangwu",
"address":"hangzhou",
"my_join_field": {
"name": "column",
"parent":2
}
}
#创建1个孙子文档
put http://localhost:9200/metadata1/_doc/4?routing=2
{
"column_id": "2",
"des": "This is a column!",
"name":"hangzhou",
"address":"zhengzhou",
"my_join_field": {
"name": "column",
"parent":2
}
}
#创建1个孙子文档,用于验证查询内容默认分词了
put http://localhost:9200/metadata1/_doc/5?routing=2
{
"column_id": "3",
"des": "This is a column!",
"name":"hangzhouren",
"address":"hangzhou city",
"my_join_field": {
"name": "column",
"parent":2
}
}
#分页查询某个字段(查询范围包括父,子,孙子文档)
post http://localhost:9200/metadata1/_search
{
"query" : {
"match": {
"address" : "hangzhou"
}
},
"from" : 1,
"size" : 1
}
#term 批量查询
post http://localhost:9200/metadata1/_search
{
"query": {
"terms" : {
"address":["hangzhou pro","zhengzhou"]
}
}
}
#查询具备满足匹配内容的孙子文档的子文档
post http://localhost:9200/metadata1/_search
{
"query": {
"has_child": {
"type": "column",
"query" : {
"match": {
"address" : "hangzhou"
}
}
}
}
}
#查询具备满足匹配内容的子文档的父文档
post http://localhost:9200/metadata1/_search
{
"query": {
"has_child": {
"type": "table",
"query" : {
"match": {
"address" : "hangzhou"
}
}
}
}
}
#查询具备满足匹配内容的孙子文档的父文档
post http://localhost:9200/metadata1/_search
{
"query": {
"has_child": {
"type": "table",
"query" : {
"has_child": {
"type": "column",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
}
}
}
}
#bool查询满足条件孙子文档的父文档,和满足条件子文档的父文档
post http://localhost:9200/metadata1/_search
{
"query": {
"bool": {
"should": [
{
"has_child": {
"type": "table",
"query" : {
"has_child": {
"type": "column",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
}
}
},
{
"has_child": {
"type": "table",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
}
]
}
}
}
#查询满足条件子文档的父文档的子文档,即子文档本身;如果父,子,孙文档的文档字段名称不同,就不用这么麻烦的查询
post http://localhost:9200/metadata1/_search
{
"query": {
"has_parent": {
"parent_type": "database",
"query" : {
"has_child": {
"type": "table",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
}
}
}
}
#以下两条查询可以类似实现"三表关联查询+条件过滤"的功能
#先查询满足条件匹配的父文档的子文档,满足条件匹配孙子文档的子文档和满足条件匹配的子文档
post http://localhost:9200/metadata1/_search
{
"query": {
"bool": {
"should": [
{
"has_parent": {
"parent_type": "database",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
},
{
"has_child": {
"type": "column",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
},
{
"has_parent": {
"parent_type": "database",
"query" : {
"has_child": {
"type": "table",
"query" : {
"multi_match": {
"query" : "hangzhou",
"fields":["address","name"]
}
}
}
}
}
}
]
}
}
}
#根据上面的子文档查询关联的父文档和孙子文档,然后再在程序里进行数据关联组装
post http://localhost:9200/metadata1/_search
{
"query": {
"bool": {
"should": [
{
"has_parent": {
"parent_type": "table",
"query" : {
"ids": {
"values" : [2]
}
}
}
},
{
"has_child": {
"type": "table",
"query" : {
"ids": {
"values" : [2]
}
}
}
}
]
}
}
}