🔍
nestedクエリにヒットした件数でソートしたい
はじめに
業務でnested
クエリを使用し、ヒットした件数で並び替える実装をしました。
SQLとは異なる部分で少し手間取ったので備忘録としてまとめます。
やりたいこと
以下のような親子関係を持つデータが存在するとします。
- 親: 著者 (authors)
- 子: 記事 (articles)
ここで、記事を検索し、ヒットした記事の件数で著者を並び替えたいとします。
SQLでは以下のようなクエリで実現できます。
SELECT authors.*, COUNT(articles.id) AS article_count
FROM authors
INNER JOIN articles
ON authors.id = articles.author_id
WHERE articles.title = "elasticsearch"
ORDER BY article_count DESC;
この記事では、これをElasticsearchで実現します。
検証用インデックスの準備
業務ではnested
フィールドを使用して親子関係を構築しました。
この記事でもauthors
インデックスにarticles
というnested
フィールドを持たせ、親子関係を再現します。
Kibanaにアクセスする
記事用の開発環境は以下の記事を参考に構築しました。
インデックスの作成
PUT authors
{
"mappings": {
"properties": {
"articles": {
"type": "nested"
}
}
}
}
ドキュメントの追加
以下のように著者3人と、それぞれ異なる件数の記事を追加します。
また、articles
内にcounter
フィールドを持たせ、全ての値を1
に設定します。
このcounter
フィールドは、ヒットした件数で並び替える際に使用します。
POST authors/_bulk
{"index":{}}
{"name":"著者その1","description":"記事を1件投稿してます。","articles":[{"title":"記事その1","counter":1}]}
{"index":{}}
{"name":"著者その2","description":"記事を2件投稿してます。","articles":[{"title":"記事その1","counter":1},{"title":"記事その2","counter":1}]}
{"index":{}}
{"name":"著者その3","description":"記事を3件投稿してます。","articles":[{"title":"記事その1","counter":1},{"title":"記事その2","counter":1},{"title":"記事その3","counter":1}]}
検索の実行
sort
クエリにnested
フィルターを設定し、ヒットした記事のcounter
フィールドの合計値で降順ソートします。
これにより、ヒットした記事の件数で著者を並び替えます。
検索クエリ
GET authors/_search
{
"query": {
"nested": {
"path": "articles",
"inner_hits": {
"_source": ["articles.title"]
},
"query": {
"bool": {
"filter": [
{
"terms": {
"articles.title.keyword": ["記事その1", "記事その2", "記事その3"]
}
}
]
}
}
}
},
"sort": [
{
"articles.counter": {
"mode": "sum",
"order": "desc",
"nested": {
"path": "articles",
"filter": {
"terms": {
"articles.title.keyword": ["記事その1", "記事その2", "記事その3"]
}
}
}
}
}
],
"_source": ["name", "description"]
}
検索結果
※ 一部不要な箇所は省略
{
"hits": {
"total": { "value": 3 },
"hits": [
{
"_source": {
"name": "著者その3",
"description": "記事を3件投稿してます。"
},
"sort": [3],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 3 },
"hits": [
{ "_source": { "title": "記事その1" } },
{ "_source": { "title": "記事その2" } },
{ "_source": { "title": "記事その3" } }
]
}
}
}
},
{
"_source": {
"name": "著者その2",
"description": "記事を2件投稿してます。"
},
"sort": [2],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 2 },
"hits": [
{ "_source": { "title": "記事その1" } },
{ "_source": { "title": "記事その2" } }
]
}
}
}
},
{
"_source": {
"name": "著者その1",
"description": "記事を1件投稿してます。"
},
"sort": [1],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 1 },
"hits": [
{ "_source": { "title": "記事その1" } }
]
}
}
}
}
]
}
}
クエリの条件を変更する
例えば、"記事その1"
と"記事その3"
に絞り込む場合、以下のようにクエリを変更します。
{
"terms": {
- "articles.title.keyword": ["記事その1", "記事その2", "記事その3"]
+ "articles.title.keyword": ["記事その1", "記事その3"]
}
}
"記事その3"
を持っているのは"著者その3"
だけなので、"著者その3"
が一番上に来ます。
検索結果
{
"hits": {
"total": { "value": 3 },
"hits": [
{
"_source": {
"name": "著者その3",
"description": "記事を3件投稿してます。"
},
"sort": [2],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 2 },
"hits": [
{ "_source": { "title": "記事その1" } },
{ "_source": { "title": "記事その3" } }
]
}
}
}
},
{
"sort": [1],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 1, },
"hits": [
{ "_source": { "title": "記事その1" } }
]
}
}
}
},
{
"_source": {
"name": "著者その2",
"description": "記事を2件投稿してます。"
},
"sort": [1],
"inner_hits": {
"articles": {
"hits": {
"total": { "value": 1 },
"hits": [
{ "_source": { "title": "記事その1" } }
]
}
}
}
}
]
}
}
参考文献
Discussion