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
内で指定している、question
とanswer
はそれぞれ親ドキュメント名、子ドキュメント名を任意の名前を指定することができます。
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" } }
join_field
のname
- 定義で指定した子ドキュメント名
answer
- 定義で指定した子ドキュメント名
join_field
のparent
- 親ドキュメントの
ID
- 親ドキュメントの
routing
- 子ドキュメントと親ドキュメントは同じシャードに保存する必要がある
- どこのシャードに保存されるか?は、
routing
で指定することができる routing
を省略した場合は、ID
となる
クエリ
上記でインデクシングしたデータを取得する。親子関係というと、親レコードに子レコードリストがぶら下がっている事を想像してしまいますが、親ドキュメントと子ドキュメントは同じレベルのドキュメントとしてフラットになっていることを意識する必要がある。
✗こっちじゃない - 親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": "好きな野球チームは?" } } } } }