跳到主要内容
版本:v2.5.x

JSON 字段

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

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

限制

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

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

  • 默认值:JSON 字段不支持默认值。不过,您可以将nullable 属性设置为True ,以允许空值。有关详情,请参阅Nullable & Default

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

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

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

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

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

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

添加 JSON 字段

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

# Import necessary libraries
from pymilvus import MilvusClient, DataType

# Define server address
SERVER_ADDR = "http://localhost:19530"

# Create a MilvusClient instance
client = MilvusClient(uri=SERVER_ADDR)

# Define the collection schema
schema = client.create_schema(
auto_id=False,
enable_dynamic_fields=True,
)

# Add a JSON field that supports null values
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)
说明
  • 如果将来需要插入其他未定义的字段,请设置enable_dynamic_fields=True

  • 使用nullable=True 允许 JSON 对象缺失或为空。

设置索引参数

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

  • 必须为向量字段建立索引(以高效运行相似性搜索)。

  • JSON 字段可选(加快特定 JSON 路径上的标量过滤器)。

为 JSON 字段建立索引 Compatible with Milvus 2.5.10+

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

在本例中,我们将针对 JSON 字段metadata 中的不同路径创建两个索引:

index_params = client.prepare_index_params()

# Example 1: Index the 'category' key inside 'product_info' as a string
index_params.add_index(
field_name="metadata", # JSON field name to index
index_type="INVERTED", # Index type. Set to INVERTED
index_name="json_index_1", # Index name
params={
"json_path": "metadata[\"product_info\"][\"category\"]", # Path in JSON field to index
"json_cast_type": "varchar" # Data type that the extracted JSON values will be cast to
}
)

# Example 2: Index 'price' as a numeric type (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 将所有数值字段索引为双倍。如果数值超过 2532^{53} 253,就会失去精度,对这些超出范围的数值进行的查询可能无法完全匹配。
  • 数据完整性

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

索引一个向量字段

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

# Set index params

# Index `embedding` with AUTOINDEX and specify similarity metric type
index_params.add_index(
field_name="embedding",
index_name="vector_index",
index_type="AUTOINDEX", # Use automatic indexing to simplify complex index settings
metric_type="COSINE" # Specify similarity metric type, options include L2, COSINE, or IP
)

创建 Collection

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

client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)

插入数据

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

# Sample data
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, # Entire JSON object is null
"pk": 2,
"embedding": [0.56, 0.78, 0.90]
},
{
# JSON field is completely missing
"pk": 3,
"embedding": [0.91, 0.18, 0.23]
},
{
# Some sub-keys are null
"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
)

使用过滤表达式查询

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

说明

对于允许空值的 JSON 字段,如果整个 JSON 对象缺失或设置为None ,则该字段将被视为空值。有关详细信息,请参阅带空值的 JSON 字段

检索metadata 不为空值的实体:

# Query to filter out records with null metadata

filter = 'metadata is not null'

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

# Expected result:
# Rows with pk=1 and pk=4 have valid, non-null metadata.
# Rows with pk=2 (metadata=None) and pk=3 (no metadata key) are excluded.

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" 的实体:

filter = 'metadata["product_info"]["category"] == "electronics"'

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

# Expected result:
# - Only pk=1 has "category": "electronics".
# - pk=4 has "category": None, so it doesn't match.
# - pk=2 and pk=3 have no valid metadata.

print(res)

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

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

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

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
)

# Expected result:
# - Only pk=1 has "brand": "BrandA" in metadata["product_info"].
# - pk=4 has "brand": "BrandB".
# - pk=2 and pk=3 have no valid metadata.
# Hence, only pk=1 matches the filter.

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 ,这可以进一步增强查询功能。更多详情,请参阅JSON 操作符