Pyohwan / english-study

0 stars 0 forks source link

[Elasticsearch 7.x] Join field type #31

Open Pyohwan opened 4 years ago

Pyohwan commented 4 years ago

https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html

Join field type

PUT my-index-000001/_doc/2?refresh { "my_id": "2", "text": "This is another question", "my_join_field": { "name": "question" } }

* parent documents 를 indexing 할때, 일반 오브젝트 표기법으로 캡슐화 하지 않고, shortcut 으로 관계를 바로 지정할 수 있다.
```json
PUT my-index-000001/_doc/1?refresh
{
  "my_id": "1",
  "text": "This is a question",
  "my_join_field": "question"  // parent document 에 대한 간단한 표기법으로 관계 이름을 사용한다.
}

PUT my-index-000001/_doc/2?refresh
{
  "my_id": "2",
  "text": "This is another question",
  "my_join_field": "question"
}

PUT my-index-000001/_doc/4?routing=1&refresh { "my_id": "4", "text": "This is another answer", "my_join_field": { "name": "answer", "parent": "1" } }

Pyohwan commented 2 years ago

Parent-join and performance

join 필드는 관계 데이터베이스처럼 사용되어서는 안됩니다. Elasticsearch에서 좋은 성능의 핵심은 데이터를 문서로 de-normalize 하는 것입니다. 각 join field 또는 has_child or has_parent 쿼리는 쿼리 성능에 상당한 세금을 추가합니다. 또한 global ordinals 가 구축되도록 트리거할 수 있습니다.

join 필드가 의미가 있는 유일한 경우는 데이터에 한 엔터티가 다른 엔터티보다 훨씬 많은 일대다 관계가 포함된 경우입니다. 이러한 사례의 예로는 이러한 제품 및 오퍼의 사용 사례가 있습니다. 오퍼가 제품 수보다 훨씬 더 많은 경우 제품을 상위 문서로 모델링하고 오퍼를 하위 문서로 모델링하는 것이 합리적입니다.

Parent-join restrictions

Searching with parent-join

parent-join 은 문서 내에 관계 이름(my_parent, my_child, ...)을 인덱싱하기 위해 하나의 필드를 만듭니다. 또한 parent/child 관계당 하나의 필드를 만듭니다. 이 필드의 이름은 # 뒤에 오는 join 필드의 이름과 관계의 상위 이름입니다. 따라서 예를 들어 my_parent → [my_child, another_child] 관계의 경우 조인 필드는 my_join_field#my_parent 라는 추가 필드를 생성합니다. 이 필드는 문서가 하위(my_child 또는 another_child)인 경우 문서가 링크하는 상위 _id 및 parent 인 경우 문서의 _id(my_parent)를 포함합니다. join 필드가 포함된 인덱스를 검색할 때 다음 두 필드가 항상 검색 응답에 반환됩니다.

GET my-index-000001/_search
{
  "query": {
    "match_all": {}
  },
  "sort": ["my_id"]
}

Will return:

{
  ...,
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      {
        "_index": "my-index-000001",
        "_type": "_doc",
        "_id": "1",
        "_score": null,
        "_source": {
          "my_id": "1",
          "text": "This is a question",
          "my_join_field": "question" # 이 문서는 `question` join 에 속한다.
        },
        "sort": [
          "1"
        ]
      },
      {
        "_index": "my-index-000001",
        "_type": "_doc",
        "_id": "2",
        "_score": null,
        "_source": {
          "my_id": "2",
          "text": "This is another question",
          "my_join_field": "question" # 이 문서는 `question` join 에 속한다.
        },
        "sort": [
          "2"
        ]
      },
      {
        "_index": "my-index-000001",
        "_type": "_doc",
        "_id": "3",
        "_score": null,
        "_routing": "1",
        "_source": {
          "my_id": "3",
          "text": "This is an answer",
          "my_join_field": {
            "name": "answer", # 이 문서는 `answer` join 에 속한다.
            "parent": "1" # child 문서에 연결되어 있는 parent id
          }
        },
        "sort": [
          "3"
        ]
      },
      {
        "_index": "my-index-000001",
        "_type": "_doc",
        "_id": "4",
        "_score": null,
        "_routing": "1",
        "_source": {
          "my_id": "4",
          "text": "This is another answer",
          "my_join_field": {
            "name": "answer",
            "parent": "1"
          }
        },
        "sort": [
          "4"
        ]
      }
    ]
  }
}

Parent-join queries and aggregations

자세한 내용은 has_childhas_parent 쿼리, children aggregationinner hits를 참조하세요.

join 필드의 값은 aggregations 및 scripts 에서 액세스할 수 있으며 parent_id query로 쿼리할 수 있습니다.

GET my-index-000001/_search
{
  "query": {
    "parent_id": { # parent id 쿼리
      "type": "answer",
      "id": "1"
    }
  },
  "aggs": {
    "parents": {
      "terms": {
        "field": "my_join_field#question",  # parent id 로 aggregating
        "size": 10
      }
    }
  },
  "runtime_mappings": {
    "parent": {
      "type": "long",
      "script": "emit(Integer.parseInt(doc['my_join_field#question'].value))" # 스크립트의 parent id 필드로 접근
    }
  },
  "fields": [
    { "field": "parent" }
  ]
}
Pyohwan commented 2 years ago

Global ordinals

join 필드는 global ordinals 를 사용하여 조인 속도를 높입니다. 샤드를 변경한 후 global ordinals 를 다시 작성해야 합니다. 더 많은 parent ID 값이 샤드에 저장될수록 join 필드에 대한 global ordinals를 다시 작성하는 데 더 오래 걸립니다.

Global ordinals는 기본적으로 빠르게 빌드됩니다. 인덱스가 변경된 경우 join 필드에 대한 global ordinals가 새로 고침의 일부로 다시 작성됩니다. 이렇게 하면 새로 고침에 상당한 시간이 추가될 수 있습니다. 그러나 대부분의 경우 이것이 올바른 trade-off 입니다. 그렇지 않으면 첫 번째 parent-join 쿼리 또는 aggregation 가 사용될 때 global ordinals 가 다시 작성됩니다. 이로 인해 사용자에게 상당한 지연 시간 스파이크가 발생할 수 있으며 일반적으로 많은 쓰기가 발생할 때 join 필드에 대한 여러 global ordinals 가 단일 새로 고침 간격 내에서 재구축을 시도할 수 있으므로 더 나쁩니다.

join 필드가 자주 사용되지 않고 쓰기가 자주 발생하는 경우 즉시 로드를 비활성화하는 것이 합리적일 수 있습니다.

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
           "question": "answer"
        },
        "eager_global_ordinals": false
      }
    }
  }
}

global ordinals가 사용하는 힙의 양은 부모 관계별로 다음과 같이 확인할 수 있습니다.

# 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-000001
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "question": ["answer", "comment"]  # question 는 answer 과 comment 의 parent 이다.
        }
      }
    }
  }
}

Multiple levels of parent join

관계형 모델을 복제하기 위해 여러 수준의 관계를 사용하는 것은 권장되지 않습니다. 관계의 각 수준은 메모리 및 계산 측면에서 쿼리 시간에 오버헤드를 추가합니다. 성능이 중요하다면 데이터를 비정규화해야 합니다.

여러 수준의 부모/자식:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_join_field": {
        "type": "join",
        "relations": {
          "question": ["answer", "comment"],  # question 는 answer 과 comment 의 parent 이다.
          "answer": "vote" # answer 는 vote 의 parent 이다.
        }
      }
    }
  }
}
위의 매핑은 다음 트리를 나타냅니다. question / \ / \ comment answer
      vote

하위 문서를 인덱싱하려면 grand-parent(계보의 parent 보다 높은)와 동일한 routing 값이 필요합니다.

PUT my-index-000001/_doc/3?routing=1&refresh # 이 하위 문서는 상위 및 상위 문서와 동일한 샤드에 있어야 합니다.
{
  "text": "This is a vote",
  "my_join_field": {
    "name": "vote",
    "parent": "2" # 이 문서의 parent ID(answer 문서를 가리켜야 함)
  }
}