Milvus(10):JSON 字段、数组字段
1 JSON 字段
JSON JSON字段是一种标量字段,它以键值对的形式与向量嵌入一起存储附加信息。下面是一个以 JSON 格式存储数据的示例:
{"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
:
# 导入必要的库
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
中的不同路径创建两个索引:
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"}
)
参数 | 说明 | 示例值 |
---|---|---|
| Schema 中 JSON 字段的名称。 |
|
| 要创建的索引类型;目前仅 |
|
| (可选)自定义索引名称。如果在同一 JSON 字段上创建多个索引,请指定不同的名称。 |
|
| 指定要索引的 JSON 路径。可以针对嵌套键、数组位置或两者(如 |
|
| 在建立索引时,Milvus 将把提取的 JSON 值转换成的数据类型。有效值
|
|
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"
存储的是字符串,而其他行存储的是数字),某些行将不会被索引。
- Milvus 不会解析或转换超出你指定铸造的 JSON 键。如果源数据不一致(例如,某些行的键
1.3.2 索引一个向量字段
下面的示例使用AUTOINDEX
索引类型为向量字段embedding
创建了一个索引。使用这种类型,Milvus 会根据数据类型自动选择最合适的索引。你也可以为每个字段自定义索引类型和参数。
# 设置索引参数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。
client.create_collection(collection_name="my_collection",schema=schema,index_params=index_params
)
1.5 插入数据
创建 Collections 后,插入与 Schema 匹配的实体。
# 样本数据
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
不为空值的实体:
# 过滤掉元数据为空的记录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"
的实体:
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 使用过滤表达式进行向量搜索
除了基本的标量字段筛选外,您还可以将向量相似性搜索与标量字段筛选结合起来。例如,下面的代码展示了如何在向量搜索中添加标量字段过滤器:
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_CONTAINS
、JSON_CONTAINS_ALL
和JSON_CONTAINS_ANY
,这可以进一步增强查询功能。
2 数组字段
ARRAY 字段存储相同数据类型元素的有序集合。下面举例说明 ARRAY 字段如何存储数据:
{"tags": ["pop", "rock", "classic"],"ratings": [5, 4, 3]
}
2.1 限制
-
默认值:ARRAY 字段不支持默认值。但是,可以将
nullable
属性设置为True
,以允许空值。 -
数据类型:数组字段中的所有元素必须具有相同的数据类型,由
element_type
指定。如果将element_type
设置为VARCHAR
,则还应为数组元素设置max_length
。 -
数组容量:数组字段中元素的数量必须小于或等于创建数组时定义的最大容量,具体由
max_capacity
指定。该值应为1至4096 范围内的整数。 -
字符串处理:数组字段中的字符串值按原样存储,不进行语义转义或转换。例如,
'a"b'
、"a'b"
、'a\'b'
和"a\"b"
按输入值存储,而'a'b'
和"a"b"
则被视为无效值。
2.2 添加 ARRAY 字段
要使用 ARRAY 字段,Milvus 需要在创建 Collections Schema 时定义相关字段类型。这一过程包括
- 将
datatype
设置为支持的数组数据类型ARRAY
。 - 使用
element_type
参数指定数组中元素的数据类型。这可以是 Milvus 支持的任何标量数据类型,如VARCHAR
或INT64
。同一数组中的所有元素必须具有相同的数据类型。 - 使用
max_capacity
参数定义数组的最大容量,即数组可包含的最大元素数。
下面介绍如何定义包含 ARRAY 字段的 Collections Schema:
# 导入必要的库
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)
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 会根据数据类型自动选择最合适的索引。您还可以自定义每个字段的索引类型和参数。
# 设置索引参数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。
client.create_collection(collection_name="my_collection",schema=schema,index_params=index_params
)
2.5 插入数据
创建 Collections 后,就可以插入包含 ARRAY 字段的数据。
# 样本数据
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
不为空的实体:
# 排除‘ 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 的实体:
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 使用过滤表达式进行向量搜索
除了基本的标量字段筛选外,您还可以将向量相似性搜索与标量字段筛选结合起来。例如,下面的代码展示了如何在向量搜索中添加标量字段过滤器:
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_ANY
和ARRAY_LENGTH
,以进一步增强查询功能。