跳到主要内容

使用 StructArray 进行 Hybrid Search

公测版

本页介绍如何在一个 Hybrid Search 请求中将 StructArray 向量搜索与其他向量搜索结合使用。StructArray Hybrid Search 可以生成 Entity-level 结果,也可以生成 Element-level 结果,具体取决于组合的 AnnSearchRequest 对象。

本页使用 创建 StructArray Field 中的 tech_articles Collection。该 Collection 包含一个名为 title_vector 的顶层 Vector Field,以及一个名为 chunks 的 StructArray Field。chunks[emb_list_vector] 子字段用于 EmbeddingList Search,chunks[emb] 用于 Element-level Search。

Hybrid Search 如何应用于 StructArray

AnnSearchRequest 组合最终候选范围结果行为element_scope
Collection-level Vector Field + StructArray EmbeddingList 子字段Entity 级最终候选以主键为 key。不要使用。
Collection-level Vector Field + StructArray Element-level 子字段Entity 级Element-level 命中会在 Hybrid 重新排序前折叠为 Entity-level 候选。可在 StructArray Element-level AnnSearchRequest 上设置折叠配置。
同一 StructArray Field 下的多个 Element-level 子字段Element 级最终候选以主键加 Struct 元素 offset 为 key。不要使用。
不同 StructArray Field 下的 Element-level 子字段Entity 级Element offset 不共享身份,因此每个 StructArray Element-level AnnSearchRequest 都会在重新排序前折叠。可在每个 StructArray Element-level AnnSearchRequest 上设置折叠配置。
🚧Warning

element_scope 只用于为非同一 Struct 的 Element-level Hybrid Search 中的 StructArray Element-level AnnSearchRequest 配置折叠。不要将它用于 EmbeddingList 请求、Collection-level Vector 请求或同一 StructArray 下的 Element-level Hybrid Search。

开始前

运行 Hybrid Search 前,请先准备好 Collection、数据和 Index。

要求说明
StructArray FieldCollection 包含 chunks 等 StructArray Field。
Vector 子字段为 EmbeddingList Search 和 Element-level Search 使用独立的 Vector 子字段。
Indexchunks[emb_list_vector] 使用 MAX_SIM* metric。chunks[emb] 使用 COSINEIPL2 等常规 Vector metric。
Reranker选择 RRFRanker 或应用支持的其他 Hybrid reranker。

Index 设置请参见为 StructArray Field 创建 Index

使用 EmbeddingList 请求执行 Hybrid Search

在 Hybrid Search 中,StructArray Vector 子字段上的 EmbeddingList Search 是 Entity-level 的。它的行为类似 Entity-level Vector Search 请求,不会返回某个匹配 Struct 元素的 offset。

python
from pymilvus import AnnSearchRequest, MilvusClient, RRFRanker
from pymilvus.client.embedding_list import EmbeddingList

client = MilvusClient(
uri="YOUR_CLUSTER_ENDPOINT",
token="YOUR_CLUSTER_TOKEN",
)

query_vector = [0.19, 0.24, 0.30, 0.37]

query_list = EmbeddingList()
query_list.add([0.12, 0.21, 0.32, 0.44])
query_list.add([0.18, 0.23, 0.29, 0.36])

title_req = AnnSearchRequest(
data=[query_vector],
anns_field="title_vector",
param={
"metric_type": "COSINE",
"params": {},
},
limit=10,
)

chunk_list_req = AnnSearchRequest(
data=[query_list],
anns_field="chunks[emb_list_vector]",
param={
"metric_type": "MAX_SIM_COSINE",
"params": {},
},
limit=10,
)

results = client.hybrid_search(
collection_name="tech_articles",
reqs=[title_req, chunk_list_req],
ranker=RRFRanker(),
limit=5,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
],
)

在此示例中,两个 AnnSearchRequest 对象都会生成 Entity-level 候选。最终结果以父 Entity 主键为 key。不要为 EmbeddingList 请求添加 element_scope

当所有 AnnSearchRequest 对象都指向同一 StructArray Field 下的 Element-level Vector 子字段时,Hybrid Search 可以在重新排序过程中保留 Element-level 候选。这是唯一一种最终结果仍保持 Element-level 的 StructArray Hybrid 模式。

以下示例假设 chunks StructArray Field 有两个 Element-level Vector 子字段:chunks[emb]chunks[code_emb],且二者都使用常规 Vector metric。

python
index_chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
param={
"metric_type": "COSINE",
"params": {},
},
limit=10,
expr='element_filter(chunks, $[section] == "index")',
)

code_chunk_req = AnnSearchRequest(
data=[code_query_vector],
anns_field="chunks[code_emb]",
param={
"metric_type": "COSINE",
"params": {},
},
limit=10,
expr='element_filter(chunks, $[has_code] == true)',
)

results = client.hybrid_search(
collection_name="tech_articles",
reqs=[index_chunk_req, code_chunk_req],
ranker=RRFRanker(),
limit=5,
output_fields=[
"doc_id",
"title",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)

for hits in results:
for hit in hits:
print(
"doc_id:", hit["id"],
"distance:", hit["distance"],
"offset:", hit.get("offset"),
"entity:", hit["entity"],
)

两个 AnnSearchRequest 对象都搜索 chunks 下的 Vector 子字段。同一个从 0 开始的 offset 指向同一个 Struct 元素,因此 Hybrid reranker 可以直接对元素候选进行排序。该模式不会执行 Entity-level 折叠,因此不要设置 element_scope

如果 Hybrid Search 将 StructArray Element-level AnnSearchRequest 与 Collection-level Vector 请求、EmbeddingList 请求,或另一个 StructArray Field 下的 Element-level 请求混合使用,最终候选范围就是 Entity 级。在这种情况下,每个 StructArray Element-level AnnSearchRequest 都会在 Hybrid 重新排序前折叠为 Entity-level 候选。

当需要控制同一 Entity 中多个匹配元素如何折叠时,请在 StructArray Element-level AnnSearchRequestparams 中使用 element_scope

python
title_req = AnnSearchRequest(
data=[query_vector],
anns_field="title_vector",
param={
"metric_type": "COSINE",
"params": {},
},
limit=10,
)

chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
param={
"metric_type": "COSINE",
"params": {
"element_scope": {
"collapse": {
"strategy": "topk_sum",
"topk": 3,
},
},
},
},
limit=30,
expr='element_filter(chunks, $[quality_score] > 0.8)',
)

results = client.hybrid_search(
collection_name="tech_articles",
reqs=[title_req, chunk_req],
ranker=RRFRanker(),
limit=5,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)

在此示例中,title_req 是 Entity-level,因此最终 Hybrid 结果也是 Entity-level。chunk_req 请求会先从 chunks[emb] 返回元素命中,然后对同一 Entity 中返回的元素按最佳三个元素分数求和进行折叠。如果需要 Entity-level 折叠但省略了 element_scope,默认折叠策略为 max

选择折叠策略

策略行为topkMetric 要求
max保留该 Entity 的最佳返回元素分数。不允许。任意受支持的常规 Vector metric。
sum对该 Entity 的所有返回元素分数求和。不允许。仅适用于正相关 metric,例如 IPCOSINE
avg对该 Entity 的所有返回元素分数取平均值。不允许。任意受支持的常规 Vector metric。
topk_sum对该 Entity 的最佳 K 个返回元素分数求和。必填,且必须为正数。仅适用于正相关 metric,例如 IPCOSINE
topk_avg对该 Entity 的最佳 K 个返回元素分数取平均值。必填,且必须为正数。任意受支持的常规 Vector metric。

折叠只使用该 StructArray Element-level AnnSearchRequest 返回的元素命中。它不会在 ANN Search 后扫描 Entity 中的所有 Struct 元素。请将请求的 limit 设置得足够大,以便提供你希望参与折叠的元素。

添加过滤器、Range Search 和 Grouping

当标量条件应作用于参与向量搜索的同一个 Struct 元素时,可以把 element_filter 附加到 StructArray Element-level AnnSearchRequest 上。也可以在 hybrid_search() 上使用顶层 filter 处理父 Entity 条件。

StructArray Element-level Vector Field 支持在 Hybrid Search 中使用 Range Search。请将 radius 以及可选的 range_filter 添加到 Element-level AnnSearchRequest 中。EmbeddingList-level StructArray 请求不支持 Range Search。

Element-level Hybrid Grouping 仅当所有 AnnSearchRequest 对象都指向同一 StructArray Field 下的 Element-level Vector Field 时支持,且 group_by_field 必须是主键。当请求混用了 Collection-level Vector Field、不同 StructArray Field 或 EmbeddingList-level 请求时,不支持 Hybrid Grouping。不要将 Range Search 与 Grouping 组合使用。

理解 Hybrid 结果

最终候选范围结果 keyOffset 行为发生场景
Entity 级主键。最终结果中没有元素 offset。Hybrid 请求包含 Collection-level Vector Field、EmbeddingList 请求,或不同 StructArray Field 下的 Element-level 请求。
Element 级主键加父 StructArray Field 加元素 offset。当 API 或 SDK 暴露时,可返回被选中元素的 offset。所有 AnnSearchRequest 对象都是 Element-level,且位于同一 StructArray Field 下。

限制

  • element_scope 只用于在 Hybrid Search 中必须折叠为 Entity-level 候选的 StructArray Element-level AnnSearchRequest

  • 不要将 element_scope 用于 EmbeddingList 请求、Collection-level Vector 请求或同一 StructArray 下的 Element-level Hybrid Search。

  • sumtopk_sum 折叠策略要求使用正相关 metric,例如 IPCOSINE。不要将它们与 L2 一起使用。

  • topk_sumtopk_avg 要求 topk 为正数。其他折叠策略不得包含 topk

  • EmbeddingList-level StructArray 请求不支持 Range Search 或 group-by。

  • Hybrid group-by 仅支持同一 StructArray 下的 Element-level Hybrid Search,且只能按主键分组。

  • 不要将 Range Search 与 group-by 组合使用。

常见错误

  • 为同一 StructArray 下的 Element-level Hybrid 请求添加 element_scope。该请求保持 Element-level,不会执行 Entity-level 折叠。

  • chunks[emb_list_vector] 添加 element_scope。EmbeddingList Search 已经是 Entity-level。

  • 误以为两个 StructArray Field 共享元素 offset。chunks 中的 offset 3 和另一个 StructArray Field 中的 offset 3 是不同元素,因此 Hybrid 请求会变成 Entity-level。

  • topk_sumL2 一起使用。对于负距离 metric,请使用 maxavgtopk_avg

  • 期待 Entity-level Hybrid 结果在折叠后仍包含被选中 Struct 元素的 offset。

下一步

  1. 如需了解两种基础 StructArray 向量搜索模式,请阅读使用 StructArray 进行基础向量搜索

  2. 如需为 Hybrid Search 添加标量过滤,请阅读使用 StructArray 进行过滤搜索

  3. 如需在 Hybrid Search 中使用评分或距离边界,请阅读使用 StructArray 进行范围搜索

  4. 如需按父 Entity 对 Element-level Hybrid 结果进行分组,请阅读使用 StructArray 进行分组搜索

  5. 如需查看 StructArray 搜索限制,请阅读 StructArray 限制

Ctrl I