検索エンジンにおいて、ユーザーが最も関心を持つ機能は、検索とソートです。検索機能では、条件に一致するすべてのドキュメントを取得できます。ソート機能では、条件に一致するドキュメントの中から、関連性の最も高いドキュメントを先に取得できます。ソート機能を使用する場合は、ビジネス要件に基づいて関連設定を調整する必要があります。そのため、OpenSearchが提供するソート機能をよく理解しておく必要があります。このトピックでは、OpenSearchが提供するソート機能の詳細と、ソート機能を適用できる一般的なシナリオについて説明します。
ソート句とソートポリシーの関係
ソート句はOpenSearchでのグローバルソートに使用され、ソートポリシーはソート句における階層的なソート方法として理解できます。ソートポリシーは、組み込み関数と式を使用して複雑なドキュメントスコアリングロジックを形成し、複雑なビジネスシナリオに適用します。ただし、ソートは、ソートポリシーの式を使用して計算された最終スコアに基づいて実装されます。たとえば、ドキュメントの新しさに基づいてドキュメントをソートする場合、同じ新しさレベルのドキュメントは、ドキュメントの類似性に基づいて再度ソートできます。上記の要件を満たすには、ソート句とソートポリシーを一緒に使用する必要があります。ビジネステーブルにcreate_timeフィールドが含まれていて、nameフィールドに基づいてドキュメントを取得する場合は、ソート句に次の内容を追加します。
sort=-create_time;-RANK // create_timeの降順、次にRANKの降順でソート大まかなソートポリシーでstatic_bm25()関数、細かいソートポリシーでtext_relevance(name)関数を設定します。ソートポリシーの設定方法の詳細については、ソートポリシーの設定を参照してください。
RANKは、ソートポリシーに基づいて取得されたスコアを示します。マイナス記号(-)は降順、プラス記号(+)は昇順を示します。
デフォルトでは、ソート句が設定されていない場合、-RANKがシステムによってソート条件として使用されます。ソート句が設定されている場合、ソートポリシーから取得したスコアに基づいてソートを実行するときは、-RANKをソート句に明示的に記述する必要があります。そうしないと、システムはスコアをソート条件として自動的に導入しません。
次の例では、ソート句とソートポリシーの関係について説明します。
この例では、OpenSearchにアプリケーションが存在します。次の表は、アプリケーションスキーマを示しています。
フィールド | タイプ | インデックス |
id | int | キーワード |
name | text | 中国語の汎用インデックス |
age | int | キーワード |
nameフィールドに大まかなソートポリシーが設定されています。式は次の図に示されています。

nameフィールドに細かいソートポリシーが設定されています。式は次の図に示されています。

データを取得するときは、ソート句を次の形式で設定します。
sort=age;-RANK // ageの昇順、次にRANKの降順でソートソート詳細の表示をオンにして、スコア計算の詳細を表示します。

まず、ソートスコアが13,10000.2259030193であることがわかります。ソート句はage;-RANKに設定されています。したがって、13はageフィールドの値を示し、10000.2259030193はソートポリシーの最終スコアを示します。OpenSearchは、ageフィールドの値に基づいて昇順でドキュメントをソートし、次に同じage値を持つドキュメントをソートポリシーの最終スコアに基づいて降順でソートします。
次に、ソート式が表示されます。
FirstRank: // 第一段階ランク
expression[static_bm25()], result[0.496452]. // 式[static_bm25()]、結果[0.496452]
SecondRank: // 第二段階ランク
expression[text_relevance(name)], result[0.225903]. // 式[text_relevance(name)]、結果[0.225903]
FirstRankは大まかなソートポリシーのスコアを示し、SecondRankは細かいソートポリシーのスコアを示します。最終スコアは10000.2259030193です。最終スコアが[0.496452+0.225903]ではなく10000.2259030193である理由は、次のセクションで詳しく説明します。
上記の例の結果は、OpenSearchのソート句がSQLのORDER BY句に似ていることを示しています。ソート句に属性フィールドを指定してドキュメントをソートできます。また、スコア計算に複雑なソートポリシーを使用することもできます。ソートポリシーには独自の関数とスコア計算ルールがあります。最終的に、ソート句は、ソート句のマイナス記号(-)またはプラス記号(+)とソートフィールドに基づいて、ドキュメントとスコアのソート方法を決定します。
ソートポリシー
ソートポリシーのスコアリング原則
ソートポリシーのスコア計算は、大まかなソートと細かいソートの2つの段階に分かれています。クエリ句を使用してドキュメントが取得され、フィルター句を使用してフィルタリングされた後、大まかなソートが実装されます。この段階では、大まかなソート式に基づいてドキュメントのスコアが計算されます。次に、上位Nスコアのドキュメントがスコアリングされ、細かいソート式に基づいてソートされます。最後に、ソートポリシーの最終スコアが返されます。次のスコア計算ルールに注意してください。
大まかなソートポリシーのみが設定されている場合、ドキュメントスコアは10,000に大まかなソート式を使用して計算された結果を加えた値になります。ドキュメントスコアの最大値は20,000です。実際のドキュメントスコアが20,000を超える場合でも、表示されるスコアは20,000のままです。
細かいソートポリシーのみが設定されている場合、ドキュメントスコアは10,000に細かいソート式を使用して計算された結果を加えた値になります。ドキュメントスコアに上限はありません。
大まかなソートポリシーと細かいソートポリシーの両方が設定されている場合、細かいソート段階に入ったドキュメントの最終スコアは10,000に細かいソート式を使用して計算された結果を加えた値になり、大まかにソートされただけの他のドキュメントの最終スコアは10,000に大まかなソート式を使用して計算された結果を加えた値になります。最終スコアの最大値は20,000です。実際のドキュメントスコアが20,000を超える場合でも、表示されるスコアは20,000のままです。
前のセクションで計算されたスコア:
FirstRank: // 第一段階ランク
expression[static_bm25()], result[0.496452]. // 式[static_bm25()]、結果[0.496452]
SecondRank: // 第二段階ランク
expression[text_relevance(name)], result[0.225903]. // 式[text_relevance(name)]、結果[0.225903]
最終スコアは10000.2259030193です。
上記の原則に基づいて、ヒットドキュメントは取得された100万件のドキュメントの1つです。ドキュメントの大まかなソートスコアは0.496452で、static_bm25関数を使用して計算されます。ドキュメントは大まかなソートスコアに基づいてすべてのヒットドキュメントの中で上位200にランクされます。したがって、ドキュメントは細かいソート段階に入ります。ドキュメントが大まかなソート段階から細かいソート段階に入ると、デフォルトでスコアに10,000ポイントが加算され、ドキュメントの大まかなソートスコアは破棄されます。最終的なソートポリシースコアは、細かいソートスコアに10,000を加えた値です。ドキュメントスコアは0.225903で、細かいソートポリシーのtext_relevance関数を使用して計算されます。したがって、ドキュメントの最終的なソートポリシースコアは10000.2259030193です。
細かいソート関数の使用方法
注:以下の組み込み関数によって参照されるアプリケーションスキーマ内のすべてのフィールドは、属性フィールドとして設定する必要があります。そうしないと、「無効な式」エラーが報告されます。
関数 | 説明 | 例 |
i in (value1, value2, …, valuen) | iの値が集合[value1, value2, …, valuen]に含まれている場合、1が返されます。それ以外の場合は、0が返されます。 | age=5 age in (1,2,3,4,5) # 1が返されます。 age in (6,7,8,9) # 0が返されます。 |
if(cond, then_value, else_value) | condパラメータの値が0でない場合、then_valueパラメータの値が返されます。それ以外の場合は、else_valueパラメータの値が返されます。たとえば、if(2,3,5)の値は3、if(0,3,5)の値は5です。 | a=1 if(a==1,5,10) # 5が返されます。 if(1,5,10) # 5が返されます。 if(a==2,5,10) # 10が返されます。 if(0,5,10) # 10が返されます。 |
random() | [0,1]の範囲のランダムな値を返します。 | - |
now() | UTCの1970年1月1日00:00:00から経過した秒数を返します。 | - |
テキスト関連度、地理的位置関連度、適時性、アルゴリズム関連度、および機能関数については、細かいソート関数を参照してください。
一般的な数値関数、式、演算子については、ソートポリシーの設定を参照してください。
ソートポリシーの式が複雑なシナリオでのスコア計算の要件を依然として満たせない場合は、Cavaプラグインを使用してスクリプトを記述できます。Cavaプラグインの使用方法と原則については、ソートプラグイン開発のためのCavaを参照してください。
一般的なシナリオでのソートポリシーの設定
1. ageフィールドの値が10より大きい場合は10ポイント、40より大きい場合は20ポイント、weightフィールドの値が60より大きい場合は30ポイントをスコアに加算するように設定します。次に、最終スコアに基づいてソートを実行します。
実装1:
# 次の細かいソート式を設定します。デフォルトでは、条件が満たされたときにスコアに1ポイントだけ加算されます。
(age>10)*10+(age>40)*20+(weight>60)*30 // ageが10より大きい場合は10ポイント、40より大きい場合は20ポイント、weightが60より大きい場合は30ポイントを加算
実装2:
# 次の細かいソート式を設定します。
if(age>10,10,0) + if(age>40,20,0) +if(weight>60,30,0) // ageが10より大きい場合は10ポイント、40より大きい場合は20ポイント、weightが60より大きい場合は30ポイントを加算
2. 「xxx会社」を「xxx杭州支店」の前にランク付けします。
実装:
# 細かいソート式でfield_match_ratio関数を設定します。
field_match_ratio(title) // タイトルフィールドの一致率に基づいてソート
3. all:'dim_itm_tb'クエリによって返された結果について、「dim_itm_tb」を「dim_itm_tb_dst_itm_relation_dd」の前にランク付けします。
実装:
# 細かいソート式でfield_match_ratio関数を設定します。
field_match_ratio(detail) // 詳細フィールドの一致率に基づいてソート
4. query = item:"iphone 8" OR item: 'iphone 8'に類似したクエリを実装します。
実装:
# 細かいソート式でquery_min_slide_window関数を設定します。
query_min_slide_window(title) // タイトルフィールドにおけるクエリ用語の最小スライドウィンドウに基づいてソート
5. 細かいソート式にtext_relevance関数が設定され、検索キーワードが「中華民国」に設定されている場合、「中華民国」を含むドキュメントを「中華民国の興味深いニュース-中華民国」または「中国の国史-中華民国」を含むドキュメントの前にランク付けします。
実装:
# 細かいソート式でquery_min_slide_window関数を設定します。
query_min_slide_window(title) // タイトルフィールドにおけるクエリ用語の最小スライドウィンドウに基づいてソート
6. 特定のフィールドで検索キーワードが繰り返しヒットした場合に、static_bm25()関数がスコアを繰り返し計算しないようにします。
実装:
# 細かいソート式でquery_match_ratio関数を設定します。
query_match_ratio(title) // タイトルフィールドのクエリ一致率に基づいてソート
7. スタックされた検索キーワードを含むドキュメントを後ろにランク付けします。
実装:
# query_term_match_count関数を設定して、ドキュメントで検索キーワードがヒットした回数に基づいて、ドキュメントにキーワードがスタックされているかどうかを確認します。
if(field_term_match_count(title)>3,1,10) // タイトルフィールドのキーワード一致回数が3より大きい場合は1、それ以外の場合は10を返す
8. 文字列が空でない場合に、ソートスコアに特定のポイントが加算されるように設定します。
実装:
ソースデータベースにmarkフィールドを追加します。文字列が空の場合、markフィールドの値は0です。それ以外の場合は、値は1です。次に、細かいソート式でif関数を設定して、文字列が空かどうかを確認します。
次の細かいソート式を設定します。markフィールドの値が1の場合、ソートスコアに500ポイントが加算されます。
if(mark==1,500,0) // markが1の場合は500、それ以外の場合は0を返す