【Milvus】过滤(Filtering)的 概述 和 表达式
过滤(Filtering) 是 Milvus 向量数据库中用于在搜索和查询过程中对数据进行筛选的重要机制。过滤通过对标量字段(如 INT64、VARCHAR、JSON)或向量字段(如 FLOAT_VECTOR、SPARSE_FLOAT_VECTOR)应用条件表达式,限制返回结果的范围,从而支持精确匹配、范围查询、全文检索等功能。过滤表达式通常作为 filter 或 expr 参数传递,配合索引(如
在 Milvus 2.5.10(搭配 pymilvus 2.5.6),过滤(Filtering) 是 Milvus 向量数据库中用于在搜索和查询过程中对数据进行筛选的重要机制。过滤通过对标量字段(如 INT64、VARCHAR、JSON)或向量字段(如 FLOAT_VECTOR、SPARSE_FLOAT_VECTOR)应用条件表达式,限制返回结果的范围,从而支持精确匹配、范围查询、全文检索等功能。过滤常与向量搜索、混合搜索和查询结合使用,广泛应用于推荐系统、RAG(Retrieval-Augmented Generation)、电商搜索等场景。以下基于 Milvus 2.5.10 官方文档(https://milvus.io/docs/zh/boolean.md),详细介绍 Milvus 中的过滤,包括定义、特性、支持的过滤表达式、配置方法、代码示例、适用场景和注意事项。
1. 过滤(Filtering)概述
过滤 是指在 Milvus 的查询(query)或搜索(search、hybrid_search)操作中,通过条件表达式筛选符合特定条件的记录。过滤主要针对标量字段,但也可结合向量字段(如 TEXT_MATCH 用于全文检索)。过滤表达式通常作为 filter 或 expr 参数传递,配合索引(如 INVERTED、TRIE)加速执行。
1.1 过滤作用
- 精确筛选:限制结果到特定条件(如
category == "electronics")。 - 范围查询:查找数值范围内的记录(如
price > 100)。 - 全文检索:匹配包含查询词的文档(如
TEXT_MATCH(content, "AI"))。 - 混合查询:结合向量搜索和标量过滤(如语义搜索 + 类别筛选)。
- 性能优化:通过标量索引(如
INVERTED)减少扫描的数据量。
1.2 过滤特性
- 表达式灵活:支持布尔运算(
AND、OR)、比较运算(==、>)、全文匹配(TEXT_MATCH)等。 - 字段支持:适用于标量字段(
INT64、VARCHAR、JSON等)和部分向量字段(通过TEXT_MATCH)。 - 索引加速:标量索引(如
INVERTED、TRIE)显著提升过滤性能。 - 一致性控制:通过
consistency_level(如ConsistencyLevel.Strong)确保数据新鲜度。 - 分区支持:可指定过滤的分区,缩小查询范围。
1.3 过滤场景
- 查询(Query):仅基于标量字段检索记录(如
client.query)。 - 搜索(Search):结合向量搜索和标量过滤(如
client.search)。 - 混合搜索(Hybrid Search):在多路搜索中应用过滤(如
AnnSearchRequest的expr)。
2. 支持的过滤表达式
Milvus 2.5.10 支持丰富的过滤表达式,涵盖标量字段和全文检索场景。以下是主要表达式类型(参考 https://milvus.io/docs/zh/basic-operators.md):
2.1 比较运算
适用于标量字段(BOOL、INT8、INT16、INT32、INT64、FLOAT、DOUBLE、VARCHAR)。
- 等值:
==,!=- 示例:
category == "electronics",id != 100
- 示例:
- 范围:
>,>=,<,<=- 示例:
price > 100,age <= 30
- 示例:
- 包含:
in,not in- 示例:
category in ["AI", "database"],id not in [1, 2, 3]
- 示例:
- 模式匹配:
like- 示例:
content like "AI%"(前缀匹配)
- 示例:
2.2 算术操作符
算术操作符(+加法 - 减法 * 乘法 / 除法 % 模乘 ** 幂)允许根据涉及数字字段的计算创建条件。
- 查找id 是偶数(即能被 2 整除)的实体
filter = 'id % 2 == 0' - 查找price 升为 2 的幂大于 1000 的实体
filter = 'price ** 2 > 1000'
2.3 IS NULL 和 IS NOT NULL 操作符
IS NULL 和IS NOT NULL 操作符用于根据字段是否包含空值(无数据)过滤字段。IS NULL:识别特定字段包含空值(即值不存在或未定义)的实体。 IS NOT NULL:识别特定字段包含除空值以外的任何值的实体,即字段具有有效的定义值。 空字符串"" 不会被视为VARCHAR 字段的 NULL 值。
- 检索 description 字段为空值的实体:
filter = 'description IS NULL' - 检索description 字段不是空值的实体:
filter = 'description IS NOT NULL' - 检索description 字段不是空值且price 字段大于 10 的实体:
filter = 'description IS NOT NULL AND price > 10' - 在以下情况下,JSON 字段会被视为空值:
- 整个 JSON 对象被明确设置为 None(空),例如{“metadata”: None} 。
- 实体中完全没有 JSON 字段。
2.4 布尔运算
组合多个条件:
- AND:
&&- 示例:
category == "AI" && price > 100
- 示例:
- OR:
||- 示例:
category == "AI" || category == "database"
- 示例:
- 括号:控制优先级
- 示例:
(price > 100 && category == "AI") || id == 1
- 示例:
2.5 全文检索(Text Match)
适用于 VARCHAR 字段(需设置 enable_match=True 和 enable_analyzer=True)。
- TEXT_MATCH:
- 语法:
TEXT_MATCH(field, query) - 描述:匹配包含查询词的文档,默认使用
OR逻辑。 - 示例:
TEXT_MATCH(content, "vector database")(匹配包含“vector”或“database”的记录) - 逻辑支持:
OR:默认,匹配任一词。AND:通过&&显式指定,如TEXT_MATCH(content, "vector && database")。
- 语法:
- 分析器:依赖分析器(如
english、chinese)分词。
2.6 JSON 字段过滤
适用于 JSON 字段。
-
JSON_CONTAINS:
- 语法:
JSON_CONTAINS(field[key], value) - 示例:
JSON_CONTAINS(metadata["category"], "AI")
- 语法:
-
json_contains_all:
- 说明:json_contains_all 操作符可确保目标字段中包含指定 JSON 表达式的所有元素。
- 示例:
filter = 'json_contains_all(product["tags"], ["electronics", "sale", "new"])'
-
json_contains_any:
- 说明:字段中至少存在一个 JSON 表达式成员的实体。
- 示例:
filter = 'json_contains_any(product["tags"], ["electronics", "new", "clearance"])'
-
路径访问:
- 使用
field[key]访问 JSON 键。 - 示例:
metadata["price"] > 100
- 使用
-
索引支持:需为 JSON 键创建
INVERTED索引。
2.7 数组字段过滤
适用于 ARRAY 字段。
-
ARRAY_CONTAINS:
- 语法:
ARRAY_CONTAINS(field, value) - 示例:
ARRAY_CONTAINS(tags, "AI")
- 语法:
-
ARRAY_CONTAINS_ALL:
- 语法:
ARRAY_CONTAINS_ALL(field, [value1, value2]) - 示例:
ARRAY_CONTAINS_ALL(tags, ["AI", "database"])
- 语法:
-
ARRAY_CONTAINS_ANY:
- 语法:
ARRAY_CONTAINS_ANY(field, [value1, value2]) - 示例:
ARRAY_CONTAINS_ANY(tags, ["AI", "cloud"])
- 语法:
-
ARRAY_LENGTH:
- 说明:ARRAY_LENGTH 操作符允许您根据数组字段中元素的数量过滤实体。
- 示例:
filter = 'ARRAY_LENGTH(history_temperatures) < 10', 查找history_temperatures 数组中元素少于 10 个的所有实体。
-
要查找记录温度的数组中第一个温度超过特定值的记录,请使用:
filter = 'history_temperatures[0] > 30'
2.8 过滤器模板
过滤表达式模板化允许你创建带有占位符的过滤表达式,这些占位符可以在查询执行过程中动态替换为值。filter_params来指定具体的值。
- query 或者 search
expr = "age > {age} AND city IN {city}" filter_params = {"age": 25, "city": ["北京", "上海"]} res = client.query( "hello_milvus", filter=expr, output_fields=["age", "city"], filter_params=filter_params ) - delete
expr = "age > {age} AND city IN {city}" filter_params = {"age": 25, "city": ["北京", "上海"]} res = client.delete( "hello_milvus", filter=expr, filter_params=filter_params )
2.9 动态字段过滤
适用于启用 enable_dynamic_field=True 的集合。
- 访问:直接使用动态字段名。
- 示例:
dynamic_field == "extra_data"
3. 过滤配置方法
过滤通过 filter 或 expr 参数在查询和搜索中应用,配合标量索引(如 INVERTED)加速执行。以下是配置步骤和代码示例。
3.1 配置步骤
- 定义 Schema:
- 指定标量字段(
VARCHAR、JSON、ARRAY等)并启用过滤支持(如enable_match=True)。 - 为全文检索配置
FunctionType.BM25和SPARSE_FLOAT_VECTOR。
- 指定标量字段(
- 创建索引:
- 为标量字段创建
INVERTED、TRIE等索引。 - 为向量字段创建
HNSW、AUTOINDEX等索引。
- 为标量字段创建
- 插入数据:
- 填充标量和向量数据。
- 执行过滤:
- 在
client.query或client.search中指定filter。 - 在
client.hybrid_search中为每个AnnSearchRequest指定expr.
- 在
3.2 代码示例:综合过滤
以下示例展示如何在查询、单向量搜索和混合搜索中使用过滤,涵盖比较运算、全文检索、JSON 过滤和数组过滤。
from pymilvus import MilvusClient, DataType, AnnSearchRequest, RRFRanker, FunctionType, Function
from pymilvus.client.constants import ConsistencyLevel
import random
# 连接 Milvus
client = MilvusClient(uri="http://localhost:19530")
try:
# 创建数据库
client.create_database(db_name="test_db")
client.use_database(db_name="test_db")
# 创建 Schema
schema = MilvusClient.create_schema(auto_id=True, enable_dynamic_field=True)
schema.add_field(
field_name="pk",
datatype=DataType.VARCHAR,
is_primary=True,
auto_id=True,
max_length=100
)
schema.add_field(
field_name="content",
datatype=DataType.VARCHAR,
max_length=65535,
enable_analyzer=True,
enable_match=True,
analyzer_params={"type": "english"}
)
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
schema.add_field(field_name="dense_vector", datatype=DataType.FLOAT_VECTOR, dim=128)
schema.add_field(field_name="price", datatype=DataType.DOUBLE)
schema.add_field(
field_name="tags",
datatype=DataType.ARRAY,
element_type=DataType.VARCHAR,
max_capacity=10,
max_length=50
)
schema.add_field(field_name="metadata", datatype=DataType.JSON)
# 定义 BM25 函数
bm25_function = Function(
name="content_bm25_sparse",
input_field_names=["content"],
output_field_names=["sparse_vector"],
function_type=FunctionType.BM25
)
schema.add_function(bm25_function)
# 创建集合
client.create_collection(
collection_name="filter_collection",
schema=schema,
consistency_level=ConsistencyLevel.Session
)
# 创建索引
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="sparse_vector",
index_type="AUTOINDEX",
metric_type="BM25"
)
index_params.add_index(
field_name="dense_vector",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 200}
)
index_params.add_index(field_name="content", index_type="INVERTED")
index_params.add_index(field_name="price", index_type="STL_SORT")
index_params.add_index(field_name="tags", index_type="INVERTED")
index_params.add_index(
field_name="metadata",
index_type="INVERTED",
index_name="json_category",
params={"json_path": "metadata[\"category\"]", "json_cast_type": "varchar"}
)
client.create_index(collection_name="filter_collection", index_params=index_params)
# 插入数据
data = [
{
"content": "Milvus supports hybrid search with BM25 and dense vectors.",
"dense_vector": [random.random() for _ in range(128)],
"price": 150.0,
"tags": ["database", "vector"],
"metadata": {"category": "database"},
"dynamic_field": "extra1"
},
{
"content": "AI applications benefit from vector databases.",
"dense_vector": [random.random() for _ in range(128)],
"price": 200.0,
"tags": ["AI", "database"],
"metadata": {"category": "AI"},
"dynamic_field": "extra2"
},
{
"content": "Vector search powers modern AI solutions.",
"dense_vector": [random.random() for _ in range(128)],
"price": 80.0,
"tags": ["AI", "search"],
"metadata": {"category": "AI"},
"dynamic_field": "extra3"
}
]
client.insert(collection_name="filter_collection", data=data)
# 加载集合
client.load_collection(collection_name="filter_collection")
# 1. 查询(标量过滤)
print("Query with scalar filtering:")
results = client.query(
collection_name="filter_collection",
filter='price > 100 && metadata["category"] == "AI"',
output_fields=["content", "price", "metadata"],
consistency_level=ConsistencyLevel.Strong
)
for result in results:
print(f"Document: {result['content']}, Price: {result['price']}, Metadata: {result['metadata']}")
# 2. 单向量搜索(BM25 + 全文检索过滤)
print("\nBM25 search with text match filtering:")
results = client.search(
collection_name="filter_collection",
data=["vector database"],
anns_field="sparse_vector",
limit=2,
filter="TEXT_MATCH(content, 'vector')",
output_fields=["content", "metadata"],
consistency_level=ConsistencyLevel.Strong
)
for result in results[0]:
print(f"Document: {result['entity']['content']}, Score: {result['distance']}")
# 3. 单向量搜索(语义搜索 + 数组和动态字段过滤)
print("\nDense vector search with array and dynamic field filtering:")
dense_query_vector = [random.random() for _ in range(128)]
results = client.search(
collection_name="filter_collection",
data=[dense_query_vector],
anns_field="dense_vector",
limit=2,
filter='ARRAY_CONTAINS(tags, "AI") && dynamic_field == "extra2"',
output_fields=["content", "tags", "dynamic_field"],
search_params={"metric_type": "COSINE", "params": {"ef": 64}},
consistency_level=ConsistencyLevel.Strong
)
for result in results[0]:
print(f"Document: {result['entity']['content']}, Tags: {result['entity']['tags']}, Dynamic: {result['entity']['dynamic_field']}")
# 4. 混合搜索(BM25 + Dense vector + 复杂过滤)
print("\nHybrid search with complex filtering:")
sparse_request = AnnSearchRequest(
data=["vector database"],
anns_field="sparse_vector",
param={"metric_type": "BM25"},
limit=2,
expr="TEXT_MATCH(content, 'vector') && metadata[\"category\"] in [\"database\", \"AI\"]"
)
dense_request = AnnSearchRequest(
data=[dense_query_vector],
anns_field="dense_vector",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=2,
expr="price > 100 && ARRAY_CONTAINS_ANY(tags, [\"AI\", \"database\"])"
)
results = client.hybrid_search(
collection_name="filter_collection",
reqs=[sparse_request, dense_request],
ranker=RRFRanker(),
limit=2,
output_fields=["content", "price", "tags", "metadata"],
consistency_level=ConsistencyLevel.Strong
)
for result in results[0]:
print(f"Document: {result['entity']['content']}, Price: {result['entity']['price']}, Tags: {result['entity']['tags']}, Metadata: {result['entity']['metadata']}, Score: {result['distance']}")
except Exception as e:
print(f"Error: {e}")
finally:
# 清理
client.drop_collection(collection_name="filter_collection")
client.drop_database(db_name="test_db")
输出示例
Query with scalar filtering:
Document: AI applications benefit from vector databases., Price: 200.0, Metadata: {'category': 'AI'}
BM25 search with text match filtering:
Document: Milvus supports hybrid search with BM25 and dense vectors., Score: 0.912
Document: AI applications benefit from vector databases., Score: 0.745
Dense vector search with array and dynamic field filtering:
Document: AI applications benefit from vector databases., Tags: ['AI', 'database'], Dynamic: extra2
Hybrid search with complex filtering:
Document: Milvus supports hybrid search with BM25 and dense vectors., Price: 150.0, Tags: ['database', 'vector'], Metadata: {'category': 'database'}, Score: 0.912
Document: AI applications benefit from vector databases., Price: 200.0, Tags: ['AI', 'database'], Metadata: {'category': 'AI'}, Score: 0.745
代码说明
- Schema:
- 包含
pk(主键)、content(VARCHAR,支持全文检索)、sparse_vector(BM25)、dense_vector(语义搜索)、price(DOUBLE)、tags(ARRAY)、metadata(JSON)。 - 启用
enable_dynamic_field=True支持动态字段。 - 添加
FunctionType.BM25函数。
- 包含
- 索引:
sparse_vector:AUTOINDEX(BM25)。dense_vector:HNSW(COSINE)。content:INVERTED(全文检索)。price:STL_SORT(范围查询)。tags:INVERTED(数组查询)。metadata:INVERTED(JSON 过滤)。
- 过滤:
- 查询:使用
price > 100 && metadata["category"] == "AI"。 - BM25 搜索:使用
TEXT_MATCH(content, 'vector')。 - 语义搜索:使用
ARRAY_CONTAINS(tags, "AI") && dynamic_field == "extra2"。 - 混合搜索:结合
TEXT_MATCH、JSON 过滤(metadata["category"] in [...])、范围过滤(price > 100)和数组过滤(ARRAY_CONTAINS_ANY)。
- 查询:使用
4. 过滤的适用场景
- 精确筛选:
- 电商:筛选价格范围内的产品(
price > 100 && price <= 200)。 - 分类过滤:查找特定类别的记录(
metadata["category"] == "AI")。
- 电商:筛选价格范围内的产品(
- 全文检索:
- RAG:检索包含关键词的文档(
TEXT_MATCH(content, "vector database"))。 - 技术文档:搜索包含特定术语的记录。
- RAG:检索包含关键词的文档(
- 数组查询:
- 标签过滤:查找包含特定标签的记录(
ARRAY_CONTAINS(tags, "AI"))。 - 多条件匹配:筛选包含多个标签的记录(
ARRAY_CONTAINS_ALL(tags, ["AI", "database"]))。
- 标签过滤:查找包含特定标签的记录(
- JSON 过滤:
- 元数据筛选:查找特定元数据的记录(
JSON_CONTAINS(metadata["category"], "database"))。 - 复杂条件:结合 JSON 键值查询(
metadata["price"] > 100)。
- 元数据筛选:查找特定元数据的记录(
- 动态字段:
- 灵活筛选:处理用户上传的动态元数据(
dynamic_field == "extra1")。
- 灵活筛选:处理用户上传的动态元数据(
- 混合搜索:
- 多模态搜索:结合关键词、语义和元数据过滤(如电商推荐)。
5. 过滤配置建议
- 表达式优化:
- 使用简单条件(如
==、in)提高性能。 - 避免复杂嵌套表达式,优先拆分为多次过滤。
- 使用简单条件(如
- 索引支持:
- 为频繁过滤的字段创建
INVERTED(等值、JSON)、STL_SORT(范围)或TRIE(前缀)索引。 - 全文检索需启用
enable_match=True和INVERTED索引。
- 为频繁过滤的字段创建
- 分析器选择:
- 全文检索使用
english(英语)或chinese(中文)分析器。 - 自定义停用词和词干提取,提升
TEXT_MATCH效果。
- 全文检索使用
- 性能优化:
- 限制
limit参数,减少结果集大小。 - 使用分区(
partition_names)缩小过滤范围。
- 限制
- 调试:
- 测试过滤表达式,确保语法正确。
- 检查字段类型和索引配置,避免性能瓶颈。
6. 注意事项
- 版本兼容性:
- 确保使用 Milvus 2.5.10 和
pymilvus2.5.6(pip show pymilvus)。 TEXT_MATCH和 JSON 过滤在 2.5.x 中增强。
- 确保使用 Milvus 2.5.10 和
- 表达式限制:
- 过滤表达式需匹配字段类型(如
VARCHAR字段使用字符串值)。 TEXT_MATCH要求enable_match=True和enable_analyzer=True。
- 过滤表达式需匹配字段类型(如
- 性能影响:
- 复杂过滤表达式可能降低性能,优先使用索引。
- 大规模数据集建议分批查询或分区过滤。
- 错误处理:
- 检查表达式语法和字段名:
try: client.query(...) except Exception as e: print(f"Error: {e}")
- 检查表达式语法和字段名:
7. 总结
Milvus 2.5.10 的 过滤(Filtering) 通过条件表达式筛选标量字段(VARCHAR、JSON、ARRAY)和向量字段(TEXT_MATCH),支持比较运算、布尔运算、全文检索和动态字段过滤。过滤与查询、搜索和混合搜索集成,配合标量索引(如 INVERTED、STL_SORT)和分析器实现高效筛选。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)