相互ランク融合

この機能は技術プレビュー中であり、将来のリリースで変更または削除される可能性があります。GAの前に構文が変更される可能性があります。Elasticは問題を修正するために取り組みますが、技術プレビューの機能は公式GA機能のサポートSLAの対象ではありません。

相互ランク融合 (RRF)は、異なる関連性指標を持つ複数の結果セットを単一の結果セットに結合する方法です。RRFは調整を必要とせず、異なる関連性指標は高品質の結果を得るために互いに関連している必要はありません。

RRFは、各ドキュメントのランク付けスコアを決定するために次の式を使用します:

Python

  1. score = 0.0
  2. for q in queries:
  3. if d in result(q):
  4. score += 1.0 / ( k + rank( result(q), d ) )
  5. return score
  6. # どこ
  7. # kはランク付け定数です
  8. # qはクエリのセット内のクエリです
  9. # dはqの結果セット内のドキュメントです
  10. # result(q)はqの結果セットです
  11. # rank( result(q), d )は、1から始まるresult(q)内のdのランクです

相互ランク融合API

RRFを検索の一部として使用して、子リトリーバーの組み合わせからの別々のトップドキュメントセット(結果セット)を使用してドキュメントを結合およびランク付けできます。RRFリトリーバーを使用するには、少なくとも2つの子リトリーバーが必要です。

RRFリトリーバーは、検索リクエストのリトリーバーパラメータの一部として定義されたオプションのオブジェクトです。RRFリトリーバーオブジェクトには次のパラメータが含まれます:

  • retrievers
  • (必須、リトリーバーオブジェクトの配列)
    RRF式が適用される返されたトップドキュメントのセットを指定するための子リトリーバーのリスト。各子リトリーバーはRRF式の一部として等しい重みを持ちます。2つ以上の子リトリーバーが必要です。
  • rank_constant
  • (オプション、整数)
    この値は、各クエリの個別の結果セット内のドキュメントが最終的なランク付け結果セットに与える影響の程度を決定します。値が高いほど、低ランクのドキュメントがより多くの影響を与えます。この値は1以上でなければなりません。デフォルトは60です。
  • rank_window_size
  • (オプション、整数)
    この値は、各クエリの個別の結果セットのサイズを決定します。値が高いほど、パフォーマンスのコストで結果の関連性が向上します。最終的なランク付け結果セットは、検索リクエストのサイズに基づいてプルーニングされます。rank_window_sizesize以上であり、1以上でなければなりません。デフォルトはsizeパラメータです。

RRFを使用したリクエストの例:

Python

  1. resp = client.search(
  2. index="example-index",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "text": "shoes"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "knn": {
  17. "field": "vector",
  18. "query_vector": [
  19. 1.25,
  20. 2,
  21. 3.5
  22. ],
  23. "k": 50,
  24. "num_candidates": 100
  25. }
  26. }
  27. ],
  28. "rank_window_size": 50,
  29. "rank_constant": 20
  30. }
  31. },
  32. )
  33. print(resp)

Js

  1. const response = await client.search({
  2. index: "example-index",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. term: {
  10. text: "shoes",
  11. },
  12. },
  13. },
  14. },
  15. {
  16. knn: {
  17. field: "vector",
  18. query_vector: [1.25, 2, 3.5],
  19. k: 50,
  20. num_candidates: 100,
  21. },
  22. },
  23. ],
  24. rank_window_size: 50,
  25. rank_constant: 20,
  26. },
  27. },
  28. });
  29. console.log(response);

コンソール

  1. GET example-index/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "text": "shoes"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "knn": {
  17. "field": "vector",
  18. "query_vector": [1.25, 2, 3.5],
  19. "k": 50,
  20. "num_candidates": 100
  21. }
  22. }
  23. ],
  24. "rank_window_size": 50,
  25. "rank_constant": 20
  26. }
  27. }
  28. }

上記の例では、knnおよびstandardリトリーバーを互いに独立して実行します。次に、rrfリトリーバーを使用して結果を結合します。

最初に、knnリトリーバーによって指定されたkNN検索を実行して、そのグローバルトップ50結果を取得します。
次に、standardリトリーバーによって指定されたクエリを実行して、そのグローバルトップ50結果を取得します。
次に、コーディネートノードで、kNN検索のトップドキュメントとクエリのトップドキュメントを結合し、rrfリトリーバーのパラメータを使用してRRF式に基づいてランク付けし、デフォルトのsizeを使用して10のトップドキュメントを取得します。
  1. ## 相互ランク融合のサポートされている機能
  2. `````rrf`````リトリーバーは次のことをサポートしています:
  3. - [集約](/read/elasticsearch-8-15/396e71470b74cb38.md)
  4. - [from](89159571f146b334.md#search-from-param)
  5. `````rrf`````リトリーバーは現在次のことをサポートしていません:
  6. - [スクロール](89159571f146b334.md#search-api-scroll-query-param)
  7. - [ポイントインタイム](89159571f146b334.md#search-api-pit)
  8. - [ソート](89159571f146b334.md#search-sort-param)
  9. - [再スコア](3f786c09568adf05.md#rescore)
  10. - [サジェスター](/read/elasticsearch-8-15/39d4e7248968ca4f.md)
  11. - [ハイライト](/read/elasticsearch-8-15/44d174e4822907ce.md)
  12. - [コラプス](/read/elasticsearch-8-15/c30d224de91b0a6e.md)
  13. - [プロファイリング](237e89fe5c1c5ae9.md#profiling-queries)
  14. `````rrf`````リトリーバーを使用した検索の一部としてサポートされていない機能を使用すると、例外が発生します。
  15. ## 複数の標準リトリーバーを使用した相互ランク融合
  16. `````rrf`````リトリーバーは、複数の`````standard`````リトリーバーを結合およびランク付けする方法を提供します。主な使用ケースは、従来のBM25クエリと[ELSER](/read/elasticsearch-8-15/070e47d72203e3e4.md)クエリからのトップドキュメントを結合して関連性を向上させることです。
  17. 複数の標準リトリーバーを使用したRRFのリクエストの例:
  18. #### Python
  19. ``````python
  20. resp = client.search(
  21. index="example-index",
  22. retriever={
  23. "rrf": {
  24. "retrievers": [
  25. {
  26. "standard": {
  27. "query": {
  28. "term": {
  29. "text": "blue shoes sale"
  30. }
  31. }
  32. }
  33. },
  34. {
  35. "standard": {
  36. "query": {
  37. "sparse_vector": {
  38. "field": "ml.tokens",
  39. "inference_id": "my_elser_model",
  40. "query": "What blue shoes are on sale?"
  41. }
  42. }
  43. }
  44. }
  45. ],
  46. "rank_window_size": 50,
  47. "rank_constant": 20
  48. }
  49. },
  50. )
  51. print(resp)
  52. `

Js

  1. const response = await client.search({
  2. index: "example-index",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. term: {
  10. text: "blue shoes sale",
  11. },
  12. },
  13. },
  14. },
  15. {
  16. standard: {
  17. query: {
  18. sparse_vector: {
  19. field: "ml.tokens",
  20. inference_id: "my_elser_model",
  21. query: "What blue shoes are on sale?",
  22. },
  23. },
  24. },
  25. },
  26. ],
  27. rank_window_size: 50,
  28. rank_constant: 20,
  29. },
  30. },
  31. });
  32. console.log(response);

コンソール

  1. GET example-index/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "text": "blue shoes sale"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "standard": {
  17. "query": {
  18. "sparse_vector":{
  19. "field": "ml.tokens",
  20. "inference_id": "my_elser_model",
  21. "query": "What blue shoes are on sale?"
  22. }
  23. }
  24. }
  25. }
  26. ],
  27. "rank_window_size": 50,
  28. "rank_constant": 20
  29. }
  30. }
  31. }

上記の例では、2つのstandardリトリーバーを互いに独立して実行します。次に、rrfリトリーバーを使用して結果を結合します。

最初に、standardリトリーバーを実行し、標準BM25スコアリングアルゴリズムを使用してblue shoes salesの用語クエリを指定します。
次に、standardリトリーバーを実行し、ELSERスコアリングアルゴリズムを使用してWhat blue shoes are on sale?のスパースベクタークエリを指定します。
rrfリトリーバーは、完全に独立したスコアリングアルゴリズムによって生成された2つのトップドキュメントセットを等しい重みで結合することを可能にします。

これにより、線形結合を使用して適切な重みを決定する必要がなくなるだけでなく、RRFは個々のクエリよりも改善された関連性を提供することが示されています。

サブ検索を使用した相互ランク融合

サブ検索を使用したRRFはもはやサポートされていません。リトリーバーAPIを代わりに使用してください。複数の標準リトリーバーを使用するの例を参照してください。

相互ランク融合の完全な例

テキストフィールド、ベクターフィールド、整数フィールドを持つインデックスのマッピングを作成し、いくつかのドキュメントをインデックスします。この例では、ランク付けを説明しやすくするために、単一の次元のみを持つベクターを使用します。

Python

  1. resp = client.indices.create(
  2. index="example-index",
  3. mappings={
  4. "properties": {
  5. "text": {
  6. "type": "text"
  7. },
  8. "vector": {
  9. "type": "dense_vector",
  10. "dims": 1,
  11. "index": True,
  12. "similarity": "l2_norm",
  13. "index_options": {
  14. "type": "hnsw"
  15. }
  16. },
  17. "integer": {
  18. "type": "integer"
  19. }
  20. }
  21. },
  22. )
  23. print(resp)
  24. resp1 = client.index(
  25. index="example-index",
  26. id="1",
  27. document={
  28. "text": "rrf",
  29. "vector": [
  30. 5
  31. ],
  32. "integer": 1
  33. },
  34. )
  35. print(resp1)
  36. resp2 = client.index(
  37. index="example-index",
  38. id="2",
  39. document={
  40. "text": "rrf rrf",
  41. "vector": [
  42. 4
  43. ],
  44. "integer": 2
  45. },
  46. )
  47. print(resp2)
  48. resp3 = client.index(
  49. index="example-index",
  50. id="3",
  51. document={
  52. "text": "rrf rrf rrf",
  53. "vector": [
  54. 3
  55. ],
  56. "integer": 1
  57. },
  58. )
  59. print(resp3)
  60. resp4 = client.index(
  61. index="example-index",
  62. id="4",
  63. document={
  64. "text": "rrf rrf rrf rrf",
  65. "integer": 2
  66. },
  67. )
  68. print(resp4)
  69. resp5 = client.index(
  70. index="example-index",
  71. id="5",
  72. document={
  73. "vector": [
  74. 0
  75. ],
  76. "integer": 1
  77. },
  78. )
  79. print(resp5)
  80. resp6 = client.indices.refresh(
  81. index="example-index",
  82. )
  83. print(resp6)

Js

  1. const response = await client.indices.create({
  2. index: "example-index",
  3. mappings: {
  4. properties: {
  5. text: {
  6. type: "text",
  7. },
  8. vector: {
  9. type: "dense_vector",
  10. dims: 1,
  11. index: true,
  12. similarity: "l2_norm",
  13. index_options: {
  14. type: "hnsw",
  15. },
  16. },
  17. integer: {
  18. type: "integer",
  19. },
  20. },
  21. },
  22. });
  23. console.log(response);
  24. const response1 = await client.index({
  25. index: "example-index",
  26. id: 1,
  27. document: {
  28. text: "rrf",
  29. vector: [5],
  30. integer: 1,
  31. },
  32. });
  33. console.log(response1);
  34. const response2 = await client.index({
  35. index: "example-index",
  36. id: 2,
  37. document: {
  38. text: "rrf rrf",
  39. vector: [4],
  40. integer: 2,
  41. },
  42. });
  43. console.log(response2);
  44. const response3 = await client.index({
  45. index: "example-index",
  46. id: 3,
  47. document: {
  48. text: "rrf rrf rrf",
  49. vector: [3],
  50. integer: 1,
  51. },
  52. });
  53. console.log(response3);
  54. const response4 = await client.index({
  55. index: "example-index",
  56. id: 4,
  57. document: {
  58. text: "rrf rrf rrf rrf",
  59. integer: 2,
  60. },
  61. });
  62. console.log(response4);
  63. const response5 = await client.index({
  64. index: "example-index",
  65. id: 5,
  66. document: {
  67. vector: [0],
  68. integer: 1,
  69. },
  70. });
  71. console.log(response5);
  72. const response6 = await client.indices.refresh({
  73. index: "example-index",
  74. });
  75. console.log(response6);

コンソール

  1. PUT example-index
  2. {
  3. "mappings": {
  4. "properties": {
  5. "text" : {
  6. "type" : "text"
  7. },
  8. "vector": {
  9. "type": "dense_vector",
  10. "dims": 1,
  11. "index": true,
  12. "similarity": "l2_norm",
  13. "index_options": {
  14. "type": "hnsw"
  15. }
  16. },
  17. "integer" : {
  18. "type" : "integer"
  19. }
  20. }
  21. }
  22. }
  23. PUT example-index/_doc/1
  24. {
  25. "text" : "rrf",
  26. "vector" : [5],
  27. "integer": 1
  28. }
  29. PUT example-index/_doc/2
  30. {
  31. "text" : "rrf rrf",
  32. "vector" : [4],
  33. "integer": 2
  34. }
  35. PUT example-index/_doc/3
  36. {
  37. "text" : "rrf rrf rrf",
  38. "vector" : [3],
  39. "integer": 1
  40. }
  41. PUT example-index/_doc/4
  42. {
  43. "text" : "rrf rrf rrf rrf",
  44. "integer": 2
  45. }
  46. PUT example-index/_doc/5
  47. {
  48. "vector" : [0],
  49. "integer": 1
  50. }
  51. POST example-index/_refresh
  1. #### Python
  2. ``````python
  3. resp = client.search(
  4. index="example-index",
  5. retriever={
  6. "rrf": {
  7. "retrievers": [
  8. {
  9. "standard": {
  10. "query": {
  11. "term": {
  12. "text": "rrf"
  13. }
  14. }
  15. }
  16. },
  17. {
  18. "knn": {
  19. "field": "vector",
  20. "query_vector": [
  21. 3
  22. ],
  23. "k": 5,
  24. "num_candidates": 5
  25. }
  26. }
  27. ],
  28. "rank_window_size": 5,
  29. "rank_constant": 1
  30. }
  31. },
  32. size=3,
  33. aggs={
  34. "int_count": {
  35. "terms": {
  36. "field": "integer"
  37. }
  38. }
  39. },
  40. )
  41. print(resp)
  42. `

Js

  1. const response = await client.search({
  2. index: "example-index",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. term: {
  10. text: "rrf",
  11. },
  12. },
  13. },
  14. },
  15. {
  16. knn: {
  17. field: "vector",
  18. query_vector: [3],
  19. k: 5,
  20. num_candidates: 5,
  21. },
  22. },
  23. ],
  24. rank_window_size: 5,
  25. rank_constant: 1,
  26. },
  27. },
  28. size: 3,
  29. aggs: {
  30. int_count: {
  31. terms: {
  32. field: "integer",
  33. },
  34. },
  35. },
  36. });
  37. console.log(response);

コンソール

  1. GET example-index/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "text": "rrf"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "knn": {
  17. "field": "vector",
  18. "query_vector": [3],
  19. "k": 5,
  20. "num_candidates": 5
  21. }
  22. }
  23. ],
  24. "rank_window_size": 5,
  25. "rank_constant": 1
  26. }
  27. },
  28. "size": 3,
  29. "aggs": {
  30. "int_count": {
  31. "terms": {
  32. "field": "integer"
  33. }
  34. }
  35. }
  36. }

ランク付けされたhitsと用語集約結果を持つ応答を受け取ります。ランカーのscore_rankオプションの両方を使用して、トップランクのドキュメントを表示できます。

コンソール-応答

  1. {
  2. "took": ...,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 5,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [
  17. {
  18. "_index" : "example-index",
  19. "_id" : "3",
  20. "_score" : 0.8333334,
  21. "_rank" : 1,
  22. "_source" : {
  23. "integer" : 1,
  24. "vector" : [
  25. 3
  26. ],
  27. "text" : "rrf rrf rrf"
  28. }
  29. },
  30. {
  31. "_index" : "example-index",
  32. "_id" : "2",
  33. "_score" : 0.5833334,
  34. "_rank" : 2,
  35. "_source" : {
  36. "integer" : 2,
  37. "vector" : [
  38. 4
  39. ],
  40. "text" : "rrf rrf"
  41. }
  42. },
  43. {
  44. "_index" : "example-index",
  45. "_id" : "4",
  46. "_score" : 0.5,
  47. "_rank" : 3,
  48. "_source" : {
  49. "integer" : 2,
  50. "text" : "rrf rrf rrf rrf"
  51. }
  52. }
  53. ]
  54. },
  55. "aggregations" : {
  56. "int_count" : {
  57. "doc_count_error_upper_bound" : 0,
  58. "sum_other_doc_count" : 0,
  59. "buckets" : [
  60. {
  61. "key" : 1,
  62. "doc_count" : 3
  63. },
  64. {
  65. "key" : 2,
  66. "doc_count" : 2
  67. }
  68. ]
  69. }
  70. }
  71. }

これらのヒットがどのようにランク付けされたかを分解しましょう。最初に、standardリトリーバーを実行し、クエリを指定し、knnリトリーバーを実行してkNN検索を個別に収集します。

最初に、standardリトリーバーからのクエリのヒットを見てみましょう。

コンソール-結果

  1. "hits" : [
  2. {
  3. "_index" : "example-index",
  4. "_id" : "4",
  5. "_score" : 0.16152832,
  6. "_source" : {
  7. "integer" : 2,
  8. "text" : "rrf rrf rrf rrf"
  9. }
  10. },
  11. {
  12. "_index" : "example-index",
  13. "_id" : "3",
  14. "_score" : 0.15876243,
  15. "_source" : {
  16. "integer" : 1,
  17. "vector" : [3],
  18. "text" : "rrf rrf rrf"
  19. }
  20. },
  21. {
  22. "_index" : "example-index",
  23. "_id" : "2",
  24. "_score" : 0.15350538,
  25. "_source" : {
  26. "integer" : 2,
  27. "vector" : [4],
  28. "text" : "rrf rrf"
  29. }
  30. },
  31. {
  32. "_index" : "example-index",
  33. "_id" : "1",
  34. "_score" : 0.13963442,
  35. "_source" : {
  36. "integer" : 1,
  37. "vector" : [5],
  38. "text" : "rrf"
  39. }
  40. }
  41. ]
ランク1、_id 4
ランク2、_id 3
ランク3、_id 2
ランク4、_id 1

最初のヒットにはvectorフィールドの値がありません。次に、knnリトリーバーからのkNN検索の結果を見てみましょう。

コンソール-結果

  1. "hits" : [
  2. {
  3. "_index" : "example-index",
  4. "_id" : "3",
  5. "_score" : 1.0,
  6. "_source" : {
  7. "integer" : 1,
  8. "vector" : [3],
  9. "text" : "rrf rrf rrf"
  10. }
  11. },
  12. {
  13. "_index" : "example-index",
  14. "_id" : "2",
  15. "_score" : 0.5,
  16. "_source" : {
  17. "integer" : 2,
  18. "vector" : [4],
  19. "text" : "rrf rrf"
  20. }
  21. },
  22. {
  23. "_index" : "example-index",
  24. "_id" : "1",
  25. "_score" : 0.2,
  26. "_source" : {
  27. "integer" : 1,
  28. "vector" : [5],
  29. "text" : "rrf"
  30. }
  31. },
  32. {
  33. "_index" : "example-index",
  34. "_id" : "5",
  35. "_score" : 0.1,
  36. "_source" : {
  37. "integer" : 1,
  38. "vector" : [0]
  39. }
  40. }
  41. ]
ランク1、_id 3
ランク2、_id 2
ランク3、_id 1
ランク4、_id 5

これで、2つの個別にランク付けされた結果セットを取得し、rrfリトリーバーのパラメータを使用してRRF式を適用して最終的なランク付けを取得できます。

Python

  1. # doc | query | knn | score
  2. _id: 1 = 1.0/(1+4) + 1.0/(1+3) = 0.4500
  3. _id: 2 = 1.0/(1+3) + 1.0/(1+2) = 0.5833
  4. _id: 3 = 1.0/(1+2) + 1.0/(1+1) = 0.8333
  5. _id: 4 = 1.0/(1+1) = 0.5000
  6. _id: 5 = 1.0/(1+4) = 0.2000

RRF式に基づいてドキュメントをランク付けし、rank_window_size5でRRF結果セットの下部2ドキュメントを切り捨てます。最終的に_id: 3_rank: 1_id: 2_rank: 2_id: 4_rank: 3になります。このランク付けは、元のRRF検索からの結果セットと一致します。

RRFでの説明

個々のクエリスコアリングの詳細に加えて、explain=trueパラメータを使用して、各ドキュメントのRRFスコアがどのように計算されたかに関する情報を取得できます。上記の例を使用し、explain=trueを検索リクエストに追加することで、次のような応答が得られます:

Js

  1. {
  2. "hits":
  3. [
  4. {
  5. "_index": "example-index",
  6. "_id": "3",
  7. "_score": 0.8333334,
  8. "_rank": 1,
  9. "_explanation":
  10. {
  11. "value": 0.8333334,
  12. "description": "rrf score: [0.8333334] computed for initial ranks [2, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",
  13. "details":
  14. [
  15. {
  16. "value": 2,
  17. "description": "rrf score: [0.33333334], for rank [2] in query at index [0] computed as [1 / (2 + 1]), for matching query with score: ",
  18. "details":
  19. [
  20. {
  21. "value": 0.15876243,
  22. "description": "weight(text:rrf in 0) [PerFieldSimilarity], result of:",
  23. "details":
  24. [
  25. ...
  26. ]
  27. }
  28. ]
  29. },
  30. {
  31. "value": 1,
  32. "description": "rrf score: [0.5], for rank [1] in query at index [1] computed as [1 / (1 + 1]), for matching query with score: ",
  33. "details":
  34. [
  35. {
  36. "value": 1,
  37. "description": "within top k documents",
  38. "details":
  39. []
  40. }
  41. ]
  42. }
  43. ]
  44. }
  45. }
  46. ...
  47. ]
  48. }
_id=3を持つドキュメントの最終RRFスコア
このスコアが各個別のクエリにおけるこのドキュメントのランクに基づいて計算された方法の説明
各クエリのRRFスコアがどのように計算されたかの詳細
valueはこのドキュメントの特定のクエリにおけるrankを指定します
基本クエリの標準explain出力、マッチする用語と重みを説明
valueはこのドキュメントの2番目の(knn)クエリにおけるrankを指定します

上記に加えて、RRFでの説明は_nameパラメータを使用してnamed queriesもサポートしています。named queriesを使用すると、複数のクエリを扱う際にRRFスコア計算の理解が容易で直感的になります。したがって、次のようになります:

Js

  1. GET example-index/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "text": "rrf"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "knn": {
  17. "field": "vector",
  18. "query_vector": [3],
  19. "k": 5,
  20. "num_candidates": 5,
  21. "_name": "my_knn_query"
  22. }
  23. }
  24. ],
  25. "rank_window_size": 5,
  26. "rank_constant": 1
  27. }
  28. },
  29. "size": 3,
  30. "aggs": {
  31. "int_count": {
  32. "terms": {
  33. "field": "integer"
  34. }
  35. }
  36. }
  37. }
knnリトリーバーのための_nameを指定します

応答には、説明にnamed queryが含まれるようになります:

Js

  1. {
  2. "hits":
  3. [
  4. {
  5. "_index": "example-index",
  6. "_id": "3",
  7. "_score": 0.8333334,
  8. "_rank": 1,
  9. "_explanation":
  10. {
  11. "value": 0.8333334,
  12. "description": "rrf score: [0.8333334] computed for initial ranks [2, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",
  13. "details":
  14. [
  15. {
  16. "value": 2,
  17. "description": "rrf score: [0.33333334], for rank [2] in query at index [0] computed as [1 / (2 + 1]), for matching query with score: ",
  18. "details":
  19. [
  20. ...
  21. ]
  22. },
  23. {
  24. "value": 1,
  25. "description": "rrf score: [0.5], for rank [1] in query [my_knn_query] computed as [1 / (1 + 1]), for matching query with score: ",
  26. "details":
  27. [
  28. ...
  29. ]
  30. }
  31. ]
  32. }
  33. }
  34. ...
  35. ]
  36. }
匿名のat index nの代わりに、named query my_knn_queryへの参照があります。

RRFでのページネーション

  1. - `````from + size````` `````rank_window_size````` : 最終的な`````rrf`````ランク付け結果セットから`````results[from: from+size]`````ドキュメントを取得できます
  2. - `````from + size````` > `````rank_window_size````` : 利用可能な`````rank_window_size`````サイズの結果セットの外にリクエストがあるため、0結果が返されます。
  3. ここで重要なことは、`````rank_window_size`````が個々のクエリコンポーネントから見ることができるすべての結果であるため、ページネーションは一貫性を保証します。すなわち、`````rank_window_size`````が同じであれば、複数のページでドキュメントがスキップされたり重複したりすることはありません。`````rank_window_size`````が変更されると、同じランクでも結果の順序が変わる可能性があります。
  4. 上記のすべてを説明するために、`````queryA``````````queryB`````2つのクエリとそのランク付けされたドキュメントを持つ次の簡略化された例を考えてみましょう:
  5. #### Python
  6. ``````python
  7. | queryA | queryB |
  8. _id: | 1 | 5 |
  9. _id: | 2 | 4 |
  10. _id: | 3 | 3 |
  11. _id: | 4 | 1 |
  12. _id: | | 2 |
  13. `
  1. #### Python
  2. ``````python
  3. # doc | queryA | queryB | score
  4. _id: 1 = 1.0/(1+1) + 1.0/(1+4) = 0.7
  5. _id: 2 = 1.0/(1+2) + 1.0/(1+5) = 0.5
  6. _id: 3 = 1.0/(1+3) + 1.0/(1+3) = 0.5
  7. _id: 4 = 1.0/(1+4) + 1.0/(1+2) = 0.533
  8. _id: 5 = 0 + 1.0/(1+1) = 0.5
  9. `

したがって、最終的なランク付け結果セットは[1, 4, 2, 3, 5]となり、rank_window_size == len(results)のためにそれをページネートします。このシナリオでは、次のようになります:

  • from=0, size=2はランク[1, 2]のドキュメント[1, 4]を返します
  • from=2, size=2はランク[3, 4]のドキュメント[2, 3]を返します
  • from=4, size=2はランク[5]のドキュメント[5]を返します
  • from=6, size=2は、反復する結果がないため、空の結果セットを返します

さて、rank_window_size=2があれば、[1, 2][5, 4]のドキュメントをそれぞれqueryAqueryBのクエリに対して見ることができます。数学を解決すると、[3: end]の位置にあるドキュメントの知識がないため、結果がわずかに異なることがわかります。

Python

  1. # doc | queryA | queryB | score
  2. _id: 1 = 1.0/(1+1) + 0 = 0.5
  3. _id: 2 = 1.0/(1+2) + 0 = 0.33
  4. _id: 4 = 0 + 1.0/(1+2) = 0.33
  5. _id: 5 = 0 + 1.0/(1+1) = 0.5

最終的なランク付け結果セットは[1, 5, 2, 4]となり、トップrank_window_size結果、すなわち[1, 5]にページネートできます。したがって、上記と同じパラメータの場合、次のようになります:

  • from=0, size=2はランク[1, 2]の[1, 5]を返します
  • from=2, size=2は、利用可能なrank_window_size結果の外にあるため、空の結果セットを返します。