需求背景
很多时候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]}}}}]}} }