定义:join 数据类型是一个特殊字段,用于在相同索引的文档中创建一对多的关系。
使用场景:支持文档一对多关系的映射,并且多的一方写多读少
实践:
需求:设计一个用户购买了 多个商品 的数据模型
定义索引:
// 创建 index
PUT join_test
{
"settings": {
"number_of_replicas": 1, // 副分片数 初始化数据 设置成 0
"number_of_shards" : 5, // 主分片数
}
}
// 设置index 的字段 父子文档共享
POST join_test/_doc/_mapping
{
"properties": {
"user_id": {
"type": "keyword"
},
"bought_products_pid": {
"type": "keyword"
},
"bought_products_price": {
"type": "long"
},
"bought_products_tm": {
"type": "long"
},
"my_join_field": { // 定义 购买商品 与用户文档的映射关系
"type": "join",
"relations": {
"user": [ // user 为用户信息文档标识 父文档类型
"bought_products" // 购买商品文档标识
]
}
}
}
}
插入用户数据:
PUT join_test/_doc/1
{
"user_id":"1",
"my_join_field":{
"name":"user"
}
}
PUT join_test/_doc/2
{
"user_id":"2",
"my_join_field":{
"name":"user"
}
}
插入购买商品数据
// 用户1 购买 商品 1和2
// doc id 规则 子文档类型 + bought_products_pid
// 子文档一定要更父文档放在一块 routing = 父文档 doc id
PUT join_test/_doc/bp_1?routing=1
{
"bought_products_pid":"1",
"bought_products_tm":1,
"bought_products_price":1,
"user_id":"1",
"my_join_field":{
"name":"bought_products", // 子文档类型
"parent": "1" // 父文档 doc id
}
}
PUT join_test/_doc/bp_2?routing=1
{
"bought_products_pid":"2",
"bought_products_tm":1,
"bought_products_price":2,
"user_id":"1",
"my_join_field":{
"name":"bought_products",
"parent": "1"
}
}
// 用户2购买商品2
PUT join_test/_doc/bp_2?routing=2
{
"bought_products_pid":"2",
"bought_products_tm":1,
"bought_products_price":2,
"user_id":"2",
"my_join_field":{
"name":"bought_products",
"parent": "2"
}
}
// 用户2购买商品3
PUT join_test/_doc/bp_3?routing=2
{
"bought_products_pid":"3",
"bought_products_price":3,
"bought_products_tm":1,
"user_id":"2",
"my_join_field":{
"name":"bought_products",
"parent": "2"
}
}
搜索:
1、查出购买商品2 的所有用户
# 查询父文档 使用 has_child 语法
GET join_test/_search
{
"query": {
"bool": {
"must": [
{
"has_child": {
"type": "bought_products",
"query": {
"match": {
"bought_products_pid": "2"
}
}
}
}
]
}
}
}
# 响应结果
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [
{
"_index" : "join_test",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"user_id" : "2",
"my_join_field" : {
"name" : "user"
}
}
},
{
"_index" : "join_test",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"user_id" : "1",
"my_join_field" : {
"name" : "user"
}
}
}
]
}
}
2、查询用户1购买的所有商品
# 查询 子文档 使用 has_parent 并且 需要 指定 需要查询出的子文档类型
GET join_test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"my_join_field": "bought_products" // 需要查出来的文档类型
}
},
{
"has_parent": { // 父文档条件
"parent_type": "user",
"query": {
"match": {
"user_id": "1"
}
}
}
}
]
}
}
}
# 响应结果
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.3566749,
"hits" : [
{
"_index" : "join_test",
"_type" : "_doc",
"_id" : "bp_1",
"_score" : 1.3566749,
"_routing" : "1",
"_source" : {
"bought_products_pid" : "1",
"bought_products_tm" : 1,
"user_id" : "1",
"my_join_field" : {
"name" : "bought_products",
"parent" : "1"
}
}
},
{
"_index" : "join_test",
"_type" : "_doc",
"_id" : "bp_2",
"_score" : 1.3566749,
"_routing" : "1",
"_source" : {
"bought_products_pid" : "2",
"bought_products_tm" : 1,
"bought_products_price" : 2,
"user_id" : "1",
"my_join_field" : {
"name" : "bought_products",
"parent" : "1"
}
}
}
]
}
}
聚合:
1、统计支付金额 Top 10的用户
GET join_test/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"match": {
"my_join_field": "user"
}
}
]
}
},
"aggs": {
"group_by_user_id": {
"terms": {
"field": "user_id",
"size": 10,
"execution_hint": "map" // 查询结果 放在 内存中 进行 聚合,结果集小的可以加快 聚合速度
},
"aggs": {
"sum_price": {
"children": {
"type": "bought_products"
},
"aggs": {
"sum_price": {
"sum": {
"field": "bought_products_price"
}
}
}
},
"sort": {
"bucket_sort": {
"sort": [
{
"sum_price>sum_price": {
"order": "desc"
}
}
]
}
}
}
}
}
}
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"group_by_user_id" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "2",
"doc_count" : 1,
"sum_price" : {
"doc_count" : 2,
"sum_price" : {
"value" : 5.0
}
}
},
{
"key" : "1",
"doc_count" : 1,
"sum_price" : {
"doc_count" : 2,
"sum_price" : {
"value" : 3.0
}
}
}
]
}
}
}