Milvus:Json字段详解(十)

一、JSON 字段概述

1.1 什么是 JSON 字段

Milvus 的 JSON 字段是支持结构化键值数据存储的灵活数据类型,能兼容嵌套对象、数组等复杂结构,同时提供索引和查询优化能力,适用于多类复杂数据场景。以下是完善后的详细指南,包含更细致的说明和代码解析。
JSON 字段是 Milvus 中以 DataType.JSON 声明的 Schema 字段类型,专门用于存储结构化及半结构化数据。它打破了传统数据库刚性列的限制,允许在单个字段中包含嵌套对象(如多层级的供应商信息)、数组(如产品标签列表)和混合数据类型(字符串、数值、布尔值共存),同时支持针对性索引配置,确保复杂结构数据的查询效率。

1.2 应用场景

  • 产品目录系统:存储不同品类产品的差异化属性,比如电子产品的参数、服装的尺码信息,无需为每类产品单独设计 Schema。
  • 内容管理系统:管理带复杂嵌套结构的文档属性,例如文章的作者信息、分类标签、关联附件等多层级数据。
  • 用户偏好引擎:记录随时间变化的用户行为数据,比如用户浏览历史、收藏偏好、设置选项等动态信息。
  • 物联网数据存储:处理设备传感器产生的异构数据,比如温度、湿度等数值型数据与设备位置、状态等结构化数据的混合存储。

二、JSON 字段基础

2.1 JSON 字段结构示例

复制代码
"metadata": {
  "category": "electronics",  // 一级字段:产品类别(字符串类型)
  "brand": "BrandA",          // 一级字段:品牌(字符串类型)
  "in_stock": true,           // 一级字段:库存状态(布尔类型)
  "price": 99.99,             // 一级字段:价格(数值类型)
  "string_price": "99.99",    // 一级字段:字符串格式价格(字符串类型)
  "tags": ["clearance", "summer_sale"],  // 一级字段:标签数组(字符串数组)
  "supplier": {               // 一级字段:嵌套对象(供应商信息)
    "name": "SupplierX",      // 二级嵌套字段:供应商名称
    "country": "USA",         // 二级嵌套字段:供应商国家
    "contact": {              // 三级嵌套字段:联系信息子对象
      "email": "support@supplierx.com",  // 三级字段:邮箱
      "phone": "+1-800-555-0199"         // 三级字段:电话
    }
  }
}

结构特点:支持多层嵌套(示例中最多三级)、数组与对象混用、多数据类型共存,且字段可灵活扩展。

2.2 JSON 字段 vs 动态字段

|-----------|-------------------------------------------------|------------------------------------------------------------------------|
| 特征 | JSON 字段 | 动态字段 |
| Schema 定义 | 标量字段,需在 Collections Schema 中明确声明为 DataType.JSON | 隐藏的 JSON 字段(默认名为 #meta ),无需手动声明 |
| 使用情况 | 适用于存储模式已知、结构一致的结构化数据,比如固定属性的产品信息 | 适用于存储模式灵活、频繁变化或半结构化数据,比如用户自定义属性 |
| 控制方式 | 字段名称、结构由开发者完全控制,可提前规划数据组织形式 | 未定义的字段由系统自动管理,无需手动维护字段结构 |
| 查询方式 | 需通过字段名 + JSON 路径查询(如 metadata["category"] ) | 两种查询方式:直接用动态键(如 "dynamic_key" )或通过 #meta 路径(如 #meta["dynamic_key"] ) |
| 核心优势 | 结构可控、查询路径明确,支持针对性索引优化 | 无需预先定义结构,适配快速变化的数据场景 |
| 适用场景 | 产品目录、固定格式文档存储 | 用户自定义属性、临时数据记录 |

三、基本操作

3.1 定义 JSON 字段

python 复制代码
# 1. 导入必要的库:MilvusClient 用于连接服务器,DataType 定义字段类型
from pymilvus import MilvusClient, DataType

# 2. 连接到 Milvus 服务器:uri 为服务器地址(本地默认 19530 端口)
client = MilvusClient(uri="http://localhost:19530")  # 生产环境需替换为实际服务器地址

# 3. 创建 Schema:auto_id=False 表示手动指定主键,enable_dynamic_field=True 启用动态字段(可选)
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)

# 4. 添加主键字段:product_id 作为唯一标识,类型为 INT64,设为必填主键
schema.add_field(
    field_name="product_id",
    datatype=DataType.INT64,
    is_primary=True  # 标记为主键字段
)

# 5. 添加向量字段:用于向量搜索,类型为 FLOAT_VECTOR,维度 dim=5(需与实际向量维度一致)
schema.add_field(
    field_name="vector",
    datatype=DataType.FLOAT_VECTOR,
    dim=5  # 向量维度,根据业务数据调整(如图片特征向量可能为 512 维)
)

# 6. 定义 JSON 字段:字段名为 metadata,类型为 JSON,nullable=True 允许空值(可选配置)
schema.add_field(
    field_name="metadata",
    datatype=DataType.JSON,
    nullable=True  # 设为 True 时,该字段可插入空值(如部分产品无供应商信息)
)

# 7. 创建集合:集合名 product_catalog,关联上述定义的 Schema
client.create_collection(
    collection_name="product_catalog",  # 集合名称(需唯一)
    schema=schema  # 绑定之前创建的 Schema
)

print("集合创建成功!")  # 执行成功后输出提示

关键说明:JSON 字段必须在 Schema 中明确声明,可通过 nullable 控制是否允许空值,需与主键、向量字段配合使用(Milvus 集合需包含向量字段用于向量搜索)。

3.2 插入数据

python 复制代码
# 1. 准备插入的数据:entities 为列表,每个元素是一条完整记录(包含主键、向量、JSON 字段)
entities = [
    {
        "product_id": 1,  # 主键值(需唯一,auto_id=False 时必须手动指定)
        "vector": [0.1, 0.2, 0.3, 0.4, 0.5],  # 向量数据(维度需与 Schema 中 dim 一致)
        "metadata": {  # JSON 字段内容,结构与 Schema 定义一致
            "category": "electronics",
            "brand": "BrandA",
            "in_stock": True,
            "price": 99.99,
            "string_price": "99.99",
            "tags": ["clearance", "summer_sale"],  # 数组类型值
            "supplier": {  # 嵌套对象类型值
                "name": "SupplierX",
                "country": "USA",
                "contact": {  # 三级嵌套
                    "email": "support@supplierx.com",
                    "phone": "+1-800-555-0199"
                }
            }
        }
    },
    {
        "product_id": 2,  # 第二条记录主键(需与第一条不同)
        "vector": [0.2, 0.3, 0.4, 0.5, 0.6],
        "metadata": {
            "category": "clothing",
            "brand": "BrandB",
            "in_stock": False,
            "price": 49.99,
            "tags": ["new", "winter"],
            "supplier": {
                "name": "SupplierY",
                "country": "China",
                "contact": {
                    "email": "contact@suppliery.com",
                    "phone": "+86-10-555-0199"
                }
            }
        }
    }
]

# 2. 插入数据:指定集合名和待插入数据,返回结果包含插入成功的 ID
result = client.insert(
    collection_name="product_catalog",  # 目标集合名
    data=entities  # 待插入的记录列表
)

# 3. 输出插入结果:result['ids'] 包含所有插入成功的主键值
print(f"插入成功!插入的ID: {result['ids']}")

关键说明:JSON 字段的结构可灵活调整(如第二条记录缺少 string_price 字段),但相同键的数据类型建议保持一致(避免同一键既存数值又存字符串);向量维度必须与 Schema 定义的 dim 匹配,否则插入失败。

3.3 基本查询操作

3.3.1 使用 JSON 路径语法进行筛选
python 复制代码
# 1. 定义过滤条件:筛选 metadata 中 category 为 "electronics" 的记录
# 语法规则:JSON 字段名["键名"],字符串值需用双引号包裹
filter_expr = 'metadata["category"] == "electronics"'

# 2. 执行搜索:结合向量搜索和属性过滤
results = client.search(
    collection_name="product_catalog",  # 目标集合名
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],  # 查询向量(与待搜索向量维度一致)
    limit=5,  # 返回结果的最大数量
    filter=filter_expr,  # 应用上述过滤条件
    output_fields=["product_id", "metadata"]  # 指定返回的字段(主键 + JSON 字段)
)

# 3. 解析并输出结果
print("电子产品搜索结果:")
for result in results:  # 遍历返回的结果列表
    # result['entity'] 包含单条记录的字段数据
    print(f"产品ID: {result['entity']['product_id']}, 距离: {result['distance']}")
    print(f"元数据: {result['entity']['metadata']}")

语法说明:metadata["category"] 是 JSON 路径表达式,用于定位 JSON 字段中的具体键;distance 是查询向量与结果向量的相似度距离(值越小越相似)。

3.3.2 过滤嵌套键
python 复制代码
# 1. 定义过滤条件:筛选嵌套键 supplier -> country 为 "USA" 的记录
# 嵌套路径语法:JSON 字段名["父键"]["子键"](支持多层嵌套)
filter_expr = 'metadata["supplier"]["country"] == "USA"'

# 2. 执行搜索:查询向量与上例一致,仅过滤条件不同
results = client.search(
    collection_name="product_catalog",
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],
    limit=5,
    filter=filter_expr,
    output_fields=["product_id", "metadata"]
)

# 3. 输出结果
print("美国供应商产品搜索结果:")
for result in results:
    print(f"产品ID: {result['entity']['product_id']}")

关键说明:嵌套路径需准确匹配 JSON 结构,若某条记录缺少路径中的某个键(如无 supplier 字段),则该记录不会被匹配到。

3.4 使用 JSON 特定操作符

3.4.1 数组包含匹配(json_contains)
python 复制代码
# 1. 过滤条件:查找 tags 数组中包含 "summer_sale" 的产品
# json_contains(数组路径, 目标值):判断数组是否包含指定值
filter_expr = 'json_contains(metadata["tags"], "summer_sale")'

results = client.search(
    collection_name="product_catalog",
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],
    limit=5,
    filter=filter_expr,
    output_fields=["product_id", "metadata"]
)

print("夏季促销产品:")
for result in results:
    print(f"产品ID: {result['entity']['product_id']}")
3.4.2 数组多值包含匹配(json_contains_any)
python 复制代码
# 1. 过滤条件:查找 tags 数组中包含 "electronics"、"new" 或 "clearance" 任意一个值的产品
# json_contains_any(数组路径, [值1, 值2, ...]):判断数组是否包含任意一个目标值
filter_expr = 'json_contains_any(metadata["tags"], ["electronics", "new", "clearance"])'

results = client.search(
    collection_name="product_catalog",
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],
    limit=5,
    filter=filter_expr,
    output_fields=["product_id", "metadata"]
)

print("包含指定标签的产品:")
for result in results:
    print(f"产品ID: {result['entity']['product_id']}, 标签: {result['entity']['metadata']['tags']}")

操作符说明:json_contains 适用于精确匹配单个值,json_contains_any 适用于多值 "或" 匹配,两者均专门针对 JSON 数组字段设计。

四、JSON 索引

4.1 JSON 索引概述

JSON 索引是针对 JSON 字段中特定路径的键创建的索引,用于加速等价查询(如 ==)和范围查询(如 >=、<=)。它不直接索引整个 JSON 对象,而是聚焦于用户指定的关键路径,适用于以下场景:

  • JSON 数据结构固定,核心查询键明确(如频繁按 category 或 price 查询)。
  • 需要对嵌套键(如 supplier.contact.email)进行高效查询。
  • 需对特定数据类型(如字符串、数值)进行精确匹配或范围筛选。
  • 希望在保持 JSON 灵活性的同时,获得接近传统列索引的查询性能。

4.2 创建 JSON 索引

python 复制代码
# 1. 准备索引参数:通过 prepare_index_params() 初始化索引配置对象
index_params = client.prepare_index_params()

# 示例1:在 JSON 一级字段 category 上创建索引
index_params.add_index(
    field_name="metadata",  # 索引对应的 JSON 字段名(必须是 Schema 中声明的 JSON 字段)
    index_type="AUTOINDEX",  # 索引类型(Milvus 自动选择最优索引算法)
    index_name="category_index",  # 索引名称(自定义,需唯一)
    params={
        "json_path": 'metadata["category"]',  # 索引的 JSON 路径(一级字段)
        "json_cast_type": "varchar"  # 索引数据类型(category 是字符串,对应 varchar)
    }
)

# 示例2:在嵌套字段 supplier.contact.email 上创建索引
index_params.add_index(
    field_name="metadata",
    index_type="AUTOINDEX",
    index_name="email_index",
    params={
        "json_path": 'metadata["supplier"]["contact"]["email"]',  # 三级嵌套路径
        "json_cast_type": "varchar"  # email 是字符串类型
    }
)

# 示例3:索引时转换数据类型(字符串转数值)
index_params.add_index(
    field_name="metadata",
    index_type="AUTOINDEX",
    index_name="string_to_double_index",
    params={
        "json_path": 'metadata["string_price"]',  # 原始字段是字符串类型("99.99")
        "json_cast_type": "double",  # 目标索引类型(双精度数值)
        "json_cast_function": "STRING_TO_DOUBLE"  # 类型转换函数(字符串转双精度)
    }
)

# 示例4:索引整个 JSON 对象
index_params.add_index(
    field_name="metadata",
    index_type="AUTOINDEX",
    index_name="metadata_full_index",
    params={
        "json_path": "metadata",  # 路径直接指定整个 JSON 字段
        "json_cast_type": "JSON"  # 索引类型为 JSON(适用于整体匹配场景)
    }
)

# 2. 应用索引配置:将索引创建到目标集合
client.create_index(
    collection_name="product_catalog",
    index_params=index_params
)

print("JSON 索引创建成功!")

关键说明:

  • 索引路径必须是 JSON 字段中实际存在的键(可嵌套),否则索引创建无效。
  • json_cast_type 需与目标键的数据类型匹配(如字符串用 varchar,数值用 double)。
  • 类型转换函数仅支持 Milvus 内置函数(如 STRING_TO_DOUBLE),需确保转换后的数据格式合法(如不能将非数字字符串转为 double)。

4.3 支持的数据类型

|-------------------------------|--------------------|----------------------------|--------------------------|
| 数据类型 | 描述 | 示例 JSON 值 | 适用场景 |
| BOOL / bool | 布尔值类型索引 | true, false | 筛选布尔型属性(如库存状态 in_stock ) |
| DOUBLE / double | 数值类型索引(含整数、浮点数) | 42, 99.99 | 价格、数量等数值的等价 / 范围查询 |
| VARCHAR / varchar | 字符串类型索引 | "electronics", "BrandA" | 类别、品牌、名称等字符串的精确匹配 |
| ARRAY_BOOL / array_bool | 布尔值数组索引 | [true, false, true] | 布尔数组的包含匹配(如多状态标记) |
| ARRAY_DOUBLE / array_double | 数值数组索引 | [1.2, 3.14, 42] | 数值数组的包含 / 范围匹配(如多规格尺寸) |
| ARRAY_VARCHAR / array_varchar | 字符串数组索引 | ["tag1", "tag2", "tag3"] | 标签、关键词等数组的包含匹配 |
| JSON / json | 整个 JSON 对象 / 子对象索引 | 任意 JSON 对象 | 整体 JSON 结构的匹配查询(较少用) |

五、JSON 切碎 (JSON Shredding)

5.1 JSON 切碎概述

JSON 切碎是 Milvus 提供的 JSON 存储优化技术,核心是将传统的行式存储的 JSON 数据,转换为优化的列式存储。简单来说,就是把 JSON 中的每个键(包括嵌套键)拆分为独立的 "虚拟列",按列存储数据。这样做的优势是:

  • 查询时仅需扫描目标键对应的列,无需加载整个 JSON 对象,提升查询速度。
  • 保持 JSON 数据模型的灵活性(仍可动态扩展字段),同时获得列式存储的性能优势。
  • 适用于 JSON 键较多、查询频繁且分散的场景(如多条件组合查询)。

5.2 启用 JSON 切碎

JSON 切碎需通过修改 Milvus 配置文件 milvus.yaml 启用,配置步骤如下:

  1. 找到 Milvus 安装目录下的 conf/milvus.yaml 文件(默认路径)。
  2. 找到 common 配置段,添加或修改以下参数:
python 复制代码
common:
  enabledJSONKeyStats: true  # 核心开关:启用 JSON 键统计构建和加载流程(必须设为 true)
  1. 保存配置文件,重启 Milvus 服务使配置生效。

5.3 配置参数

|--------------------------------------------|------------------------------|-------|------------------------------------------------------|
| 参数名称 | 说明 | 默认值 | 调整建议 |
| common.enabledJSONKeyStats | 控制是否启用 JSON 切碎功能 | false | 必须设为 true 才能激活切碎,否则后续配置无效 |
| common.usingJsonStatsForQuery | 控制查询时是否使用切碎后的数据加速 | true | 正常场景保持 true;若查询失败,可设为 false 恢复原始查询路径 |
| queryNode.mmap.jsonStats | 决定是否使用内存映射(mmap)加载切碎数据 | true | mmap 可减少内存占用,提升加载速度,建议保持默认 |
| dataCoord.jsonStatsMaxShreddingColumns | 切碎后允许的最大虚拟列数量(即 JSON 键的最大数量) | 1024 | 若 JSON 包含数千个频繁使用的键,需增大该值(如 2048),避免部分键无法切碎 |
| dataCoord.jsonStatsShreddingRatioThreshold | JSON 键被切碎的最小出现率(比例) | 0.3 | 出现率 = 包含该键的记录数 / 总记录数;降低阈值(如 0.1)可切碎更多低频率键,提升小众键查询效率 |

5.3 完整配置示例

python 复制代码
common:
  enabledJSONKeyStats: true  # 启用 JSON 切碎
  usingJsonStatsForQuery: true  # 使用切碎数据加速查询
dataCoord:
  jsonStatsMaxShreddingColumns: 1024  # 最大切碎列数(根据实际场景调整)
  jsonStatsShreddingRatioThreshold: 0.3  # 最小出现率(30%)
queryNode:
  mmap:
    jsonStats: true  # 启用 mmap 加载切碎数据

六、查询优化技术比较

|----------|----------------------------|--------------|-------------------------------|----------------------------|
| 技术 | 最适合场景 | 数组加速 | 优势 | 局限性 |
| JSON 索引 | 少数频繁查询的键、特定数组键查询 | 是(仅在索引的数组键上) | 针对性强,查询速度快,配置灵活 | 需预先选择索引键,Schema 变化后需重新维护索引 |
| JSON 切碎 | 复杂 JSON 文档、多个分散查询键、多条件组合查询 | 否(不加速数组内的值) | 无需手动配置索引,自动适配多键查询,保持 JSON 灵活性 | 额外占用少量存储,数组查询无加速效果 |
| NGRAM 索引 | 通配符搜索、文本字段子串匹配(如模糊查询) | 不适用 | 支持灵活的文本模糊查询(如 %elect% ) | 不适用于数字、布尔值,无法进行范围筛选 |

优化组合建议

  • 常规场景:JSON 切碎 + 高频键 JSON 索引,兼顾多键查询灵活性和高频查询效率。
  • 文本搜索场景:JSON 切碎 + NGRAM 索引,满足结构化查询和文本模糊查询需求。
  • 数组查询场景:JSON 索引(针对数组键),提升数组包含匹配的速度。

七、应用场景示例

7.1 电商产品搜索(多条件组合查询)

python 复制代码
def search_products_by_filters(
    client, 
    query_vector, 
    category=None, 
    brand=None, 
    min_price=None, 
    max_price=None, 
    tags=None
):
    """
    根据多种条件搜索电商产品(支持类别、品牌、价格范围、标签筛选)
    参数说明:
    - client: MilvusClient 实例(已连接服务器)
    - query_vector: 查询向量(维度需与集合向量字段一致)
    - category: 产品类别(可选,如 "electronics")
    - brand: 品牌(可选,如 "BrandA")
    - min_price: 最低价格(可选)
    - max_price: 最高价格(可选)
    - tags: 标签列表(可选,如 ["clearance", "new"])
    返回:满足条件的搜索结果列表
    """
    filters = []  # 存储所有过滤条件(最终拼接为逻辑表达式)
    
    # 1. 类别筛选(字符串精确匹配)
    if category:
        filters.append(f'metadata["category"] == "{category}"')
    
    # 2. 品牌筛选(字符串精确匹配)
    if brand:
        filters.append(f'metadata["brand"] == "{brand}"')
    
    # 3. 价格范围筛选(数值比较)
    if min_price is not None and max_price is not None:
        # 同时指定最低和最高价格:价格 >= min_price 且 <= max_price
        filters.append(f'metadata["price"] >= {min_price} && metadata["price"] <= {max_price}')
    elif min_price is not None:
        # 仅指定最低价格:价格 >= min_price
        filters.append(f'metadata["price"] >= {min_price}')
    elif max_price is not None:
        # 仅指定最高价格:价格 <= max_price
        filters.append(f'metadata["price"] <= {max_price}')
    
    # 4. 标签筛选(数组包含任意一个标签)
    if tags:
        # 生成每个标签的包含条件(json_contains),用 || 连接(逻辑或)
        tag_filters = [f'json_contains(metadata["tags"], "{tag}")' for tag in tags]
        filters.append(f'({" || ".join(tag_filters)})')
    
    # 拼接所有过滤条件(用 && 连接,逻辑与)
    filter_expr = " && ".join(filters) if filters else ""
    
    # 执行搜索
    results = client.search(
        collection_name="product_catalog",  # 目标集合
        data=[query_vector],  # 查询向量
        limit=10,  # 返回最多 10 条结果
        filter=filter_expr if filter_expr else None,  # 过滤条件(无条件时设为 None)
        output_fields=["product_id", "metadata"]  # 返回字段
    )
    
    return results

# 使用示例:搜索电子类、价格 50-200 元、包含 "clearance" 或 "new" 标签的产品
if __name__ == "__main__":
    # 1. 重新连接 Milvus 服务器(若之前断开连接)
    client = MilvusClient(uri="http://localhost:19530")
    
    # 2. 定义查询向量(示例向量,实际应使用产品特征向量)
    query_vector = [0.15, 0.25, 0.35, 0.45, 0.55]
    
    # 3. 调用函数执行搜索
    results = search_products_by_filters(
        client=client,
        query_vector=query_vector,
        category="electronics",  # 类别:电子类
        min_price=50,  # 最低价格 50 元
        max_price=200,  # 最高价格 200 元
        tags=["clearance", "new"]  # 标签:清仓或新品
    )
    
    # 4. 输出结果
    print("筛选结果:")
    for result in results:
        product = result['entity']
        print(f"ID: {product['product_id']}, 价格: {product['metadata']['price']}, 标签: {product['metadata']['tags']}")

7.2 内容管理系统(文档搜索)

python 复制代码
def search_documents(
    client, 
    query_vector, 
    document_type=None, 
    author=None, 
    publish_date=None, 
    keywords=None
):
    """
    在内容管理系统中搜索文档(支持文档类型、作者、发布日期、关键词筛选)
    参数说明:
    - client: MilvusClient 实例
    - query_vector: 文档特征向量
    - document_type: 文档类型(可选,如 "article")
    - author: 作者(可选)
    - publish_date: 发布日期(可选,如 "2024-01-01",支持 >= 筛选)
    - keywords: 关键词列表(可选,如 ["AI", "Milvus"])
    返回:满足条件的文档列表
    """
    filters = []
    
    # 1. 文档类型筛选
    if document_type:
        filters.append(f'metadata["document_type"] == "{document_type}"')
    
    # 2. 作者筛选
    if author:
        filters.append(f'metadata["author"] == "{author}"')
    
    # 3. 发布日期筛选(日期字符串比较,需确保存储格式一致)
    if publish_date:
        filters.append(f'metadata["publish_date"] >= "{publish_date}"')
    
    # 4. 关键词筛选(数组包含匹配)
    if keywords:
        keyword_filters = [f'json_contains(metadata["keywords"], "{keyword}")' for keyword in keywords]
        filters.append(f'({" || ".join(keyword_filters)})')
    
    # 拼接过滤条件
    filter_expr = " && ".join(filters) if filters else ""
    
    # 执行搜索
    results = client.search(
        collection_name="document_store",  # 文档集合名(需提前创建)
        data=[query_vector],
        limit=10,
        filter=filter_expr if filter_expr else None,
        output_fields=["doc_id", "metadata", "content_summary"]  # 返回文档ID、元数据、内容摘要
    )
    
    return results

# 调用示例(搜索 2024 年之后发布的 AI 相关文章)
query_vector = [0.3, 0.4, 0.5, 0.6, 0.7]  # 文档特征向量
results = search_documents(
    client=client,
    query_vector=query_vector,
    document_type="article",
    publish_date="2024-01-01",
    keywords=["AI", "Milvus"]
)

# 输出结果
for result in results:
    doc = result['entity']
    print(f"文档ID: {doc['doc_id']}, 作者: {doc['metadata']['author']}, 发布日期: {doc['metadata']['publish_date']}")

八、注意事项和最佳实践

8.1 命名约定(严格遵循)

  • JSON 键仅支持字母(a-z、A-Z)、数字(0-9)和下划线(_),且不能以数字开头。
  • 禁止使用特殊字符(如 !、@、#)、空格或点(.)、斜杠(/)等。
  • 错误示例:"product.price"(含点)、"product name"(含空格)、"123_product"(以数字开头)。
  • 影响:不兼容的键会导致过滤表达式解析失败,或无法创建索引。

8.2 字符串处理(格式规范)

  • 有效字符串:使用双引号包裹,特殊字符需转义,如 "a\"b"(含双引号)、"a\\b"(含反斜杠)、"a'b"(含单引号)。
  • 无效字符串:使用单引号包裹,如 'a"b'、'a\'b'(Milvus JSON 仅支持双引号字符串)。
  • 建议:存储字符串时统一使用双引号,特殊字符按 JSON 规范转义,避免解析错误。

8.3 限制和约束(重点提醒)

  • 大小限制:每个 JSON 字段的最大容量为 65,536 字节(约 64KB),超过会导致插入失败。
  • 默认值:JSON 字段不支持设置默认值(如 default: {}),需在插入时显式指定值(或设为 None,需开启 nullable=True)。
  • 空值支持:仅当字段定义时设置 nullable=True,才能插入 null 或 None(如 metadata: None)。
  • 数据类型:同一键在不同记录中建议保持数据类型一致(如 price 始终存数值,不混用字符串),否则索引失效或查询结果异常。

8.4 性能优化建议(实操性增强)

  1. 索引策略选择
  • 高频单键查询:为该键创建 JSON 索引(如 category、price)。
  • 多键分散查询:启用 JSON 切碎,无需手动创建多个索引。
  • 文本模糊查询:搭配 NGRAM 索引(需单独创建)。
  1. 数据类型一致性
  • 确保同一 JSON 键的数据类型统一(如 tags 始终是字符串数组)。
  • 若存在格式不一致数据(如部分 price 存字符串),使用 json_cast_function 转换类型后再建索引。
  1. 查询优化技巧
  • 为常用查询路径创建索引(如嵌套的 supplier.country)。
  • 合理使用过滤表达式,优先筛选掉大量无关数据(如先按 category 筛选,再进行向量搜索)。
  • 避免查询时返回不必要的字段,通过 output_fields 指定所需字段,减少数据传输量。
  • 结合向量搜索的 limit 参数,控制返回结果数量,提升响应速度。

8.5 故障排除

8.5.1 验证 JSON 切碎是否生效
  1. 使用 Birdwatcher 工具(Milvus 内置诊断工具)连接 Milvus 服务。
  2. 执行命令查看段状态:show segment --format table。
  3. 查看 JSONStats 列:若值为 true,说明该段已启用切碎;若为 false,则未生效。
  4. 未生效排查:检查 enabledJSONKeyStats 是否设为 true,并重启 Milvus 服务。
8.5.2 查询失败恢复步骤
  1. 若使用切碎后查询失败,先设置 common.usingJsonStatsForQuery=false,恢复原始查询路径。
  2. 重启 Milvus 服务,重新执行查询,观察是否恢复正常。
  3. 若仍失败,禁用 JSON 切碎:设置 common.enabledJSONKeyStats=false,重启服务。
  4. 清除残留任务:使用 Birdwatcher 执行 remove stats-task (task_id 可通过 show stats-task 查看)。

九、总结

Milvus 的 JSON 字段兼具结构化数据的规范性和半结构化数据的灵活性,通过 JSON 索引、JSON 切碎等优化技术,可在复杂场景下实现高效查询。核心要点如下:

  • 场景适配:根据数据结构是否固定,选择 JSON 字段(固定结构)或动态字段(灵活结构)。
  • 优化组合:按查询模式选择索引策略(高频键用 JSON 索引,多键查询用切碎)。
  • 规范使用:遵循命名约定和数据类型一致性要求,避免解析和索引问题。
  • 动态调优:通过监控工具(如 Birdwatcher)观察性能,调整索引和切碎配置。
    通过合理运用 JSON 字段的功能,可轻松应对产品目录、内容管理、物联网数据等多类复杂数据存储和查询需求,同时保持系统的灵活性和高性能。
相关推荐
长桥夜波3 小时前
机器学习日报07
人工智能·机器学习
长桥夜波3 小时前
机器学习日报11
人工智能·机器学习
一个处女座的程序猿6 小时前
LLMs之SLMs:《Small Language Models are the Future of Agentic AI》的翻译与解读
人工智能·自然语言处理·小语言模型·slms
ManageEngineITSM6 小时前
技术的秩序:IT资产与配置管理的现代重构
大数据·运维·数据库·重构·工单系统
自由随风飘6 小时前
python 题目练习1~5
开发语言·python
fl1768318 小时前
基于python的天气预报系统设计和可视化数据分析源码+报告
开发语言·python·数据分析
档案宝档案管理8 小时前
档案宝:企业合同档案管理的“安全保险箱”与“效率加速器”
大数据·数据库·人工智能·安全·档案·档案管理
wangjialelele8 小时前
mysql库操作二
数据库·mysql