ore88ore memo

プログラミング関連のTipsをメインに書いていきます。どなたかのお役に立てれば幸いです。

Elasticsearch Joinデータ型で親子データの定義とクエリ

Joinデータ型を利用することで、同一インデックス内に親子関係を持ったドキュメントを作成することができます。
ユースケースとしては、1つのエンティティが他のエンティティを大幅に上回っている1対多の関係がデータに含まれている場合と公式ページに記載されています。
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/parent-join.html

また、クエリの際は、パフォーマンス的な負担が大きいと記載されているので、注意が必要です。
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/parent-join.html#_parent_join_and_performance

実行環境

  • Elasticsearch
    • 7.4.2
  • Kibana
    • 7.4.2

定義

フィールド名をjoin_fieldとして、Joinデータ型を定義してみます。relations内で指定している、questionanswerはそれぞれ親ドキュメント名、子ドキュメント名を任意の名前を指定することができます。

PUT /join_test
{
  "mappings": {
    "properties": {
      "join_field": {
        "type": "join",
        "relations": {
          "question": "answer" 
        }
      }
    }
  }
}

インデクシング

Joinデータ型を定義したインデックスに親ドキュメント(質問)、子ドキュメント(解答)をインデクシングする。

親ドキュメント

Joinデータ型で指定した、親ドキュメント名(今回の例だとquestion)を指定してインデクシングする。また、nameの指定は省略することができるので、以下の2パターンは同じ意味となる。

PUT /join_test/_doc/1
{
  "question_text": "好きな野球チームは?",
  "join_field": {
    "name": "question" 
  }
}

PUT /join_test/_doc/2
{
  "question_text": "好きなサッカーチームは?",
  "join_field": "question" 
}

子ドキュメント

Joinデータ型で指定した、子ドキュメント名(今回の例だとanswer)を指定してインデクシングする。以下の例だと1つ目の質問の子ドキュメント(解答)を2つインデクシングしている。

PUT /join_test/_doc/3?routing=1
{
  "answer_text": "日ハム",
  "join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

PUT /join_test/_doc/4?routing=1
{
  "answer_text": "巨人",
  "join_field": {
    "name": "answer", 
    "parent": "1" 
  }
}

クエリ

上記でインデクシングしたデータを取得する。親子関係というと、親レコードに子レコードリストがぶら下がっている事を想像してしまいますが、親ドキュメントと子ドキュメントは同じレベルのドキュメントとしてフラットになっていることを意識する必要がある。

✗こっちじゃない
- 親1
    - 子1
    - 子2
- 親2
    - 子1

○こっち
- 親1
- 親1の子1
- 親1の子2
- 親2
- 親2子1

親ドキュメントのみ取得

GET /join_test/_search
{
  "query": {
    "term": {
      "join_field": "question"
    }
  }
}

子ドキュメントのみ取得

GET /join_test/_search
{
  "query": {
    "term": {
      "join_field": "answer"
    }
  }
}

子ドキュメントの条件に合致する親ドキュメントを取得

answer_text日ハムの子ドキュメントを持つ親ドキュメントリストを取得する。inner_hitsを付与することで、条件に合致する子ドキュメントもレスポンスとして含まれる。
https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl-has-child-query.html

GET /join_test/_search
{
  "query": {
    "has_child": {
      "type": "answer",
      "query": {
        "term": {
          "answer_text.keyword": "日ハム"
        }
      },
      "inner_hits": {}
    }
  }
}

親ドキュメントが同じ子ドキュメントを取得

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl-parent-id-query.html https://www.elastic.co/guide/en/elasticsearch/reference/7.4/query-dsl-has-parent-query.html

GET /join_test/_search
{
  "query": {
    "parent_id": { 
      "type": "answer",
      "id": "1"
    }
  }
}

GET /join_test/_search
{
  "query": {
    "has_parent": { 
      "parent_type" : "question",
      "query": {
        "term": {
          "question_text.keyword": "好きな野球チームは?"
        }
      }
    }
  }
}