[翻訳] ハイブリッドクエリにおけるソートの仕組み
OpenSearch 2.10 でハイブリッドクエリが導入されて以来、セマンティック検索の関連性を向上させたいユーザーの間で人気が高まっています。ハイブリッドクエリは、全文検索 (レキシカル検索) とセマンティック検索を組み合わせることで、どちらか単独よりも優れた結果を提供します。EC サイト、ドキュメント検索、ログ分析、データ探索など、幅広いアプリケーションで活用されています。ハイブリッドクエリに馴染みがない方は、まずハイブリッド検索を紹介し、その品質とパフォーマンスの結果を示した以前の記事をお読みください。
OpenSearch は、ポストフィルター、アグリゲーション、クエリの並列化などの機能を導入し、ハイブリッドクエリの機能とパフォーマンスを継続的に強化してきました。しかし、ハイブリッドクエリが最初に導入された時点では、カスタムソート条件に基づく検索結果のソートはサポートされていませんでした。OpenSearch 2.16 では、ハイブリッドクエリにソート機能を追加し、アプリケーションが特定のフィールドや属性で結果をソートできるようになりました。この機能強化により、柔軟性が向上し、ハイブリッドクエリの関連性と使いやすさが改善されました。
この記事では、まずクエリ実行のワークフローを確認し、次にハイブリッドクエリでソートがどのように機能するかを説明し、最後にこの機能の使用方法を紹介します。
クエリ実行のワークフロー
クエリ実行は大きく分けて、クエリフェーズとフェッチフェーズの 2 つのフェーズに分かれています。クエリフェーズでは、OpenSearch は各シャードからドキュメント ID と関連性スコアを含むオブジェクトとしてマッチする結果を取得します。これらの結果はフェッチフェーズに移り、各ドキュメント ID に対応する完全なテキストがシャードから取得されます。最後に、OpenSearch はすべてのシャードからの結果を結合し、最終的な検索レスポンスを生成してユーザーに送信します。クエリ処理の詳細については、こちらの記事で説明されています。
ハイブリッドクエリにおけるソートの概要
一般的なハイブリッドクエリでは、レキシカルとセマンティックの両方の関連性スコアに基づいて結果が取得されます。各シャードからスコアの高いドキュメントが結合され、他のソート条件よりも関連性を優先した最終結果が形成されます。
このプロセスでソートを有効にすると、指定されたソート条件に従ってサブクエリの結果が取得されます。ソートされたサブクエリの結果はコーディネーターレベルでマージされ、関連性スコアのみに依存せず、ユーザーが要求したソート順序に従った最終出力が生成されます。
以下の例は、ハイブリッドクエリにおけるソートのワークフローを示しています。この例では、ユーザーが match と term の 2 つのサブクエリを持つハイブリッドクエリを実行し、検索結果を株価の降順でソートするよう要求しています。
まず、ハイブリッドクエリは match と term のサブクエリを個別に実行し、各結果セットを指定された条件 (この場合は株価) でソートします。従来のソートとは異なり、関連性スコアを無視するのではなく、ハイブリッドクエリではクエリフェーズの結果に関連性スコアとソートフィールドの両方が含まれます。これらの値は正規化プロセスで使用されます。正規化中、関連性スコアは正規化スコアの計算に使用され、サブクエリの結果を結合する際のタイブレーカーとして機能します。最後に、サブクエリの結果はコーディネーターレベルで結合され、要求どおり株価の降順でソートされた最終結果が形成されます。
以下の図は、クエリフェーズのワークフローと、ソート条件に基づく最終シャード結果の作成を示しています。

ハイブリッドクエリにおけるソートの詳細
先ほど説明した概要を踏まえて、ハイブリッドクエリにおけるソートの詳細なプロセスを見ていきましょう。これは 2 つの主要なステップに分けられます。
- シャードレベルでの個々のサブクエリ結果のソート
- コーディネーターノードでのソート条件に基づく複数のサブクエリ結果のマージ
以下の図は、これらのステップを詳細に示しています。

コーディネーターノードは、各シャードからクエリ結果を取得するためにデータノードにリクエストを送信します。内部的には、クエリフェーズ中に以下のプロセスが行われます。
- 各シャードでコレクターが実行され、各サブクエリの結果を収集します。結果が収集されると、ソート条件に基づいて順序付けられた優先度キューに挿入されます。
- クエリフェーズの終了時に、サブクエリの結果は優先度キューからソートされた順序でポップされます。
コーディネーターノードがすべてのデータノードからレスポンスを受信すると、正規化プロセスが開始されます。このプロセスでは、各サブクエリの結果にスコアを割り当て、指定されたソート条件に従って最終的なソート済みリストにマージします。正規化後、結果はフェッチフェーズに移り、ドキュメント ID を使用してシャードから完全なドキュメント内容が取得されます。最終的な検索レスポンスがユーザーに送信されます。
ソートの制限事項
検索結果の生成に伴う複雑さのため、ハイブリッドクエリのソートには以下の制限があります。
-
複数フィールドでのソートに
_scoreを含めることはできない: サブクエリの結果はソートフィールドまたは_scoreのいずれかに基づいてのみ結合できるため、フィールドの 1 つが_scoreの場合、複数フィールドでソートすることはできません。したがって、以下のようなクエリはエラーを返します。
{
"sort": [
{
"foo": {
"order": "desc"
}
},
{
"_score": {
"order": "asc"
}
}
]
}
-
ソートは
track_scoresと互換性がない: 検索リクエストでtrack_scores=trueを指定すると、OpenSearch はフェッチフェーズ中にスコアを計算します。しかし、ハイブリッドクエリではこれができません。ハイブリッドクエリでは、スコアは最初にクエリフェーズで計算され、その後正規化プロセッサによって正規化されます。track_scoresが有効な場合、ソート中にスコアの再計算がトリガーされますが、再計算されたスコアは先に実行された正規化を反映しないため、不正確な結果になる可能性があります。
ソートの使用方法
ソートを使用するには、ハイブリッドクエリに sort 句を追加し、ソート条件を定義します。
GET /my-nlp-index/_search?search_pipeline=nlp-search-pipeline
{
"query": {
"hybrid": {
"queries": [
{
"match": {
"title": "wind"
}
},
{
"knn": {
"location": {
"vector": [5, 4],
"k": 3
}
}
}
]
}
},
"sort": [
{
"foo": {
"order": "desc"
}
}
]
}
詳細については、ハイブリッドクエリでのソートの使用を参照してください。
まとめ
この記事では、個々のサブクエリ結果のソートから、選択した条件に基づく結果の結合まで、ハイブリッドクエリでソートがどのように機能するかを説明しました。詳細については、ハイブリッド検索のドキュメントを参照してください。説明可能性やページネーションなど、ハイブリッドクエリをより柔軟で便利にする今後の機能にご期待ください。
OpenSearch Project(OSS) の Publicationです。 OpenSearch Tokyo User Group : meetup.com/opensearch-project-tokyo/
Discussion