zoukankan      html  css  js  c++  java
  • elasticsearch 基础 —— Jion父子关系

    前言

    由于ES6.X版本以后,每个索引下面只支持单一的类型(type),因此不再支持以下形式的父子关系:

    PUT /company
    {
       "mappings": {
            "branch": {},
             "employee": {
                 "_parent": {
                          "type": "branch"
                  }
             }
         }
    }

    解决方案:

    引入 join datatype

    为同一索引中的文档定义父/子关系。

    join datatype

    join datatype是在同一索引文档中创建父/子关系的特殊字段。该relations部分定义了文档内的一组可能的关系,每个关系是父名和子名。父/子关系可以定义如下:

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "my_join_field": { ①
              "type": "join",
              "relations": {
                "question": "answer"  ②
              }
            }
          }
        }
      }
    }
    

    该字段的名称

    定义单个关系,其中answer父级为question

    若要用join来索引(动词)文档,必须在文中提供文档的关系名称和可选父文档。例如,下面的示例在question上下文中创建两个父文档:

    PUT my_index/_doc/1?refresh
    {
      "text": "This is a question",
      "my_join_field": {
        "name": "question" ①
      }
    }
    
    PUT my_index/_doc/2?refresh
    {
      "text": "This is a another question",
      "my_join_field": {
        "name": "question"
      }
    }

    本文档是一份question文档

    在索引父文档时,可以选择仅指定关系的名称作为快捷方式,而不是将其封装在普通对象符号中:

    PUT my_index/_doc/1?refresh
    {
      "text": "This is a question",
      "my_join_field": "question" ①
    }
    
    PUT my_index/_doc/2?refresh
    {
      "text": "This is another question",
      "my_join_field": "question"
    }

    父文档的简单表示法只使用关系名称。

    索引子文档时,必须在文中添加父级的关系的名称以及文档的父级_source ID 

    例如,以下示例显示如何索引两个child文档: 

    PUT my_index/_doc/3?routing=1&refresh 
    {
      "text": "This is an answer",
      "my_join_field": {
        "name": "answer", 
        "parent": "1" 
      }
    }
    
    PUT my_index/_doc/4?routing=1&refresh
    {
      "text": "This is another answer",
      "my_join_field": {
        "name": "answer",
        "parent": "1"
      }
    }
    • 路由值是强制性的,因为父文件和子文件必须在相同的分片上建立索引。
    • “answer”是此子文档的加入名称。
    • 指定此子文档的父文档ID:1。

    Parent-join and performance 父连接和性能

    连接字段不应该像关系数据库中的连接那样使用。在弹性搜索中,良好性能的关键是将数据归一化为文档。每个连接字段,HaseBoad或HasyPrad查询对查询性能都会产生显著的影响。

    联接字段唯一有意义的情况是,如果数据包含一对多关系,其中一个实体的数量显著超过另一个实体。

    Join 类型约束

    1. 每个索引只允许一个Join类型Mapping定义;
    2. 父文档和子文档必须在同一个分片上编入索引;这意味着,当进行删除、更新、查找子文档时候需要提供相同的路由值。
    3. 一个文档可以有多个子文档,但只能有一个父文档。
    4. 可以为已经存在的Join类型添加新的关系。
    5. 当一个文档已经成为父文档后,可以为该文档添加子文档。

    Searching with parent-join 使用父连接搜索

    父联接创建一个字段,索引文件(中关系的名称my_parentmy_child...)。

    它还为每个父/子关系创建一个字段。此字段的名称是join后跟字段#的名称以及关系中父项的名称。因此,例如对于my_parent⇒[ my_childanother_child]关系,该join字段会创建一个名为的附加字段my_join_field#my_parent

    _id如果文档是子文件(my_childanother_child),则此字段包含文档链接到的父项,如果文档是_id父项(my_parent),则包含文档链接到的父项。

    搜索包含join字段的索引时,始终在搜索响应中返回这两个字段:

    GET my_index/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": ["_id"]
    }

    将返回:

    {
        "took": 1,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": 4,
            "max_score": null,
            "hits": [
                {
                    "_index": "my_index",
                    "_type": "_doc",
                    "_id": "1",
                    "_score": null,
                    "_source": {
                        "text": "This is a question",
                        "my_join_field": {
                            "name": "question" ①
                        }
                    },
                    "sort": [
                        "1"
                    ]
                },
                {
                    "_index": "my_index",
                    "_type": "_doc",
                    "_id": "2",
                    "_score": null,
                    "_source": {
                        "text": "This is a another question",
                        "my_join_field": {
                            "name": "question" ②
                        }
                    },
                    "sort": [
                        "2"
                    ]
                },
                {
                    "_index": "my_index",
                    "_type": "_doc",
                    "_id": "3",
                    "_score": null,
                    "_routing": "1",
                    "_source": {
                        "text": "This is an answer",
                        "my_join_field": {
                            "name": "answer", ③
                            "parent": "1" ④
                        }
                    },
                    "sort": [
                        "3"
                    ]
                },
                {
                    "_index": "my_index",
                    "_type": "_doc",
                    "_id": "4",
                    "_score": null,
                    "_routing": "1",
                    "_source": {
                        "text": "This is another answer",
                        "my_join_field": {
                            "name": "answer",
                            "parent": "1"
                        }
                    },
                    "sort": [
                        "4"
                    ]
                }
            ]
        }
    }

    此文档属于question join

    此文档属于question join

    此文档属于answer join

    子文档的链接父I​​D

    Parent-join queries and aggregations 父连接查询和聚合

    有关详细信息,请参阅has_child和 has_parent查询,children聚合和内部命中

    join可以在聚合和脚本中访问该字段的值,并可以使用查询 parent_id查询

    GET my_index/_search
    {
      "query": {
        "parent_id": { 
          "type": "answer", ①
          "id": "1"
        }
      },
      "aggs": {
        "parents": {
          "terms": {
            "field": "my_join_field#question",  ②
            "size": 10
          }
        }
      },
      "script_fields": {
        "parent": {
          "script": {
             "source": "doc['my_join_field#question']"  ③
          }
        }
      }
    }

    查询parent id字段(另请参阅has_parent查询has_child查询

    parent id字段上聚合(也参见children聚合)

    访问脚本中的父ID字段

    Child-join queries and aggregations 子连接查询和聚合

    {
      "query": {
        "has_child": {
          "type": "answer",
          "query": {
            "match": {
              "text": "This is question"
            }
          }
        }
      }
    }
    
    或
    
    {
      "query": {
        "has_parent": {
          "parent_type": "question",
          "query": {
            "term": {
              "_id": 1
            }
          },
          "inner_hits": {
            
          }
        }
      }
    }
    {
        "took": 42,
        "timed_out": false,
        "_shards": {
            "total": 5,
            "successful": 5,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": 1,
            "max_score": 1,
            "hits": [
                {
                    "_index": "my_index",
                    "_type": "_doc",
                    "_id": "1",
                    "_score": 1,
                    "_source": {
                        "text": "This is a question",
                        "my_join_field": {
                            "name": "question"
                        }
                    }
                }
            ]
        }
    }

    Global ordinals 全局序数

    join字段使用全局序数来加速连接。在对碎片进行任何更改后,需要重建全局序数。父分区值存储在分片中的次数越多,重建join字段的全局序数所需的时间就越长。

    默认情况下,全局序数是急切建立的:如果索引发生了变化,该join字段的全局序数将作为刷新的一部分重建。这可以为刷新增加大量时间。但是大多数情况下这是正确的权衡,否则在使用第一个父连接查询或聚合时会重建全局序数。这可能会为您的用户带来显着的延迟峰值,并且通常情况会更糟,因为join 当发生许多写操作时,可能会在单个刷新间隔内尝试重建该字段的多个全局序数。

    join字段不经常使用并且频繁发生写入时,禁用预先加载可能是有意义的:

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "my_join_field": {
              "type": "join",
              "relations": {
                 "question": "answer"
              },
              "eager_global_ordinals": false
            }
          }
        }
      }
    }

    可以按父关系检查全局序数使用的堆量,如下所示:

    # Per-index
    GET _stats/fielddata?human&fields=my_join_field#question
    
    # Per-node per-index
    GET _nodes/stats/indices/fielddata?human&fields=my_join_field#question

    Multiple children per parent

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "my_join_field": {
              "type": "join",
              "relations": {
                "question": ["answer", "comment"]  
              }
            }
          }
        }
      }
    }

    questionanswer和comment的父级

    Multiple levels of parent join 多级父级联接

    不建议使用多级关系来复制关系模型。每个级别的关系在查询时在内存和计算方面增加了开销。如果您关心性能,则应该对数据进行去规范化。

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "my_join_field": {
              "type": "join",
              "relations": {
                "question": ["answer", "comment"],   ①
                "answer": "vote"  ②
              }
            }
          }
        }
      }
    }

    question 是 answer 和comment 的父级

    answer 是 vote 的父级

    实现如下图的祖孙三代关联关系的定义。

    question
        /    
       /      
    comment  answer
               |
               |
              vote

    文档必须位于与其父父级和父级相同的分片上:

    PUT my_index/_doc/3?routing=1&refresh  ①
    {
      "text": "This is a vote",
      "my_join_field": {
        "name": "vote", ②
        "parent": "2" 
      }
    }
    

    此子文档必须位于与其父父级和父级相同的分片上

    此文档的父ID(必须指向answer文档)

  • 相关阅读:
    Codeforces 1045C Hyperspace Highways (看题解) 圆方树
    Codeforces 316E3 线段树 + 斐波那切数列 (看题解)
    Codeforces 803G Periodic RMQ Problem 线段树
    Codeforces 420D Cup Trick 平衡树
    Codeforces 295E Yaroslav and Points 线段树
    Codeforces 196E Opening Portals MST (看题解)
    Codeforces 653F Paper task SA
    Codeforces 542A Place Your Ad Here
    python基础 异常与返回
    mongodb 删除
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864043.html
Copyright © 2011-2022 走看看