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_field 的 Basic 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 参数(如 radius 或 range_filter)结合使用。 |
有关 Index 设置,参见 Index StructArray Fields。
执行 grouped element-level search
以下示例会先搜索各个 chunk,然后按父 Entity 的主键对命中的元素进行分组。
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。
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 折叠匹配的元素命中结果。
Use grouping in hybrid search
使用 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。
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 中使用
EmbeddingListquery 或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_field与radius或range_filter结合使用。
后续步骤
-
如需先了解非分组的元素级搜索,请阅读 Basic Vector Search with StructArray。
-
如需为分组搜索添加标量过滤条件,请阅读 Filtered Search with StructArray。
-
如需使用分数或距离边界而不是分组,请阅读 Range Search with StructArray。
-
如需查看 StructArray 搜索限制,请阅读 StructArray Limits。