Milvus 2.5.10(搭配 pymilvus 2.5.6),过滤(Filtering) 是 Milvus 向量数据库中用于在搜索和查询过程中对数据进行筛选的重要机制。过滤通过对标量字段(如 INT64VARCHARJSON)或向量字段(如 FLOAT_VECTORSPARSE_FLOAT_VECTOR)应用条件表达式,限制返回结果的范围,从而支持精确匹配、范围查询、全文检索等功能。过滤常与向量搜索、混合搜索和查询结合使用,广泛应用于推荐系统、RAG(Retrieval-Augmented Generation)、电商搜索等场景。以下基于 Milvus 2.5.10 官方文档(https://milvus.io/docs/zh/boolean.md),详细介绍 Milvus 中的过滤,包括定义、特性、支持的过滤表达式、配置方法、代码示例、适用场景和注意事项。


1. 过滤(Filtering)概述

过滤 是指在 Milvus 的查询(query)或搜索(searchhybrid_search)操作中,通过条件表达式筛选符合特定条件的记录。过滤主要针对标量字段,但也可结合向量字段(如 TEXT_MATCH 用于全文检索)。过滤表达式通常作为 filterexpr 参数传递,配合索引(如 INVERTEDTRIE)加速执行。

1.1 过滤作用

  • 精确筛选:限制结果到特定条件(如 category == "electronics")。
  • 范围查询:查找数值范围内的记录(如 price > 100)。
  • 全文检索:匹配包含查询词的文档(如 TEXT_MATCH(content, "AI"))。
  • 混合查询:结合向量搜索和标量过滤(如语义搜索 + 类别筛选)。
  • 性能优化:通过标量索引(如 INVERTED)减少扫描的数据量。

1.2 过滤特性

  • 表达式灵活:支持布尔运算(ANDOR)、比较运算(==>)、全文匹配(TEXT_MATCH)等。
  • 字段支持:适用于标量字段(INT64VARCHARJSON 等)和部分向量字段(通过 TEXT_MATCH)。
  • 索引加速:标量索引(如 INVERTEDTRIE)显著提升过滤性能。
  • 一致性控制:通过 consistency_level(如 ConsistencyLevel.Strong)确保数据新鲜度。
  • 分区支持:可指定过滤的分区,缩小查询范围。

1.3 过滤场景

  • 查询(Query):仅基于标量字段检索记录(如 client.query)。
  • 搜索(Search):结合向量搜索和标量过滤(如 client.search)。
  • 混合搜索(Hybrid Search):在多路搜索中应用过滤(如 AnnSearchRequestexpr)。

2. 支持的过滤表达式

Milvus 2.5.10 支持丰富的过滤表达式,涵盖标量字段和全文检索场景。以下是主要表达式类型(参考 https://milvus.io/docs/zh/basic-operators.md):

2.1 比较运算

适用于标量字段(BOOLINT8INT16INT32INT64FLOATDOUBLEVARCHAR)。

  • 等值==, !=
    • 示例: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 NULLIS 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=Trueenable_analyzer=True)。

  • TEXT_MATCH
    • 语法:TEXT_MATCH(field, query)
    • 描述:匹配包含查询词的文档,默认使用 OR 逻辑。
    • 示例:TEXT_MATCH(content, "vector database")(匹配包含“vector”或“database”的记录)
    • 逻辑支持:
      • OR:默认,匹配任一词。
      • AND:通过 && 显式指定,如 TEXT_MATCH(content, "vector && database")
  • 分析器:依赖分析器(如 englishchinese)分词。

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. 过滤配置方法

过滤通过 filterexpr 参数在查询和搜索中应用,配合标量索引(如 INVERTED)加速执行。以下是配置步骤和代码示例。

3.1 配置步骤

  1. 定义 Schema
    • 指定标量字段(VARCHARJSONARRAY 等)并启用过滤支持(如 enable_match=True)。
    • 为全文检索配置 FunctionType.BM25SPARSE_FLOAT_VECTOR
  2. 创建索引
    • 为标量字段创建 INVERTEDTRIE 等索引。
    • 为向量字段创建 HNSWAUTOINDEX 等索引。
  3. 插入数据
    • 填充标量和向量数据。
  4. 执行过滤
    • client.queryclient.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(主键)、contentVARCHAR,支持全文检索)、sparse_vector(BM25)、dense_vector(语义搜索)、priceDOUBLE)、tagsARRAY)、metadataJSON)。
    • 启用 enable_dynamic_field=True 支持动态字段。
    • 添加 FunctionType.BM25 函数。
  • 索引
    • sparse_vectorAUTOINDEX(BM25)。
    • dense_vectorHNSW(COSINE)。
    • contentINVERTED(全文检索)。
    • priceSTL_SORT(范围查询)。
    • tagsINVERTED(数组查询)。
    • metadataINVERTED(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"))。
    • 技术文档:搜索包含特定术语的记录。
  • 数组查询
    • 标签过滤:查找包含特定标签的记录(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=TrueINVERTED 索引。
  • 分析器选择
    • 全文检索使用 english(英语)或 chinese(中文)分析器。
    • 自定义停用词和词干提取,提升 TEXT_MATCH 效果。
  • 性能优化
    • 限制 limit 参数,减少结果集大小。
    • 使用分区(partition_names)缩小过滤范围。
  • 调试
    • 测试过滤表达式,确保语法正确。
    • 检查字段类型和索引配置,避免性能瓶颈。

6. 注意事项

  • 版本兼容性
    • 确保使用 Milvus 2.5.10 和 pymilvus 2.5.6(pip show pymilvus)。
    • TEXT_MATCH 和 JSON 过滤在 2.5.x 中增强。
  • 表达式限制
    • 过滤表达式需匹配字段类型(如 VARCHAR 字段使用字符串值)。
    • TEXT_MATCH 要求 enable_match=Trueenable_analyzer=True
  • 性能影响
    • 复杂过滤表达式可能降低性能,优先使用索引。
    • 大规模数据集建议分批查询或分区过滤。
  • 错误处理
    • 检查表达式语法和字段名:
      try:
          client.query(...)
      except Exception as e:
          print(f"Error: {e}")
      

7. 总结

Milvus 2.5.10 的 过滤(Filtering) 通过条件表达式筛选标量字段(VARCHARJSONARRAY)和向量字段(TEXT_MATCH),支持比较运算、布尔运算、全文检索和动态字段过滤。过滤与查询、搜索和混合搜索集成,配合标量索引(如 INVERTEDSTL_SORT)和分析器实现高效筛选。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐