ES实现三表关联查询+条件过滤

需求背景

        很多时候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]
								}
						}
					}
				}
			]
		}
	}	
	 
}


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中实现ES(Elasticsearch)的left join关联查询,可以通过以下步骤进行: 1. 首先,你需要使用Java的Elasticsearch客户端库来连接和操作Elasticsearch。常用的Java Elasticsearch客户端库有Elasticsearch Java High Level REST Client和Elasticsearch Java Transport Client。 2. 创建一个Elasticsearch的查询请求,使用`SearchRequest`对象来定义查询的索引、类型和查询条件等信息。 3. 使用`SearchSourceBuilder`对象来构建查询的DSL(Domain Specific Language),包括设置查询条件过滤条件、排序等。 4. 对于left join关联查询,你可以使用Elasticsearch的`Nested`或`Parent-Child`关系来实现。如果你的数据模型中存在嵌套字段,可以使用`Nested`关系来进行关联查询。如果你的数据模型中存在父子关系,可以使用`Parent-Child`关系来进行关联查询。 5. 在查询DSL中,使用`Nested Query`或`Has Parent Query`来进行关联查询。你可以通过设置`path`参数指定嵌套字段的路径或者指定父文档的类型。 6. 执行查询请求,获取查询结果。使用Elasticsearch客户端库提供的方法来发送查询请求并解析返回的结果。 下面是一个示例代码片段,演示了如何使用Java Elasticsearch High Level REST Client实现ES的left join关联查询: ```java // 创建Elasticsearch客户端 RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("localhost", 9200, "http"))); // 创建查询请求 SearchRequest searchRequest = new SearchRequest("index_name"); searchRequest.types("type_name"); // 构建查询DSL SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(QueryBuilders.nestedQuery("nested_field", QueryBuilders.matchQuery("nested_field.field_name", "query_value"), ScoreMode.None)); // 设置查询DSL searchRequest.source(sourceBuilder); // 执行查询请求 SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); // 解析查询结果 SearchHits hits = searchResponse.getHits(); for (SearchHit hit : hits) { // 处理每个文档的结果 Map<String, Object> sourceAsMap = hit.getSourceAsMap(); // ... } // 关闭Elasticsearch客户端 client.close(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值