Filtered Search with StructArray
本页介绍如何在 StructArray 字段上为向量搜索添加标量过滤。StructArray 过滤分为两个层级:行级过滤用于选择父 Entity,而元素级过滤用于约束哪些 Struct 元素参与元素级向量搜索。
本页使用 Create a StructArray Field 中的 tech_articles Collection。该 Collection 包含一个名为 chunks 的 StructArray 字段,其中包括 section、page、quality_score 和 has_code 等标量子字段,以及用于搜索的向量子字段。
选择 filter 类型
| 目标 | 使用方式 | 结果行为 |
|---|---|---|
按顶层标量字段(例如 category)进行过滤。 | 常规 filter expression。 | 在搜索前或搜索过程中选择父 Entity。 |
| 将元素级向量搜索限制在满足标量条件的 Struct 元素中。 | element_filter。 | 仅搜索匹配的 Struct 元素,并可返回匹配元素的 offset。 |
| 根据是否有任意、全部或特定数量的 Struct 元素匹配 predicate 来选择 Entity。 | MATCH_ANY、MATCH_ALL、MATCH_LEAST、MATCH_MOST 或 MATCH_EXACT。 | 行级过滤。这些运算符本身不会返回 offset。 |
本页说明如何在搜索工作流中使用 StructArray filter。有关完整语法规则、支持的 predicate 类型以及不支持的 predicate 矩阵,请参阅 StructArray Operators。
Filter by top-level fields
当过滤条件属于父 Entity,而不是某个单独的 Struct 元素时,请使用常规过滤表达式。这同时适用于 EmbeddingList search 和 element-level search。
from pymilvus import MilvusClient
from pymilvus.client.embedding_list import EmbeddingList
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus",
)
query = EmbeddingList()
query.add([0.12, 0.21, 0.32, 0.44])
query.add([0.18, 0.23, 0.29, 0.36])
results = client.search(
collection_name="tech_articles",
data=[query],
anns_field="chunks[emb_list_vector]",
filter='category == "search"',
limit=3,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
],
)
上面的过滤条件仅选择顶层 category 字段为 "search" 的 Entity。它不会标识某个匹配的 Struct 元素。
Filter element-level vector search
当标量条件必须应用于参与 element-level vector search 的同一个 Struct element 时,使用 element_filter(structArrayField, predicate)。在 predicate 内,使用 $[subfield] 引用当前 Struct element 的标量子字段。
query_vector = [0.19, 0.24, 0.30, 0.37]
filter_expr = (
'category == "search" && '
'element_filter(chunks, '
'$[section] == "index" && '
'$[quality_score] > 0.9 && '
'$[has_code] == true)'
)
results = client.search(
collection_name="tech_articles",
data=[query_vector],
anns_field="chunks[emb]",
filter=filter_expr,
limit=5,
output_fields=[
"doc_id",
"title",
"chunks[text]",
"chunks[section]",
"chunks[page]",
"chunks[quality_score]",
"chunks[has_code]",
],
)
for hits in results:
for hit in hits:
print(
"doc_id:", hit["id"],
"distance:", hit["distance"],
"offset:", hit.get("offset"),
"entity:", hit["entity"],
)
在此示例中,顶层 predicate category == "search" 用于选择候选 Entity,而 element_filter 会将 element-level vector search 限制在 section、quality_score 和 has_code 都在同一个 Struct element 中匹配的 chunk 上。
警告
将顶层 predicate 与 element_filter 结合使用时,请将 element_filter 放在表达式末尾。一个 filter expression 只能包含一个 element_filter,并且不能在另一个 StructArray operator 内嵌套 element_filter 或 MATCH_*。
Filter entities with MATCH operators
当 filter 需要根据 Struct 元素判断父 Entity 是否符合条件时,使用 MATCH_* operators。这些 operators 是行级 filter:它们会选择 Entity,但本身不会返回元素偏移量。
| Operator | 适用场景 | 示例 |
|---|---|---|
MATCH_ANY | 至少一个 Struct 元素必须满足谓词。 | MATCH_ANY(chunks, $[section] == "index") |
MATCH_ALL | 所有 Struct 元素都必须满足谓词。 | MATCH_ALL(chunks, $[quality_score] > 0.5) |
MATCH_LEAST | 至少 N 个 Struct 元素必须满足谓词。 | MATCH_LEAST(chunks, $[has_code] == true, threshold=2) |
MATCH_MOST | 最多 N 个 Struct 元素必须满足谓词。 | MATCH_MOST(chunks, $[section] == "appendix", threshold=1) |
MATCH_EXACT | 恰好 N 个 Struct 元素必须满足谓词。 | MATCH_EXACT(chunks, $[section] == "summary", threshold=1) |
filter_expr = (
'category == "search" && '
'MATCH_ANY(chunks, $[section] == "index" && $[quality_score] > 0.9)'
)
results = client.search(
collection_name="tech_articles",
data=[query],
anns_field="chunks[emb_list_vector]",
filter=filter_expr,
limit=3,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)
这里使用 MATCH_ANY,因为 EmbeddingList search result 是 Entity 级别的。该 filter 要求 Entity 中至少有一个 chunk 是高质量的 "index" chunk,但 search result 本身仍表示父 Entity。
在 Hybrid Search 中使用 Filter
在 Hybrid Search 中,请在条件应生效的位置应用 StructArray filter。顶层 filter 可由整个 Hybrid Search 共享。element_filter 应附加到需要元素级约束的 StructArray 元素级请求上。
from pymilvus import AnnSearchRequest, RRFRanker
query_vector = [0.19, 0.24, 0.30, 0.37]
title_req = AnnSearchRequest(
data=[query_vector],
anns_field="title_vector",
limit=10,
)
chunk_req = AnnSearchRequest(
data=[query_vector],
anns_field="chunks[emb]",
limit=10,
expr='element_filter(chunks, $[section] == "index" && $[quality_score] > 0.9)',
)
results = client.hybrid_search(
collection_name="tech_articles",
reqs=[title_req, chunk_req],
ranker=RRFRanker(),
filter='category == "search"',
limit=5,
output_fields=[
"doc_id",
"title",
"category",
"chunks[text]",
"chunks[section]",
"chunks[quality_score]",
],
)
filter 参数用于应用顶层 Entity 条件,而 chunk_req 上的 expr 仅约束 StructArray 元素级向量请求。有关支持的 Hybrid Search 组合和特定版本限制,请参阅 Hybrid Search with StructArray 和 StructArray Limits。
Predicate support summary
在 StructArray predicate 中使用标量子字段。Vector 子字段不是标量 predicate 输入。
| 子字段类型 | 典型 predicate 示例 |
|---|---|
BOOL | $[has_code] == true, !($[has_code] == true) |
| Integer 类型 | $[page] >= 2, $[page] in [1, 2, 3] |
FLOAT, DOUBLE | $[quality_score] > 0.9, 0.7 < $[quality_score] < 0.95 |
VARCHAR | $[section] == "index", $[text] like "range%" |
| Vector 子字段 | 不支持作为 $[...] 标量 predicate 输入。请改为通过 vector search 使用 Vector 子字段。 |
对于不支持的情况,例如 JSON 路径、数组容器函数、text match 函数、$[...] 上的 NULL predicate、Geometry 函数、Timestamptz 表达式以及通用函数调用,请参阅 StructArray Operators。
常见错误
-
在
element_filter或MATCH_*之外使用$[subfield]。 -
使用
chunks.section,而不是 StructArray operator 语法,例如element_filter(chunks, $[section] == "index")。 -
在只需要行级过滤时使用
element_filter。如果你只需要选择 entities,请改用MATCH_ANY。 -
期望
MATCH_*返回元素偏移量。这些 operators 用于选择 entities,本身不会识别某个匹配的元素。 -
编写裸 boolean predicates,例如
$[has_code]。请使用显式比较,例如$[has_code] == true。 -
在同一个 filter 表达式中,将
element_filter放在顶层 predicate 之前。
后续步骤
-
要查看完整的 StructArray filter 语法,请阅读 StructArray Operators。
-
要先运行不带 filter 的 vector search,请阅读 Basic Vector Search with StructArray。
-
要为常用的 StructArray filter 创建 scalar index,请阅读 Index StructArray Fields。
-
要查看特定版本的 filter 和 search 限制,请阅读 StructArray Limits。