Hybrid Search with StructArray
本页介绍如何在一次 Hybrid Search 请求中,将 StructArray vector search 与其他 vector search 组合使用。StructArray hybrid search 可以生成 entity-level 结果或 element-level 结果,具体取决于你组合的 AnnSearchRequest 对象。
本页使用 Create a StructArray Field 中的 tech_articles Collection。该 Collection 包含一个名为 title_vector 的顶层 vector field,以及一个名为 chunks 的 StructArray field。chunks[emb_list_vector] 子字段已为 EmbeddingList search 创建索引,chunks[emb] 已为 element-level search 创建索引。
How hybrid search applies to StructArray
AnnSearchRequest 组合 | 最终候选范围 | 结果行为 | element_scope |
|---|---|---|---|
| Collection 级别的向量字段 + StructArray EmbeddingList 子字段 | Entity 级别 | 最终候选结果以主键为键。 | 不要使用。 |
| Collection 级别的向量字段 + StructArray element-level 子字段 | Entity 级别 | element-level 命中会先折叠为 Entity 级别的候选结果,然后再进行 hybrid reranking。 | 可选:在 StructArray element-level AnnSearchRequest 上配置 collapse。 |
| 同一 StructArray 字段下的多个 element-level 子字段 | Element 级别 | 最终候选结果以主键加 Struct 元素偏移量为键。 | 不要使用。 |
| 不同 StructArray 字段下的 element-level 子字段 | Entity 级别 | 元素偏移量不共享身份,因此每个 StructArray element-level AnnSearchRequest 都会在 reranking 前先折叠。 | 可选:在每个 StructArray element-level AnnSearchRequest 上配置 collapse。 |
警告
仅在非同一 Struct 的 element-level hybrid search 中,使用 element_scope 为 StructArray element-level AnnSearchRequest 对象配置 collapse。不要将其用于 EmbeddingList 请求、Collection 级别向量请求,或同一 StructArray 的 element-level hybrid search。
准备工作
在运行 Hybrid Search 之前,请先准备好 Collection、数据和 Index。
| 要求 | 详情 |
|---|---|
| StructArray 字段 | Collection 包含一个 StructArray 字段,例如 chunks。 |
| 向量子字段 | 为 EmbeddingList search 和 element-level search 使用不同的向量子字段。 |
| Index | chunks[emb_list_vector] 使用 MAX_SIM* metric。chunks[emb] 使用常规向量 metric,例如 COSINE、IP 或 L2。 |
| Reranker | 选择 Hybrid Search Reranker,例如 RRFRanker,或你的应用支持的其他 Reranker。 |
有关 Index 设置,请参阅 Index StructArray Fields。
Run hybrid search with an EmbeddingList request
在 hybrid search 中,针对 StructArray vector 子字段的 EmbeddingList search 是 entity 级别的。它的行为类似 entity 级别的 vector search request,不会返回匹配到的 Struct element offset。
from pymilvus import AnnSearchRequest, MilvusClient, RRFRanker
from pymilvus.client.embedding_list import EmbeddingList
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus",
)
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",
limit=10,
)
chunk_list_req = AnnSearchRequest(
data=[query_list],
anns_field="chunks[emb_list_vector]",
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 级别的候选结果。最终结果以父 entity 的主键为键。不要向 EmbeddingList request 添加 element_scope。
运行 same-StructArray element-level Hybrid Search
当所有 AnnSearchRequest 对象都以同一个 StructArray 字段下的 element-level vector subfield 为目标时,Hybrid Search 可以在 reranking 过程中保留 element-level candidates。这是唯一一种最终结果仍保持 element-level 的 StructArray hybrid mode。
以下示例假设 chunks StructArray 字段包含两个 element-level vector subfields:chunks[emb] 和 chunks[code_emb],并且二者都使用常规向量 metrics。
index_chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
limit=10,
expr='element_filter(chunks, $[section] == "index")',
)
code_chunk_req = AnnSearchRequest(
data=[code_query_vector],
anns_field="chunks[code_emb]",
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 subfields。同一个从 0 开始的 offset 指向同一个 Struct element,因此 hybrid reranker 可以直接对 element candidates 进行排序。在此模式下不要设置 element_scope,因为不会执行 entity-level collapse。
Collapse element-level hits for entity-level hybrid search
如果 Hybrid Search 将 StructArray element-level AnnSearchRequest 与 collection-level vector request、EmbeddingList request,或另一个 StructArray 字段下的 element-level request 混合使用,最终候选范围为 entity-level。在这种情况下,每个 StructArray element-level AnnSearchRequest 都会先折叠为 entity-level candidates,然后再进行 hybrid reranking。
当你需要控制同一 Entity 中多个匹配元素如何折叠时,请在 StructArray element-level AnnSearchRequest 的 params 中使用 element_scope。
title_req = AnnSearchRequest(
data=[query_vector],
anns_field="title_vector",
limit=10,
)
chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
param={
"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 Search 结果也是 entity-level。chunk_req request 会先从 chunks[emb] 返回 element hits,然后将同一 Entity 中返回的元素按最佳三个 element scores 求和的方式折叠。如果在需要 entity-level collapse 时省略 element_scope,collapse strategy 默认使用 max。
Choose a collapse strategy
| 策略 | 行为 | topk | Metric 要求 |
|---|---|---|---|
max | 保留该 Entity 的最佳返回元素得分。 | 不允许。 | 任意受支持的常规向量 Metric。 |
sum | 对该 Entity 的所有返回元素得分求和。 | 不允许。 | 仅限正相关 Metric,例如 IP 或 COSINE。 |
avg | 对该 Entity 的所有返回元素得分求平均值。 | 不允许。 | 任意受支持的常规向量 Metric。 |
topk_sum | 对该 Entity 的最佳 K 个返回元素得分求和。 | 必填且必须为正数。 | 仅限正相关 Metric,例如 IP 或 COSINE。 |
topk_avg | 对该 Entity 的最佳 K 个返回元素得分求平均值。 | 必填且必须为正数。 | 任意受支持的常规向量 Metric。 |
Collapse 仅使用该 StructArray 元素级 AnnSearchRequest 返回的元素命中。它不会在 ANN Search 之后扫描 Entity 中的每个 Struct 元素。请将请求的 limit 设置得足够高,以提供你希望用于 Collapse 的元素。
添加 Filter、Range Search 和 Grouping
当标量条件需要应用于参与向量搜索的同一批 Struct 元素时,你可以在 StructArray 元素级 AnnSearchRequest 上附加 element_filter。你也可以在 hybrid_search() 上使用顶层 filter 来设置父 Entity 条件。
StructArray 元素级向量字段支持在 hybrid search 中使用 Range Search。向元素级 AnnSearchRequest 添加 radius,并可选添加 range_filter。EmbeddingList 级 StructArray 请求不支持 Range Search。
仅当所有 AnnSearchRequest 对象都指向同一个 StructArray 字段下的元素级向量字段时,才支持元素级 hybrid grouping,并且 group_by_field 必须是主键。如果请求混用了 Collection 级向量字段、不同的 StructArray 字段或 EmbeddingList 级请求,则不支持 hybrid grouping。不要将 Range Search 与 grouping 组合使用。
Interpret hybrid results
| 最终候选范围 | 结果键 | Offset 行为 | 适用场景 |
|---|---|---|---|
| Entity 级别 | Primary key。 | 最终结果中不包含 element offset。 | hybrid request 包含 collection-level vector field、EmbeddingList request,或不同 StructArray field 下的 element-level request。 |
| Element 级别 | Primary key 加上父 StructArray field 和 element offset。 | 当 API 或 SDK 暴露该信息时,可以返回所选 element offset。 | 所有 AnnSearchRequest 对象都是 element-level,并且位于同一个 StructArray field 下。 |
Limitations
-
仅对 hybrid search 中必须折叠为 entity-level candidates 的 StructArray element-level
AnnSearchRequest对象使用element_scope。 -
不要将
element_scope用于 EmbeddingList 请求、collection-level vector 请求,或 same-StructArray element-level hybrid search。 -
sum和topk_sum折叠策略要求使用正相关指标,例如IP或COSINE。不要将它们与L2搭配使用。 -
topk_sum和topk_avg要求topk为正值。其他折叠策略不得包含topk。 -
EmbeddingList-level StructArray 请求不支持 range search 或 group-by。
-
Hybrid group-by 仅支持 same-StructArray element-level hybrid search,并且只能按 primary key 分组。
-
不要将 range search 与 group-by 组合使用。
Common mistakes
-
在同一个 StructArray 的 element-level hybrid 请求中添加
element_scope。该请求仍然是 element-level,不会执行 entity-level collapse。 -
向
chunks[emb_list_vector]添加element_scope。EmbeddingList search 本身已经是 entity-level。 -
假设两个 StructArray 字段共享元素 offset。
chunks中的 offset3与另一个 StructArray 字段中的 offset3是不同元素,因此 hybrid 请求会变成 entity-level。 -
将
topk_sum与L2搭配使用。对于负向距离度量,请使用max、avg或topk_avg。 -
期望 entity-level hybrid 结果在 collapse 后包含选中的 Struct 元素 offset。
后续步骤
-
如需了解 StructArray 的两种基本向量搜索模式,请阅读 Basic Vector Search with StructArray。
-
如需在 Hybrid Search 中添加标量过滤器,请阅读 Filtered Search with StructArray。
-
如需在 Hybrid Search 中使用 score 或 distance 边界,请阅读 Range Search with StructArray。
-
如需按父 Entity 对元素级 Hybrid Search 结果进行分组,请阅读 Grouping Search with StructArray。
-
如需查看 StructArray 搜索限制,请阅读 StructArray Limits。