es-复杂查询Nested和父子文档查询join

原文地址:Elasticsearch14-复杂查询Nested和父子文档查询join - 陕西小楞娃 - 博客园

Elasticsearch14-复杂查询Nested和父子文档查询join

文档id类型为keyword 性能最高

Nested search
  -- nested类型是object数据类型的专用版本,它允许可以彼此独立地查询对象的方式对对象数组进行索引,当存储内部对象为复杂类型时应该使用nested而不是object.

查询语法

"query": {
    "nested": {
      "path": "user",
      "query": { 
      }
    }
}

# path: nested对象的查询深度
# score_mode: ii.聚合分数计算方式
    1.avg (默认):使用所有匹配的子对象的平均相关性得分。
    2.max:使用所有匹配的子对象中的最高相关性得分。
    3.min:使用所有匹配的子对象中最低的相关性得分。
    4.none:不要使用匹配的子对象的相关性分数。该查询为父文档分配得分为0。
    5.sum:将所有匹配的子对象的相关性得分相加。

解决方式

1.删除索引
2.重新创建索引, goods_list字段类型设置为:nested

# 查询出必须为小米10并且价格为4999的
GET /order/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "goods_list",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "goods_list.name": "小米10"
                    }
                  },
                  {
                    "match": {
                      "goods_list.price": 4999
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

省市区查询练习

#1.插入mapping
PUT /area
{
  "mappings": {
    "properties": {
      "province": {
        "type": "nested",
        "properties": {
          "name": {
            "type": "text",
            "analyzer": "ik_max_word"
          },
          "cities": {
            "type": "nested",
            "properties": {
              "name": {
                "type": "text",
                "analyzer": "ik_max_word"
              },
              "district": {
                "type": "nested",
                "properties": {
                  "name": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

#2.插入数据
PUT /area/_doc/1
{
  "province": {
    "name": "北京",
    "cities": [
      {
        "name": "北京市",
        "district": [
          {"name":"丰台区"},
          {"name":"海淀区"},
          {"name":"朝阳区"},
          {"name":"东城区"},
          {"name":"西城区"},
          {"name":"昌平区"}
          ]
      }
    ]
  }
}
PUT /area/_doc/2
{
  "province": {
    "name": "河南省",
    "cities": [
      {
        "name": "郑州市",
        "district": [
          {"name":"金水区"},
          {"name":"高新区"},
          {"name":"郑东新区"},
          {"name":"二七区"},
          {"name":"中原区"},
          {"name":"惠济区"}
          ]
      },
       {
        "name": "鹤壁市",
        "district": [
          {"name":"山城区"},
          {"name":"淇滨区"},
          {"name":"鹤山区"},
          {"name":"朝歌"},
          {"name":"浚县"}
          ]
      }
    ]
  }
}
PUT /area/_doc/3
{
  "province": {
    "name": "台湾省",
    "cities": [
      {
        "name": "台北市",
        "district": [
          {"name":"中正区"},
          {"name":"大同区"},
          {"name":"中山区"},
          {"name":"万华区"},
          {"name":"信义区"},
          {"name":"松山区"}
          ]
      },
       {
        "name": "高雄",
        "district": [
          {"name":"小港区"},
          {"name":"鼓山区"},
          {"name":"三民区"}
          ]
      }
    ]
  }
}


#3.city为包含北京市 或者 包含淇滨区的 省份信息
GET /area/_search
{
  "query": {
    "nested": {
      "path": "province",
      "query": {
        "nested": {
          "path": "province.cities",
          "query": {
            "bool": {
              "should": [
                {
                  "term": {
                    "province.cities.name": "北京市"
                  }
                },
                {
                  "nested": {
                    "path": "province.cities.district",
                    "query": {
                      "bool": {
                        "must": [
                          {
                            "term": {
                              "province.cities.district.name": "淇滨区"
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}

Join
  -- 一个问题有多个答案、一本博客有多个评论。此处我们可以使用 es 的 jion数据类型或 nested来实现。

创建mapping

PUT /plan_index
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "plan_id":{
        "type": "keyword"
      },
      "plan_name":{
        "type": "text",
        "fields": {
          "keyword":{
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "act_id":{
        "type": "keyword"
      },
      "act_name":{
        "type": "text",
        "fields": {
          "keyword":{
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "comment_id":{
        "type": "keyword"
      },
      "comment_name":{
        "type": "text",
        "fields": {
          "keyword":{
            "type" : "keyword",
            "ignore_above" : 256
          }
        }
      },
      "creator":{
        "type": "keyword"
      },
      "create_time":{
        "type": "date",
        "format": "yyyy-MM-dd||yyyy-MM-dd HH:mm:ss"
      },
      "plan_join": {
        "type": "join",
        "relations": {
          "plan": ["activity", "book"],
          "book": "comments"
        }
      }
    }
  }
}

添加父文档数据

#添加父文档数据
PUT /plan_index/_doc/plan-001
{
  "plan_id": "plan-001",
  "plan_name": "四月计划",
  "creator": "huan",
  "create_time": "2021-04-07 16:27:30",
  "plan_join": {
    "name": "plan"
  }
}

PUT /plan_index/_doc/plan-002
{
  "plan_id": "plan-002",
  "plan_name": "五月计划",
  "creator": "huan",
  "create_time": "2021-05-07 16:27:30",
  "plan_join": "plan"
}


注意⚠️:
    1.如果是创建父文档,则需要使用 plan_join 指定父文档的关系的名字(此处为plan)。
    2.plan_join为创建索引的 mapping时指定join的字段的名字。
    3.指定父文档时,plan_join的这2种写法都可以。
    

# 添加子文档数据
PUT /plan_index/_doc/act-001?routing=plan-001
{
  "act_id":"act-001",
  "act_name":"四月第一个活动",
  "creator":"huan.fu",
  "plan_join":{
    "name":"activity",
    "parent":"plan-001"
  }
}

PUT /plan_index/_doc/book-001?routing=plan-001
{
  "book_id":"book-001",
  "book_name":"四月读取的第一本书",
  "creator":"huan.fu",
  "plan_join":{
    "name":"book",
    "parent":"plan-001"
  }
}

PUT /plan_index/_doc/book-002?routing=plan-001
{
  "book_id":"book-002",
  "book_name":"编程珠玑",
  "creator":"huan.fu",
  "plan_join":{
    "name":"book",
    "parent":"plan-001"
  }
}

PUT /plan_index/_doc/book-003?routing=plan-002
{
  "book_id":"book-003",
  "book_name":"java编程思想",
  "creator":"huan.fu",
  "plan_join":{
    "name":"book",
    "parent":"plan-002"
  }
}

# 理论上 comment 的父文档是 book ,但是此处routing使用 plan 也是可以的。
PUT /plan_index/_doc/comment-001?routing=plan-001
{
  "comment_id":"comment-001",
  "comment_name":"这本书还可以",
  "creator":"huan.fu",
  "plan_join":{
    "name":"comments",
    "parent":"book-001"
  }
}

PUT /plan_index/_doc/comment-002?routing=plan-001
{
  "comment_id":"comment-002",
  "comment_name":"值得一读,棒。",
  "creator":"huan.fu",
  "plan_join":{
    "name":"comments",
    "parent":"book-001"
  }
}
注意⚠️:
    1. routing=plan-001 子文档需要和父档使用相同的路由键
    2. "name":"book" 子文档在mapping-join中定义的名字
    3. "parent":"plan-001" 父文档id

根据父文档id查询它下方的子文档

#返回父文档id是plan-001下的类型为book的所有子文档。
GET /plan_index/_search
{
  "query":{
    "parent_id": {
      "type":"book",
      "id":"plan-001"
    }
  }
}

has_child返回满足条件的父文档

#返回创建者(creator)是huan.fu,并且子文档最少有2个的父文档。
GET /plan_index/_search
{
  "query": {
    "has_child": {
      "type": "book",
      "min_children": 2,  
      "query": {
        "match": {
          "creator": "huan.fu"
        }
      }
    }
  }
}

has_parent返回满足父文档的子文档

#返回父文档(book)的创建者是huan.fu的所有子文档
GET /plan_index/_search
{
  "query": {
    "has_parent": {
      "parent_type": "book",
      "query": {
        "match": {
          "creator":"huan.fu"
        }
      }
    }
  }
}

Nested和Join区别

Nested Objectjoin
1、文档存储在一起,读取性能高1、父子文档单独存储,互不影响。但是为了维护join的关系,需要占用额外的内容,读取性能略差。
2、更新父文档或子文档时,需要更新整个文档。2、父文档和子文档可以单独更新。
3、适用于查询频繁,子文档偶尔更新的情况。3、适用于更新频繁的情况,且子文档的数量远远超过父文档的数量。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值