倒数排名融合

编辑

倒数排名融合 (RRF) 是一种将多个具有不同相关性指标的结果集组合成单个结果集的方法。RRF 不需要调整,不同的相关性指标之间也不必相互关联即可获得高质量的结果。

RRF 使用以下公式来确定每个文档的排名分数

score = 0.0
for q in queries:
    if d in result(q):
        score += 1.0 / ( k + rank( result(q), d ) )
return score

# where
# k is a ranking constant
# q is a query in the set of queries
# d is a document in the result set of q
# result(q) is the result set of q
# rank( result(q), d ) is d's rank within the result(q) starting from 1

倒数排名融合 API

编辑

你可以使用 RRF 作为 搜索 的一部分,使用 子检索器 的组合中的单独的顶部文档集(结果集),使用 RRF 检索器 来合并和排名文档。排名至少需要 两个 子检索器。

RRF 检索器是一个可选对象,定义为搜索请求的 retriever 参数 的一部分。RRF 检索器对象包含以下参数

retrievers

(必需,检索器对象数组)

子检索器列表,用于指定哪些返回的顶部文档集将应用 RRF 公式。每个子检索器作为 RRF 公式的一部分,权重相等。需要两个或多个子检索器。

rank_constant

(可选,整数)

此值确定每个查询中单个结果集中的文档对最终排名结果集的影响程度。较高的值表示排名较低的文档具有更大的影响力。此值必须大于或等于 1。默认为 60

rank_window_size

(可选,整数)

此值确定每个查询中单个结果集的大小。较高的值将提高结果相关性,但会牺牲性能。最终排名结果集被裁剪为搜索请求的 sizerank_window_size 必须大于或等于 size 且大于或等于 1。默认为 size 参数。

使用 RRF 的示例请求

resp = client.search(
    index="example-index",
    retriever={
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "text": "shoes"
                            }
                        }
                    }
                },
                {
                    "knn": {
                        "field": "vector",
                        "query_vector": [
                            1.25,
                            2,
                            3.5
                        ],
                        "k": 50,
                        "num_candidates": 100
                    }
                }
            ],
            "rank_window_size": 50,
            "rank_constant": 20
        }
    },
)
print(resp)
const response = await client.search({
  index: "example-index",
  retriever: {
    rrf: {
      retrievers: [
        {
          standard: {
            query: {
              term: {
                text: "shoes",
              },
            },
          },
        },
        {
          knn: {
            field: "vector",
            query_vector: [1.25, 2, 3.5],
            k: 50,
            num_candidates: 100,
          },
        },
      ],
      rank_window_size: 50,
      rank_constant: 20,
    },
  },
});
console.log(response);
GET example-index/_search
{
    "retriever": {
        "rrf": { 
            "retrievers": [
                {
                    "standard": { 
                        "query": {
                            "term": {
                                "text": "shoes"
                            }
                        }
                    }
                },
                {
                    "knn": { 
                        "field": "vector",
                        "query_vector": [1.25, 2, 3.5],
                        "k": 50,
                        "num_candidates": 100
                    }
                }
            ],
            "rank_window_size": 50,
            "rank_constant": 20
        }
    }
}

在上面的示例中,我们独立执行 knnstandard 检索器。然后我们使用 rrf 检索器来合并结果。

首先,我们执行由 knn 检索器指定的 kNN 搜索,以获得其全局前 50 个结果。

其次,我们执行由 standard 检索器指定的查询,以获得其全局前 50 个结果。

然后,在协调节点上,我们将 kNN 搜索的顶部文档与查询的顶部文档合并,并根据 RRF 公式使用 rrf 检索器中的参数对其进行排名,以使用默认 size10 获取合并的顶部文档。

请注意,如果 knn 搜索中的 k 大于 rank_window_size,则结果将截断为 rank_window_size。如果 k 小于 rank_window_size,则结果的大小为 k

倒数排名融合支持的功能

编辑

rrf 检索器支持

rrf 检索器当前不支持

在搜索中使用不支持的功能作为 rrf 检索器的一部分会导致异常。

最好避免提供 时间点 作为请求的一部分,因为 RRF 会在内部创建一个时间点,由所有子检索器共享,以确保结果一致。

使用多个标准检索器的倒数排名融合

编辑

rrf 检索器提供了一种合并和排名多个 standard 检索器的方法。主要用例是合并来自传统 BM25 查询和 ELSER 查询的顶部文档,以提高相关性。

使用多个标准检索器的 RRF 的示例请求

resp = client.search(
    index="example-index",
    retriever={
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "text": "blue shoes sale"
                            }
                        }
                    }
                },
                {
                    "standard": {
                        "query": {
                            "sparse_vector": {
                                "field": "ml.tokens",
                                "inference_id": "my_elser_model",
                                "query": "What blue shoes are on sale?"
                            }
                        }
                    }
                }
            ],
            "rank_window_size": 50,
            "rank_constant": 20
        }
    },
)
print(resp)
const response = await client.search({
  index: "example-index",
  retriever: {
    rrf: {
      retrievers: [
        {
          standard: {
            query: {
              term: {
                text: "blue shoes sale",
              },
            },
          },
        },
        {
          standard: {
            query: {
              sparse_vector: {
                field: "ml.tokens",
                inference_id: "my_elser_model",
                query: "What blue shoes are on sale?",
              },
            },
          },
        },
      ],
      rank_window_size: 50,
      rank_constant: 20,
    },
  },
});
console.log(response);
GET example-index/_search
{
    "retriever": {
        "rrf": { 
            "retrievers": [
                {
                    "standard": { 
                        "query": {
                            "term": {
                                "text": "blue shoes sale"
                            }
                        }
                    }
                },
                {
                    "standard": { 
                        "query": {
                            "sparse_vector":{
                                "field": "ml.tokens",
                                "inference_id": "my_elser_model",
                                "query": "What blue shoes are on sale?"
                            }
                        }
                    }
                }
            ],
            "rank_window_size": 50,
            "rank_constant": 20
        }
    }
}

在上面的示例中,我们独立执行两个 standard 检索器中的每一个。然后我们使用 rrf 检索器来合并结果。

首先,我们运行 standard 检索器,使用标准 BM25 评分算法为 blue shoes sales 指定词项查询。

接下来,我们运行 standard 检索器,使用我们的 ELSER 评分算法为 What blue shoes are on sale? 指定 sparse_vector 查询。

rrf 检索器允许我们以相等的权重合并由完全独立的评分算法生成的两个顶部文档集。

这不仅消除了使用线性组合来计算适当权重的需要,而且 RRF 也被证明可以提供比单独使用任何一个查询更好的相关性。

使用子搜索的倒数排名融合

编辑

不再支持使用子搜索的 RRF。请改用 检索器 API。有关示例,请参阅使用多个标准检索器

倒数排名融合完整示例

编辑

我们首先为索引创建一个映射,该索引具有文本字段、向量字段和整数字段,并为几个文档建立索引。在此示例中,我们将使用只有单个维度的向量,以使排名更容易解释。

resp = client.indices.create(
    index="example-index",
    mappings={
        "properties": {
            "text": {
                "type": "text"
            },
            "vector": {
                "type": "dense_vector",
                "dims": 1,
                "index": True,
                "similarity": "l2_norm",
                "index_options": {
                    "type": "hnsw"
                }
            },
            "integer": {
                "type": "integer"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="example-index",
    id="1",
    document={
        "text": "rrf",
        "vector": [
            5
        ],
        "integer": 1
    },
)
print(resp1)

resp2 = client.index(
    index="example-index",
    id="2",
    document={
        "text": "rrf rrf",
        "vector": [
            4
        ],
        "integer": 2
    },
)
print(resp2)

resp3 = client.index(
    index="example-index",
    id="3",
    document={
        "text": "rrf rrf rrf",
        "vector": [
            3
        ],
        "integer": 1
    },
)
print(resp3)

resp4 = client.index(
    index="example-index",
    id="4",
    document={
        "text": "rrf rrf rrf rrf",
        "integer": 2
    },
)
print(resp4)

resp5 = client.index(
    index="example-index",
    id="5",
    document={
        "vector": [
            0
        ],
        "integer": 1
    },
)
print(resp5)

resp6 = client.indices.refresh(
    index="example-index",
)
print(resp6)
const response = await client.indices.create({
  index: "example-index",
  mappings: {
    properties: {
      text: {
        type: "text",
      },
      vector: {
        type: "dense_vector",
        dims: 1,
        index: true,
        similarity: "l2_norm",
        index_options: {
          type: "hnsw",
        },
      },
      integer: {
        type: "integer",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "example-index",
  id: 1,
  document: {
    text: "rrf",
    vector: [5],
    integer: 1,
  },
});
console.log(response1);

const response2 = await client.index({
  index: "example-index",
  id: 2,
  document: {
    text: "rrf rrf",
    vector: [4],
    integer: 2,
  },
});
console.log(response2);

const response3 = await client.index({
  index: "example-index",
  id: 3,
  document: {
    text: "rrf rrf rrf",
    vector: [3],
    integer: 1,
  },
});
console.log(response3);

const response4 = await client.index({
  index: "example-index",
  id: 4,
  document: {
    text: "rrf rrf rrf rrf",
    integer: 2,
  },
});
console.log(response4);

const response5 = await client.index({
  index: "example-index",
  id: 5,
  document: {
    vector: [0],
    integer: 1,
  },
});
console.log(response5);

const response6 = await client.indices.refresh({
  index: "example-index",
});
console.log(response6);
PUT example-index
{
    "mappings": {
        "properties": {
            "text" : {
                "type" : "text"
            },
            "vector": {
                "type": "dense_vector",
                "dims": 1,
                "index": true,
                "similarity": "l2_norm",
                 "index_options": {
                     "type": "hnsw"
                 }
            },
            "integer" : {
                "type" : "integer"
            }
        }
    }
}

PUT example-index/_doc/1
{
    "text" : "rrf",
    "vector" : [5],
    "integer": 1
}

PUT example-index/_doc/2
{
    "text" : "rrf rrf",
    "vector" : [4],
    "integer": 2
}

PUT example-index/_doc/3
{
    "text" : "rrf rrf rrf",
    "vector" : [3],
    "integer": 1
}

PUT example-index/_doc/4
{
    "text" : "rrf rrf rrf rrf",
    "integer": 2
}

PUT example-index/_doc/5
{
    "vector" : [0],
    "integer": 1
}

POST example-index/_refresh

我们现在使用带有 standard 检索器的 rrf 检索器执行搜索,指定 BM25 查询,一个指定 kNN 搜索的 knn 检索器,以及一个词项聚合。

resp = client.search(
    index="example-index",
    retriever={
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "text": "rrf"
                            }
                        }
                    }
                },
                {
                    "knn": {
                        "field": "vector",
                        "query_vector": [
                            3
                        ],
                        "k": 5,
                        "num_candidates": 5
                    }
                }
            ],
            "rank_window_size": 5,
            "rank_constant": 1
        }
    },
    size=3,
    aggs={
        "int_count": {
            "terms": {
                "field": "integer"
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "example-index",
  retriever: {
    rrf: {
      retrievers: [
        {
          standard: {
            query: {
              term: {
                text: "rrf",
              },
            },
          },
        },
        {
          knn: {
            field: "vector",
            query_vector: [3],
            k: 5,
            num_candidates: 5,
          },
        },
      ],
      rank_window_size: 5,
      rank_constant: 1,
    },
  },
  size: 3,
  aggs: {
    int_count: {
      terms: {
        field: "integer",
      },
    },
  },
});
console.log(response);
GET example-index/_search
{
    "retriever": {
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "text": "rrf"
                            }
                        }
                    }
                },
                {
                    "knn": {
                        "field": "vector",
                        "query_vector": [3],
                        "k": 5,
                        "num_candidates": 5
                    }
                }
            ],
            "rank_window_size": 5,
            "rank_constant": 1
        }
    },
    "size": 3,
    "aggs": {
        "int_count": {
            "terms": {
                "field": "integer"
            }
        }
    }
}

我们收到带有排名 hits 和词项聚合结果的响应。我们同时具有排名器的 score_rank 选项,以显示我们排名靠前的文档。

{
    "took": ...,
    "timed_out" : false,
    "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
    },
    "hits" : {
        "total" : {
            "value" : 5,
            "relation" : "eq"
        },
        "max_score" : ...,
        "hits" : [
            {
                "_index" : "example-index",
                "_id" : "3",
                "_score" : 0.8333334,
                "_source" : {
                    "integer" : 1,
                    "vector" : [
                        3
                    ],
                    "text" : "rrf rrf rrf"
                }
            },
            {
                "_index" : "example-index",
                "_id" : "2",
                "_score" : 0.5833334,
                "_source" : {
                    "integer" : 2,
                    "vector" : [
                        4
                    ],
                    "text" : "rrf rrf"
                }
            },
            {
                "_index" : "example-index",
                "_id" : "4",
                "_score" : 0.5,
                "_source" : {
                    "integer" : 2,
                    "text" : "rrf rrf rrf rrf"
                }
            }
        ]
    },
    "aggregations" : {
        "int_count" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
                {
                    "key" : 1,
                    "doc_count" : 3
                },
                {
                    "key" : 2,
                    "doc_count" : 2
                }
            ]
        }
    }
}

让我们分解一下这些命中是如何排名的。我们首先分别运行指定查询的 standard 检索器和指定 kNN 搜索的 knn 检索器,以收集它们的各个命中。

首先,我们查看 standard 检索器中查询的命中。

"hits" : [
    {
        "_index" : "example-index",
        "_id" : "4",
        "_score" : 0.16152832,              
        "_source" : {
            "integer" : 2,
            "text" : "rrf rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "3",                        
        "_score" : 0.15876243,
        "_source" : {
            "integer" : 1,
            "vector" : [3],
            "text" : "rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "2",                        
        "_score" : 0.15350538,
        "_source" : {
            "integer" : 2,
            "vector" : [4],
            "text" : "rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "1",                        
        "_score" : 0.13963442,
        "_source" : {
            "integer" : 1,
            "vector" : [5],
            "text" : "rrf"
        }
    }
]

排名 1,_id 4

排名 2,_id 3

排名 3,_id 2

排名 4,_id 1

请注意,我们的第一个命中没有 vector 字段的值。现在,我们查看来自 knn 检索器的 kNN 搜索结果。

"hits" : [
    {
        "_index" : "example-index",
        "_id" : "3",                   
        "_score" : 1.0,
        "_source" : {
            "integer" : 1,
            "vector" : [3],
            "text" : "rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "2",                   
        "_score" : 0.5,
        "_source" : {
            "integer" : 2,
            "vector" : [4],
            "text" : "rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "1",                   
        "_score" : 0.2,
        "_source" : {
            "integer" : 1,
            "vector" : [5],
            "text" : "rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "5",                   
        "_score" : 0.1,
        "_source" : {
            "integer" : 1,
            "vector" : [0]
        }
    }
]

排名 1,_id 3

排名 2,_id 2

排名 3,_id 1

排名 4,_id 5

现在,我们可以获取两个单独排名的结果集,并使用 rrf 检索器中的参数将 RRF 公式应用于它们,以获得最终排名。

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

我们根据 RRF 公式对文档进行排名,其中 rank_window_size5,截断 RRF 结果集中的底部 2 个文档,size3。最终结果为 _id: 3,为 _rank: 1_id: 2,为 _rank: 2,以及 _id: 4,为 _rank: 3。此排名与预期的原始 RRF 搜索的结果集匹配。

RRF 中的 Explain

编辑

除了单独的查询评分详细信息之外,我们还可以使用 explain=true 参数来获取有关每个文档的 RRF 分数如何计算的信息。使用上面的示例,并通过将 explain=true 添加到搜索请求中,我们现在将获得如下所示的响应

{
    "hits":
    [
        {
            "_index": "example-index",
            "_id": "3",
            "_score": 0.8333334,
            "_explanation":
            {
                "value": 0.8333334,                                                                                                                                               
                "description": "rrf score: [0.8333334] computed for initial ranks [2, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",            
                "details":                                                                                                                                                        
                [
                    {
                        "value": 2,                                                                                                                                               
                        "description": "rrf score: [0.33333334], for rank [2] in query at index [0] computed as [1 / (2 + 1]), for matching query with score: ",
                        "details":                                                                                                                                                
                        [
                            {
                                "value": 0.15876243,
                                "description": "weight(text:rrf in 0) [PerFieldSimilarity], result of:",
                                "details":
                                [
                                    ...
                                ]
                            }
                        ]
                    },
                    {
                        "value": 1,                                                                                                                                              
                        "description": "rrf score: [0.5], for rank [1] in query at index [1] computed as [1 / (1 + 1]), for matching query with score: ",
                        "details":
                        [
                            {
                                "value": 1,
                                "description": "within top k documents",
                                "details":
                                []
                            }
                        ]
                    }
                ]
            }
        }
        ...
    ]
}

文档 _id=3 的最终 RRF 分数

有关此分数如何根据此文档在每个单独查询中的排名计算的描述

有关每个查询如何计算 RRF 分数的详细信息

这里的 value 指定此文档在特定查询中的 rank

底层查询的标准 explain 输出,描述了匹配的词项和权重

这里的 value 指定此文档在第二个 (knn) 查询中的 rank

除了以上内容,RRF 中的 explain 还支持使用 _name 参数的命名查询。使用命名查询可以更容易和更直观地理解 RRF 分数的计算,尤其是在处理多个查询时。因此,我们现在将拥有

GET example-index/_search
{
    "retriever": {
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "text": "rrf"
                            }
                        }
                    }
                },
                {
                    "knn": {
                        "field": "vector",
                        "query_vector": [3],
                        "k": 5,
                        "num_candidates": 5,
                        "_name": "my_knn_query"                           
                    }
                }
            ],
            "rank_window_size": 5,
            "rank_constant": 1
        }
    },
    "size": 3,
    "aggs": {
        "int_count": {
            "terms": {
                "field": "integer"
            }
        }
    }
}

这里我们为 knn 检索器指定一个 _name

响应现在将在解释中包括命名查询

{
    "hits":
    [
        {
            "_index": "example-index",
            "_id": "3",
            "_score": 0.8333334,
            "_explanation":
            {
                "value": 0.8333334,
                "description": "rrf score: [0.8333334] computed for initial ranks [2, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",
                "details":
                [
                    {
                        "value": 2,
                        "description": "rrf score: [0.33333334], for rank [2] in query at index [0] computed as [1 / (2 + 1]), for matching query with score: ",
                        "details":
                        [
                            ...
                        ]
                    },
                    {
                        "value": 1,
                        "description": "rrf score: [0.5], for rank [1] in query [my_knn_query] computed as [1 / (1 + 1]), for matching query with score: ",                      
                        "details":
                        [
                           ...
                        ]
                    }
                ]
            }
        }
        ...
    ]
}

现在我们有了对命名查询 my_knn_query 的引用,而不是匿名的 at index n

RRF 中的分页

编辑

使用 rrf 时,可以使用 from 参数在结果中进行分页。由于最终排名仅取决于原始查询排名,为了确保分页时的一致性,我们必须确保在 from 更改时,我们已经看到的内容的顺序保持不变。为此,我们使用固定的 rank_window_size 作为可以进行分页的整个可用结果集。这基本上意味着,如果

  • from + sizerank_window_size:我们可以从最终的 rrf 排名结果集中获得 results[from: from+size] 文档
  • from + size > rank_window_size:我们将获得 0 个结果,因为请求将超出可用的 rank_window_size 大小的结果集。

这里需要注意的重要一点是,由于 rank_window_size 是我们从单个查询组件中获得的所有结果,因此,如果 rank_window_size 保持不变,则分页会保证一致性,即不会在多个页面中跳过或重复文档。如果 rank_window_size 发生变化,即使对于相同的排名,结果的顺序也可能会发生变化。

为了说明上述所有内容,让我们考虑以下简化示例,其中我们有两个查询,queryAqueryB 及其排名文档

     |  queryA   |  queryB    |
_id: |  1        |  5         |
_id: |  2        |  4         |
_id: |  3        |  3         |
_id: |  4        |  1         |
_id: |           |  2         |

对于 rank_window_size=5,我们将看到来自 queryAqueryB 的所有文档。假设 rank_constant=1rrf 分数将是

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

因此,最终排名结果集将是 [1, 4, 2, 3, 5],我们将在其上进行分页,因为 rank_window_size == len(results)。在这种情况下,我们将拥有

  • from=0, size=2 将返回文档 [1, 4],排名为 [1, 2]
  • from=2, size=2 将返回文档 [2, 3],其排名为 [3, 4]
  • from=4, size=2 将返回文档 [5],其排名为 [5]
  • from=6, size=2 将返回一个空的结果集,因为没有更多的结果可以迭代。

现在,如果我们有一个 rank_window_size=2,那么对于查询 queryAqueryB,我们分别只能看到 [1, 2][5, 4] 的文档。经过计算,我们会发现结果现在会略有不同,因为我们不知道任何查询中位置 [3: end] 的文档。

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

最终的排名结果集将是 [1, 5, 2, 4],并且我们能够在前 rank_window_size 个结果上进行分页,即 [1, 5]。因此,对于上面相同的参数,我们现在会有

  • from=0, size=2 将返回 [1, 5],其排名为 [1, 2]
  • from=2, size=2 将返回一个空的结果集,因为它将超出可用的 rank_window_size 结果范围。

RRF 中的聚合

编辑

rrf 检索器支持来自所有指定的子检索器的聚合。关于聚合的重要说明:

  • 它们作用于来自所有子检索器的完整结果集
  • 它们不受 rank_window_size 参数的限制
  • 它们处理所有匹配文档的并集

例如,考虑以下文档集

{
    "_id": 1, "termA": "foo",
    "_id": 2, "termA": "foo", "termB": "bar",
    "_id": 3, "termA": "aardvark", "termB": "bar",
    "_id": 4, "termA": "foo", "termB": "bar"
}

使用 rrf 检索器对 termA 字段执行词条聚合

{
    "retriever": {
        "rrf": {
            "retrievers": [
                {
                    "standard": {
                        "query": {
                            "term": {
                                "termB": "bar"
                            }
                        }
                    }
                },
                {
                    "standard": {
                        "query": {
                            "match_all": { }
                        }
                    }
                }
            ],
            "rank_window_size": 1
        }
    },
    "size": 1,
    "aggs": {
        "termA_agg": {
            "terms": {
                "field": "termA"
            }
        }
    }
}

聚合结果将包括所有匹配的文档,而无论 rank_window_size 如何。

{
    "foo": 3,
    "aardvark": 1
}

RRF 中的高亮显示

编辑

使用 rrf 检索器,您可以添加 高亮片段,以在搜索结果中显示相关的文本片段。高亮显示的片段是基于子检索器上定义的匹配文本查询计算的。

不支持使用 knn 检索器或 knn 查询在高亮显示向量字段。

检索器示例页面中,还可以找到一个更具体的 RRF 高亮显示示例。

RRF 中的内部命中

编辑

rrf 检索器支持 内部命中 功能,允许您检索与主要搜索结果相关的嵌套或父/子文档。可以将内部命中指定为任何嵌套子检索器的一部分,并且会传播到顶级父检索器。请注意,内部命中计算仅在 rrf 检索器对顶部匹配文档的评估结束时执行,而不是作为嵌套子检索器查询执行的一部分。

在多个子检索器中定义多个 inner_hits 部分时

  • 每个 inner_hits 部分都必须具有唯一的名称
  • 名称在搜索请求的所有子检索器中都必须是唯一的