跳到主要内容
版本:v3.0.x

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 使用不同的向量子字段。
Indexchunks[emb_list_vector] 使用 MAX_SIM* metric。chunks[emb] 使用常规向量 metric,例如 COSINEIPL2
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。

Text
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

当所有 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。

Text
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。

如果 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 AnnSearchRequestparams 中使用 element_scope

Text
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

策略行为topkMetric 要求
max保留该 Entity 的最佳返回元素得分。不允许。任意受支持的常规向量 Metric。
sum对该 Entity 的所有返回元素得分求和。不允许。仅限正相关 Metric,例如 IPCOSINE
avg对该 Entity 的所有返回元素得分求平均值。不允许。任意受支持的常规向量 Metric。
topk_sum对该 Entity 的最佳 K 个返回元素得分求和。必填且必须为正数。仅限正相关 Metric,例如 IPCOSINE
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。

  • sumtopk_sum 折叠策略要求使用正相关指标,例如 IPCOSINE。不要将它们与 L2 搭配使用。

  • topk_sumtopk_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 中的 offset 3 与另一个 StructArray 字段中的 offset 3 是不同元素,因此 hybrid 请求会变成 entity-level。

  • topk_sumL2 搭配使用。对于负向距离度量,请使用 maxavgtopk_avg

  • 期望 entity-level hybrid 结果在 collapse 后包含选中的 Struct 元素 offset。

后续步骤

  1. 如需了解 StructArray 的两种基本向量搜索模式,请阅读 Basic Vector Search with StructArray

  2. 如需在 Hybrid Search 中添加标量过滤器,请阅读 Filtered Search with StructArray

  3. 如需在 Hybrid Search 中使用 score 或 distance 边界,请阅读 Range Search with StructArray

  4. 如需按父 Entity 对元素级 Hybrid Search 结果进行分组,请阅读 Grouping Search with StructArray

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