Milvus(10):JSON 字段、数组字段

1 JSON 字段

JSON JSON字段是一种标量字段,它以键值对的形式与向量嵌入一起存储附加信息。下面是一个以 JSON 格式存储数据的示例:

python 复制代码
{
  "metadata": {
    "product_info": {
      "category": "electronics",
      "brand": "BrandA"
    },
    "price": 99.99,
    "in_stock": true,
    "tags": ["summer_sale", "clearance"]
  }
}

1.1 限制

  • 字段大小:JSON 字段的大小限制为 65,536 字节。

  • 嵌套字典:JSON 字段值中的任何嵌套字典都将作为纯字符串存储。

  • 默认值 :JSON 字段不支持默认值。不过,您可以将nullable 属性设置为True ,以允许空值。

  • 类型匹配:如果 JSON 字段的键值是整数或浮点数,则只能(通过表达式过滤器)与另一个相同类型的数字键进行比较。

  • 命名:命名 JSON 键时,建议只使用字母、数字和下划线。使用其他字符可能会在过滤或搜索时造成问题。

  • 字符串处理:Milvus 在 JSON 字段中存储输入的字符串值,不进行语义转换。例如

    • 'a"b',"a'b",'a\\'b', 和"a\\"b" 会按原样存储。

    • 'a'b' 和 被视为无效。"a"b"

  • JSON 索引:为 JSON 字段编制索引时,可以在 JSON 字段中指定一个或多个路径,以加快过滤速度。每增加一条路径都会增加索引开销,因此请仔细规划索引策略。

1.2 添加 JSON 字段

要将此 JSON 字段metadata 添加到 Collections Schema 中,请使用DataType.JSON 。下面的示例定义了一个允许空值的 JSON 字段metadata

python 复制代码
# 导入必要的库
from pymilvus import MilvusClient, DataType

# 定义服务器地址
SERVER_ADDR = "http://localhost:19530"

# 创建一个MilvusClient实例
client = MilvusClient(uri=SERVER_ADDR)

# 定义集合模式
schema = client.create_schema(
    auto_id=False,
    enable_dynamic_fields=True,
)

# 添加一个支持空值的JSON字段
schema.add_field(field_name="metadata", datatype=DataType.JSON, nullable=True)
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)

1.3 设置索引参数

索引可帮助 Milvus 快速过滤或搜索大量数据。在 Milvus 中,索引是

  • 必须 为向量字段建立索引(以高效运行相似性搜索)。
  • JSON 字段可选(加快特定 JSON 路径上的标量过滤器)。

1.3.1 为 JSON 字段建立索引

默认情况下,JSON 字段未编入索引,因此任何过滤查询(如metadata["price"] < 100 )都必须扫描所有行。如果想加速对metadata 字段中特定路径的查询,可以在关心的每条路径上创建一个反向索引 。在本例中,我们将针对 JSON 字段metadata 中的不同路径创建两个索引:

python 复制代码
index_params = client.prepare_index_params()

# 示例1:将'product_info'中的'category'键索引为字符串
index_params.add_index(
    field_name="metadata", # JSON字段名称到索引
    index_type="INVERTED", # 索引类型。设置为倒置
    index_name="json_index_1", # 索引名
    params={
        "json_path": "metadata[\"product_info\"][\"category\"]", # JSON字段到索引的路径
        "json_cast_type": "varchar" # 提取的JSON值将被强制转换为的数据类型
    }
)

# 示例2:索引'price'作为数字类型(double)
index_params.add_index(
    field_name="metadata",
    index_type="INVERTED",
    index_name="json_index_2",
    params={
        "json_path": "metadata[\"price\"]",
        "json_cast_type": "double"
    }
)
参数 说明 示例值
field_name Schema 中 JSON 字段的名称。 "metadata"
index_type 要创建的索引类型;目前仅INVERTED 支持 JSON 路径索引。 "INVERTED"
index_name (可选)自定义索引名称。如果在同一 JSON 字段上创建多个索引,请指定不同的名称。 "json_index_1"
params.json_path 指定要索引的 JSON 路径。可以针对嵌套键、数组位置或两者(如metadata["product_info"]["category"]metadata["tags"][0] )。如果路径缺失或数组元素不存在于某一行,则在索引过程中会跳过该行,不会抛出错误。 "metadata[\"product_info\"][\"category\"]"
params.json_cast_type 在建立索引时,Milvus 将把提取的 JSON 值转换成的数据类型。有效值 * "bool""BOOL" * "double""DOUBLE" * "varchar""VARCHAR" 注意:对于整数值,Milvus 内部使用 double 作为索引。超过 2^53 的大整数将失去精度。如果类型转换失败(由于类型不匹配),不会抛出错误,也不会索引该行的值。 "varchar"

JSON 索引的注意事项

  • 过滤逻辑

    • 如果创建了一个双类型索引json_cast_type="double" ),则只有数字类型的过滤条件才能使用该索引。如果过滤器将双索引与非数值型条件进行比较,Milvus 就会退回到蛮力搜索。

    • 如果创建了 varchar 类型的索引 (json_cast_type="varchar"),则只有字符串类型的过滤条件才能使用该索引。否则,Milvus 将退回蛮力搜索。

    • 布尔索引的行为与 varchar 类型类似。

  • 术语表达式

    • 可以使用json["field"] in [value1, value2, ...] 。但是,索引只对存储在该路径下的标量值有效。如果json["field"] 是一个数组,查询将退回到蛮力方式(尚未支持数组类型索引)。
  • 数值精度

    • 在内部,Milvus 将所有数值字段索引为双倍。如果数值超过 2^{53},就会失去精度,对超出范围的数值进行查询时可能无法精确匹配。
  • 数据完整性

    • Milvus 不会解析或转换超出你指定铸造的 JSON 键。如果源数据不一致(例如,某些行的键"k" 存储的是字符串,而其他行存储的是数字),某些行将不会被索引。

1.3.2 索引一个向量字段

下面的示例使用AUTOINDEX 索引类型为向量字段embedding 创建了一个索引。使用这种类型,Milvus 会根据数据类型自动选择最合适的索引。你也可以为每个字段自定义索引类型和参数。

python 复制代码
# 设置索引参数

index_params = client.prepare_index_params()

# 索引"嵌入"与AUTOINDEX并指定相似度度量类型
index_params.add_index(
    field_name="embedding",
    index_name="vector_index",
    index_type="AUTOINDEX",  # 使用自动索引来简化复杂的索引设置
    metric_type="COSINE"  # 指定相似度量类型,选项包括L2、COSINE或IP
)

1..4 创建 Collections

定义好 Schema 和索引后,创建一个包含字符串字段的 Collection。

python 复制代码
client.create_collection(
    collection_name="my_collection",
    schema=schema,
    index_params=index_params
)

1.5 插入数据

创建 Collections 后,插入与 Schema 匹配的实体。

python 复制代码
# 样本数据
data = [
    {
        "metadata": {
            "product_info": {"category": "electronics", "brand": "BrandA"},
            "price": 99.99,
            "in_stock": True,
            "tags": ["summer_sale"]
        },
        "pk": 1,
        "embedding": [0.12, 0.34, 0.56]
    },
    {
        "metadata": None,  # 整个JSON对象为空
        "pk": 2,
        "embedding": [0.56, 0.78, 0.90]
    },
    {
        # JSON字段完全丢失
        "pk": 3,
        "embedding": [0.91, 0.18, 0.23]
    },
    {
        # 有些子键为空
        "metadata": {
            "product_info": {"category": None, "brand": "BrandB"},
            "price": 59.99,
            "in_stock": None
        },
        "pk": 4,
        "embedding": [0.56, 0.38, 0.21]
    }
]

client.insert(
    collection_name="my_collection",
    data=data
)

1.6 使用过滤表达式查询

插入实体后,使用query 方法检索与指定过滤表达式匹配的实体。对于允许空值的 JSON 字段,如果整个 JSON 对象缺失或设置为None ,则该字段将被视为空值。检索metadata 不为空值的实体:

python 复制代码
# 过滤掉元数据为空的记录

filter = 'metadata is not null'

res = client.query(
    collection_name="my_collection",
    filter=filter,
    output_fields=["metadata", "pk"]
)

# 预期结果
# pk=1和pk=4的行具有有效的非空元数据。
# 排除pk=2(元数据=None)和pk=3(无元数据键)的行。

print(res)

# Output:
# data: [
#     "{'metadata': {'product_info': {'category': 'electronics', 'brand': 'BrandA'}, 'price': 99.99, 'in_stock': True, 'tags': ['summer_sale']}, 'pk': 1}",
#     "{'metadata': {'product_info': {'category': None, 'brand': 'BrandB'}, 'price': 59.99, 'in_stock': None}, 'pk': 4}"
# ]

检索metadata["product_info"]["category"]"electronics" 的实体:

python 复制代码
filter = 'metadata["product_info"]["category"] == "electronics"'

res = client.query(
    collection_name="my_collection",
    filter=filter,
    output_fields=["metadata", "pk"]
)

# 预期结果
# - 只有pk=1有 "category": "electronics".
# - pk=4有"category":None,所以它不匹配。
# - Pk =2和Pk =3没有有效的元数据。

print(res)

# Output:
# data: [
#     "{'pk': 1, 'metadata': {'product_info': {'category': 'electronics', 'brand': 'BrandA'}, 'price': 99.99, 'in_stock': True, 'tags': ['summer_sale']}}"
# ]

1.7 使用过滤表达式进行向量搜索

除了基本的标量字段筛选外,您还可以将向量相似性搜索与标量字段筛选结合起来。例如,下面的代码展示了如何在向量搜索中添加标量字段过滤器:

python 复制代码
filter = 'metadata["product_info"]["brand"] == "BrandA"'

res = client.search(
    collection_name="my_collection",
    data=[[0.3, -0.6, 0.1]],
    limit=5,
    search_params={"params": {"nprobe": 10}},
    output_fields=["metadata"],
    filter=filter
)

# 预期结果:
# - 元数据["product_info"]中只有pk=1有"brand": "BrandA"。
# - pk=4有"brand":"BrandB"。
# - Pk =2和Pk =3没有有效的元数据。
# 因此,只有pk=1匹配过滤器。

print(res)

# Output:
# data: [
#     "[{'id': 1, 'distance': -0.2479381263256073, 'entity': {'metadata': {'product_info': {'category': 'electronics', 'brand': 'BrandA'}, 'price': 99.99, 'in_stock': True, 'tags': ['summer_sale']}}}]"
# ]

此外,Milvus 还支持高级 JSON 过滤操作符,如JSON_CONTAINSJSON_CONTAINS_ALLJSON_CONTAINS_ANY ,这可以进一步增强查询功能。

2 数组字段

ARRAY 字段存储相同数据类型元素的有序集合。下面举例说明 ARRAY 字段如何存储数据:

python 复制代码
{
  "tags": ["pop", "rock", "classic"],
  "ratings": [5, 4, 3]
}

2.1 限制

  • 默认值 :ARRAY 字段不支持默认值。但是,可以将nullable 属性设置为True ,以允许空值。

  • 数据类型 :数组字段中的所有元素必须具有相同的数据类型,由element_type 指定。如果将element_type 设置为VARCHAR ,则还应为数组元素设置max_length

  • 数组容量 :数组字段中元素的数量必须小于或等于创建数组时定义的最大容量,具体由max_capacity 指定。该值应为14096 范围内的整数。

  • 字符串处理 :数组字段中的字符串值按原样存储,不进行语义转义或转换。例如,'a"b'"a'b"'a\'b'"a\"b" 按输入值存储,而'a'b'"a"b" 则被视为无效值。

2.2 添加 ARRAY 字段

要使用 ARRAY 字段,Milvus 需要在创建 Collections Schema 时定义相关字段类型。这一过程包括

  1. datatype 设置为支持的数组数据类型ARRAY
  2. 使用element_type 参数指定数组中元素的数据类型。这可以是 Milvus 支持的任何标量数据类型,如VARCHARINT64 。同一数组中的所有元素必须具有相同的数据类型。
  3. 使用max_capacity 参数定义数组的最大容量,即数组可包含的最大元素数。

下面介绍如何定义包含 ARRAY 字段的 Collections Schema:

python 复制代码
# 导入必要的库
from pymilvus import MilvusClient, DataType

# 定义服务器地址
SERVER_ADDR = "http://localhost:19530"

# 创建一个MilvusClient实例
client = MilvusClient(uri=SERVER_ADDR)

# 定义集合模式
schema = client.create_schema(
    auto_id=False,
    enable_dynamic_fields=True,
)

#  添加' tags '和' ratings ' ARRAY字段,null =True
schema.add_field(field_name="tags", datatype=DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=10, max_length=65535, nullable=True)
schema.add_field(field_name="ratings", datatype=DataType.ARRAY, element_type=DataType.INT64, max_capacity=5, nullable=True)
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)
python 复制代码
export arrayField1='{
    "fieldName": "tags",
    "dataType": "Array",
    "elementDataType": "VarChar",
    "elementTypeParams": {
        "max_capacity": 10,
        "max_length": 65535
    }
}'

export arrayField2='{
    "fieldName": "ratings",
    "dataType": "Array",
    "elementDataType": "Int64",
    "elementTypeParams": {
        "max_capacity": 5
    }
}'

export pkField='{
    "fieldName": "pk",
    "dataType": "Int64",
    "isPrimary": true
}'

export vectorField='{
    "fieldName": "embedding",
    "dataType": "FloatVector",
    "elementTypeParams": {
        "dim": 3
    }
}'

export schema="{
    \"autoID\": false,
    \"fields\": [
        $arrayField1,
        $arrayField2,
        $pkField,
        $vectorField
    ]
}"

2.3 设置索引参数

索引有助于提高搜索和查询性能。在 Milvus 中,对于向量字段必须建立索引,但对于标量字段则是可选的。下面的示例使用AUTOINDEX 索引类型为向量字段embedding 和 ARRAY 字段tags 创建了索引。使用这种类型,Milvus 会根据数据类型自动选择最合适的索引。您还可以自定义每个字段的索引类型和参数。

python 复制代码
# 设置索引参数

index_params = client.prepare_index_params()

# 设置索引参数
index_params.add_index(
    field_name="tags",
    index_type="AUTOINDEX",
    index_name="tags_index"
)

# 索引"嵌入"与AUTOINDEX并指定相似度度量类型
index_params.add_index(
    field_name="embedding",
    index_type="AUTOINDEX",  # 使用自动索引来简化复杂的索引设置
    metric_type="COSINE"  # 指定相似度量类型,选项包括L2、COSINE或IP
)

2.4 创建 Collections

定义好 Schema 和索引后,创建一个包含 ARRAY 字段的 Collection。

python 复制代码
client.create_collection(
    collection_name="my_collection",
    schema=schema,
    index_params=index_params
)

2.5 插入数据

创建 Collections 后,就可以插入包含 ARRAY 字段的数据。

python 复制代码
# 样本数据
data = [
  {
      "tags": ["pop", "rock", "classic"],
      "ratings": [5, 4, 3],
      "pk": 1,
      "embedding": [0.12, 0.34, 0.56]
  },
  {
      "tags": None,  # 整个数组为空
      "ratings": [4, 5],
      "pk": 2,
      "embedding": [0.78, 0.91, 0.23]
  },
  {  # 标签字段完全缺失
      "ratings": [9, 5],
      "pk": 3,
      "embedding": [0.18, 0.11, 0.23]
  }
]

client.insert(
    collection_name="my_collection",
    data=data
)

2.6 使用过滤表达式查询

插入实体后,使用query 方法检索与指定过滤表达式匹配的实体。检索tags 不为空的实体:

python 复制代码
# 排除' tags '不为空的实体的查询

filter = 'tags IS NOT NULL'

res = client.query(
    collection_name="my_collection",
    filter=filter,
    output_fields=["tags", "ratings", "pk"]
)

print(res)

# Example output:
# data: [
#     "{'tags': ['pop', 'rock', 'classic'], 'ratings': [5, 4, 3], 'pk': 1}"
# ]

检索ratings 第一个元素的值大于 4 的实体:

python 复制代码
filter = 'ratings[0] > 4'

res = client.query(
    collection_name="my_collection",
    filter=filter,
    output_fields=["tags", "ratings", "embedding"]
)

print(res)

# Example output:
# data: [
#     "{'tags': ['pop', 'rock', 'classic'], 'ratings': [5, 4, 3], 'embedding': [0.12, 0.34, 0.56], 'pk': 1}",
#     "{'tags': None, 'ratings': [9, 5], 'embedding': [0.18, 0.11, 0.23], 'pk': 3}"
# ]

2.7 使用过滤表达式进行向量搜索

除了基本的标量字段筛选外,您还可以将向量相似性搜索与标量字段筛选结合起来。例如,下面的代码展示了如何在向量搜索中添加标量字段过滤器:

python 复制代码
filter = 'tags[0] == "pop"'

res = client.search(
    collection_name="my_collection",
    data=[[0.3, -0.6, 0.1]],
    limit=5,
    search_params={"params": {"nprobe": 10}},
    output_fields=["tags", "ratings", "embedding"],
    filter=filter
)

print(res)

# Example output:
# data: [
#     "[{'id': 1, 'distance': -0.2479381263256073, 'entity': {'tags': ['pop', 'rock', 'classic'], 'ratings': [5, 4, 3], 'embedding': [0.11999999731779099, 0.3400000035762787, 0.5600000023841858]}}]"
# ]

此外,Milvus 还支持高级数组过滤操作符,如ARRAY_CONTAINS,ARRAY_CONTAINS_ALL,ARRAY_CONTAINS_ANYARRAY_LENGTH ,以进一步增强查询功能。

相关推荐
秋风&萧瑟9 小时前
【QT】QT中http协议和json数据的解析-http获取天气预报
qt·http·json
游王子12 小时前
Milvus(13):自定义分析器、过滤器
milvus
游王子12 小时前
Milvus(12):分析器
milvus
今晚去打老虎21 小时前
快速掌握大语言模型+向量数据库_RAG实现
人工智能·python·语言模型·milvus
小龙在山东1 天前
flask 获取各种请求数据:GET form-data x-www-form-urlencoded JSON headers 上传文件
python·flask·json
数据的流2 天前
WebAPI项目从Newtonsoft.Json迁移到System.Text.Json踩坑备忘
json
不吃肘击3 天前
SpringMVC中自定义消息转换器处理响应和请求时的Json数据序列化的格式
java·spring·json·序列化·反序列化·消息转换器
Dreamfine4 天前
Rust将结构导出到json如何处理小数点问题
rust·json·dolphindb·rust_decimal·serde_json
damonzheng464 天前
milvus编译与使用
milvus