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

Grouping Search with StructArray

使用本页说明按父 Entity 对 StructArray 元素级搜索结果进行分组。当多个 Struct 元素匹配查询时,元素级搜索可能会从同一个 Entity 返回多条命中结果。分组会将这些元素命中结果折叠起来,使每个父 Entity 最多只出现一次。

本页使用 Create a StructArray Field 中的 tech_articles Collection。该 Collection 包含一个名为 chunks 的 StructArray 字段。chunks[emb] 向量子字段已使用常规向量 metric 建立索引,用于元素级搜索。

How grouping applies to StructArray

搜索模式Grouping 行为结果行为
EmbeddingList search不支持。不适用。
Element-level search支持按 primary key 进行 grouping。每个父 Entity 最多返回一个结果。Element-level metadata 会被保留,因此当 API 或 SDK 暴露所选元素的 index 或 offset 时,可以返回该信息。
Hybrid search仅当所有子搜索都针对同一 StructArray 字段下的 element-level vector field 时才支持。Element-level 子搜索会先按 primary key 分组,然后再进行最终结果处理。

当未分组的 element-level search 返回过多重复的父 Entity 时,请使用 grouping。如果你希望将每个匹配的 Struct element 作为独立命中返回,请使用不带 group_by_fieldBasic Vector Search with StructArray

Before you begin

在运行 grouping search 前,准备好 Collection、数据和 Index。

要求详情
元素级 vector subfield使用 StructArray vector subfield,例如 chunks[emb],并使用常规 vector metric 建立 Index。
常规 vector query使用常规 query vector,而不是 EmbeddingList
Primary key grouping将 Collection primary key 用作 group_by_field,例如 doc_id
无 range 参数不要将 grouping search 与 range-search 参数(如 radiusrange_filter)结合使用。

有关 Index 设置,参见 Index StructArray Fields

以下示例会先搜索各个 chunk,然后按父 Entity 的主键对命中的元素进行分组。

Python
from pymilvus import MilvusClient

client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus",
)

query_vector = [0.19, 0.24, 0.30, 0.37]

results = client.search(
collection_name="tech_articles",
data=[query_vector],
anns_field="chunks[emb]",
limit=5,
group_by_field="doc_id",
output_fields=[
"doc_id",
"title",
"chunks[text]",
"chunks[section]",
"chunks[page]",
"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"],
)

如果不进行分组,当多个 chunk 匹配查询时,同一个 doc_id 可能会出现多次。使用 group_by_field="doc_id" 后,每个父 Entity 最多出现一次。分组会保留 element-level metadata,因此当 API 或 SDK 暴露相关信息时,分组结果仍可以包含所选 Struct 元素的索引或 offset。

Add scalar filters

你可以将 grouping search 与 StructArray scalar filtering 结合使用。当 scalar 条件需要约束哪些 Struct 元素参与 element-level vector search 时,请使用 element_filter

Python
filter_expr = (
'category == "search" && '
'element_filter(chunks, '
'$[section] == "index" && '
'$[quality_score] > 0.9)'
)

results = client.search(
collection_name="tech_articles",
data=[query_vector],
anns_field="chunks[emb]",
filter=filter_expr,
limit=5,
group_by_field="doc_id",
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)

顶层 predicate 会选择候选 Entity。element_filter predicate 会将 element-level vector search 限制在匹配的 Struct 元素上。然后,Grouping 会按 primary key 折叠匹配的元素命中结果。

使用 StructArray 进行 Hybrid Search 时,grouping 是 element-level 特性。仅当所有 sub-search 都面向同一个 StructArray 字段下的 element-level vector field 时,才支持该功能。不要在使用 grouping 的 StructArray Hybrid Search 中使用 EmbeddingList-level 请求。

以下示例假设 chunks StructArray 字段包含两个 element-level vector subfield:chunks[emb]chunks[code_emb],并且二者都使用常规 vector metric 建立了 Index。

Python
from pymilvus import AnnSearchRequest, RRFRanker

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,
group_by_field="doc_id",
output_fields=[
"doc_id",
"title",
"chunks[text]",
"chunks[section]",
],
)

在此示例中,两个 sub-request 都面向同一个 StructArray 字段 chunks 下的 element-level vector field。如果混用普通 vector field、不同的 StructArray 字段或 EmbeddingList-level 请求,Hybrid Search 不支持 element-level group-by。

Interpret grouped results

结果项含义
id分组后的父 Entity 的主键。
distance 或 score该父 Entity 中被选中的 Struct 元素的得分或距离。
offset返回时被选中 Struct 元素的从零开始的位置。
重复的主键按主键分组时不应出现。
limit应用于分组后的父 Entity 结果。

Limitations

  • Grouping search 仅适用于 element-level StructArray vector search。EmbeddingList search 和 EmbeddingList-level hybrid search 不支持 group-by。

  • 使用 primary key 作为 group_by_field。StructArray element-level grouping 不是针对任意 scalar field 的通用 group-by。

  • 不要将 grouping search 与 Range Search 结合使用。

  • 不要在 grouping search 中使用 EmbeddingList query 或 MAX_SIM* metric。

  • 仅当所有 sub-search 都以同一个 StructArray field 下的 element-level vector field 为目标时,才支持 hybrid grouping。

  • 如果 Hybrid Search 混合了普通 vector field、不同的 StructArray field 或 EmbeddingList-level request,则不支持 hybrid grouping。

常见错误

  • 将 grouping 用于 chunks[emb_list_vector],该字段用于 EmbeddingList search。

  • 按非主键标量字段进行分组。

  • 按多个字段进行分组。元素级 StructArray 分组仅支持按主键分组。

  • 期望分组结果代表每个匹配的 Struct 元素。分组最多为每个父 Entity 返回一条结果。

  • 认为分组后的元素级 search 会重新计算 EmbeddingList 风格的 MAX_SIM* 分数。分组只是折叠元素级命中结果;它不会改变评分模型。

  • group_by_fieldradiusrange_filter 结合使用。

后续步骤

  1. 如需先了解非分组的元素级搜索,请阅读 Basic Vector Search with StructArray

  2. 如需为分组搜索添加标量过滤条件,请阅读 Filtered Search with StructArray

  3. 如需使用分数或距离边界而不是分组,请阅读 Range Search with StructArray

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