1 动态字段
Collections 的 Schema 中定义的所有字段都必须包含在要插入的实体中。如果希望某些字段是可选的,可以考虑启用动态字段。
1.1 概述
在 Milvus 中,可以通过设置 Collections 中每个字段的名称和数据类型来创建 Collections Schema。向 Schema 中添加字段时,请确保该字段包含在要插入的实体中。如果希望某些字段是可选的,启用动态字段是一种选择。
动态字段是一个名为$meta 的保留字段,属于 JavaScript Object Notation(JSON)类型。实体中任何未在 Schema 中定义的字段都将以键值对的形式存储在这个保留的 JSON 字段中。对于启用了动态字段的 Collections,可以使用动态字段中的键进行标量过滤,就像使用模式中明确定义的字段一样。
1.2 启用动态字段
使用 "即时创建集合"中描述的方法创建的集合默认已启用动态字段。也可以在创建具有自定义设置的 Collections 时手动启用动态字段。
from pymilvus import MilvusClientclient= MilvusClient(uri="http://localhost:19530")client.create_collection(collection_name="my_collection",dimension=5,# highlight-next-lineenable_dynamic_field=True
)
1.3 使用动态字段
当在 Collections 中启用动态字段时,所有未在 Schema 中定义的字段及其值都将作为键值对存储在动态字段中。例如,假设您的 Collections Schema 只定义了两个字段,名为id
和vector
,并启用了动态字段。现在,在此 Collections 中插入以下数据集。
[{id: 0, vector: [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592], color: "pink_8682"},{id: 1, vector: [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104], color: "red_7025"},{id: 2, vector: [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592], color: "orange_6781"},{id: 3, vector: [0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345], color: "pink_9298"},{id: 4, vector: [0.4452349528804562, -0.8757026943054742, 0.8220779437047674, 0.46406290649483184, 0.30337481143159106], color: "red_4794"},{id: 5, vector: [0.985825131989184, -0.8144651566660419, 0.6299267002202009, 0.1206906911183383, -0.1446277761879955], color: "yellow_4222"},{id: 6, vector: [0.8371977790571115, -0.015764369584852833, -0.31062937026679327, -0.562666951622192, -0.8984947637863987], color: "red_9392"},{id: 7, vector: [-0.33445148015177995, -0.2567135004164067, 0.8987539745369246, 0.9402995886420709, 0.5378064918413052], color: "grey_8510"},{id: 8, vector: [0.39524717779832685, 0.4000257286739164, -0.5890507376891594, -0.8650502298996872, -0.6140360785406336], color: "white_9381"},{id: 9, vector: [0.5718280481994695, 0.24070317428066512, -0.3737913482606834, -0.06726932177492717, -0.6980531615588608], color: "purple_4976"}
]
上面的数据集包含 10 个实体,每个实体都包括字段id
,vector
, 和color
。这里,Schema 中没有定义color
字段。由于 Collections 启用了动态字段,因此字段color
将作为键值对存储在动态字段中。
1.3.1 插入数据
以下代码演示了如何将此数据集插入 Collections。
data=[{"id": 0, "vector": [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592], "color": "pink_8682"},{"id": 1, "vector": [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104], "color": "red_7025"},{"id": 2, "vector": [0.43742130801983836, -0.5597502546264526, 0.6457887650909682, 0.7894058910881185, 0.20785793220625592], "color": "orange_6781"},{"id": 3, "vector": [0.3172005263489739, 0.9719044792798428, -0.36981146090600725, -0.4860894583077995, 0.95791889146345], "color": "pink_9298"},{"id": 4, "vector": [0.4452349528804562, -0.8757026943054742, 0.8220779437047674, 0.46406290649483184, 0.30337481143159106], "color": "red_4794"},{"id": 5, "vector": [0.985825131989184, -0.8144651566660419, 0.6299267002202009, 0.1206906911183383, -0.1446277761879955], "color": "yellow_4222"},{"id": 6, "vector": [0.8371977790571115, -0.015764369584852833, -0.31062937026679327, -0.562666951622192, -0.8984947637863987], "color": "red_9392"},{"id": 7, "vector": [-0.33445148015177995, -0.2567135004164067, 0.8987539745369246, 0.9402995886420709, 0.5378064918413052], "color": "grey_8510"},{"id": 8, "vector": [0.39524717779832685, 0.4000257286739164, -0.5890507376891594, -0.8650502298996872, -0.6140360785406336], "color": "white_9381"},{"id": 9, "vector": [0.5718280481994695, 0.24070317428066512, -0.3737913482606834, -0.06726932177492717, -0.6980531615588608], "color": "purple_4976"}
]res = client.insert(collection_name="my_collection",data=data
)print(res)# Output
# {'insert_count': 10, 'ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
1.3.2 在动态字段中索引标量字段
启用动态字段后,任何未定义的标量字段都会以 JSON 格式存储为键值对。Milvus 支持在这种未定义的标量字段上创建索引,有效的方法是建立一个 JSON 路径索引。具体操作如下
-
选择要索引的 Dynamic Field 关键字。例如,上例中的
"color"
。 -
为在该关键字中找到的值决定一个铸模类型。Milvus 将解析动态字段,提取指定键下的值,并将它们转换为你配置的类型。
-
支持的
json_cast_type
值有bool
(或BOOL
)、double
(或DOUBLE
)和varchar
(或VARCHAR
)。 -
如果解析或转换失败(例如,试图将字符串解析为 double),索引将跳过这些行。
-
-
将该键的JSON 路径指定为
json_path
。由于动态字段是以 JSON 格式存储的,因此可以指定类似"color"
这样的路径,如果有嵌套结构,则可以指定更深的路径(例如my_json["field"]["subfield"]
)。 -
创建 INVERTED 索引。目前,JSON 路径索引只支持
INVERTED
类型。
下面是如何在"color"
字段上创建索引的示例:
# 准备索引参数
index_params = client.prepare_index_params()index_params.add_index(field_name="color", # 在查询中看到的“列”的名称(动态键)。index_type="INVERTED", # 目前,JSON字段的索引只支持“INVERTED”。index_name="color_index", # 为这个索引指定一个名称。params={"json_path": "color", # 要索引的键的JSON路径。"json_cast_type": "varchar" # Milvus将将提取的值强制转换到的类型。}
)# Create the index
client.create_index(collection_name="my_collection",index_params=index_params
)
1.3.3 使用动态字段进行查询和搜索
Milvus 支持在查询和搜索过程中使用过滤表达式,允许您指定在结果中包含哪些字段。下面的示例演示了如何通过动态字段使用color
字段执行查询和搜索,该字段在 Schema 中没有定义。
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]res = client.search(collection_name="my_collection",data=[query_vector],limit=5,# highlight-startfilter='color like "red%"',output_fields=["color"]# highlight-end
)print(res)# Output
# data: ["[{'id': 1, 'distance': 0.6290165185928345, 'entity': {'color': 'red_7025'}}, {'id': 4, 'distance': 0.5975797176361084, 'entity': {'color': 'red_4794'}}, {'id': 6, 'distance': -0.24996188282966614, 'entity': {'color': 'red_9392'}}]"]
在上面代码示例中使用的过滤表达式color like "red%" and likes > 50
中,条件指定color
字段的值必须以"红色 "开头。
[{"id": 1, "distance": 0.6290165,"entity": {"color": "red_7025"}},{"id": 4, "distance": 0.5975797,"entity": {"color": "red_4794"}},{"id": 6, "distance": -0.24996188,"entity": {"color": "red_9392"}},
]
2 可归零和默认值
Milvus 允许你为标量字段(主字段除外)设置nullable
属性和默认值。对于标记为nullable=True
的字段,您可以在插入数据时跳过该字段,或直接将其设置为空值,系统会将其视为空值而不会导致错误。当字段具有默认值时,如果在插入过程中没有为该字段指定数据,系统将自动应用该值。
默认值和可归零属性允许处理带有空值的数据集并保留默认值设置,从而简化了从其他数据库系统到 Milvus 的数据迁移。在创建 Collections 时,也可以启用可归零属性或为可能存在不确定值的字段设置默认值。
2.1 限制
- 只有标量字段(主字段除外)支持默认值和 nullable 属性。
- JSON 和数组字段不支持默认值。
- 默认值或 nullable 属性只能在创建 Collections 时配置,之后不能修改。
- 启用了可归零属性的标量字段不能在分组搜索中用作
group_by_field
。 - 标记为可归零的字段不能用作分区键。
- 在启用了可归零属性的标量字段上创建索引时,索引将排除空值。
- JSON 和 ARRAY 字段:当使用
IS NULL
或IS NOT NULL
操作符对 JSON 或 ARRAY 字段进行筛选时,这些操作符在列级别工作,这表明它们只评估整个 JSON 对象或数组是否为空。例如,如果 JSON 对象中的某个键为空,IS NULL
过滤器将无法识别该键。
2.2 Nullable 属性
通过nullable
属性,可以在 Collections 中存储空值,从而在处理未知数据时提供灵活性。
2.2.1 设置 nullable 属性
创建 Collections 时,使用nullable=True
定义可归零字段(默认为False
)。下面的示例创建了一个名为my_collection
的 Collection,并将age
字段设置为可归零:
from pymilvus import MilvusClient, DataTypeclient = MilvusClient(uri='http://localhost:19530')# 定义集合模式
schema = client.create_schema(auto_id=False,enable_dynamic_schema=True,
)schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=5)
schema.add_field(field_name="age", datatype=DataType.INT64, nullable=True) # 可空字段# 设置索引参数
index_params = client.prepare_index_params()
index_params.add_index(field_name="vector", index_type="AUTOINDEX", metric_type="L2")# 创建集合
client.create_collection(collection_name="my_collection", schema=schema, index_params=index_params)
2.2.2 插入实体
在可空字段中插入数据时,插入空值或直接省略该字段:
data = [{"id": 1, "vector": [0.1, 0.2, 0.3, 0.4, 0.5], "age": 30},{"id": 2, "vector": [0.2, 0.3, 0.4, 0.5, 0.6], "age": None},{"id": 3, "vector": [0.3, 0.4, 0.5, 0.6, 0.7]}
]client.insert(collection_name="my_collection", data=data)
2.2.3 使用空值进行搜索和查询
使用search
方法时,如果字段包含null
值,则搜索结果将以空值返回该字段:
res = client.search(collection_name="my_collection",data=[[0.1, 0.2, 0.4, 0.3, 0.128]],limit=2,search_params={"params": {"nprobe": 16}},output_fields=["id", "age"]
)print(res)# Output
# data: ["[{'id': 1, 'distance': 0.15838398039340973, 'entity': {'age': 30, 'id': 1}}, {'id': 2, 'distance': 0.28278401494026184, 'entity': {'age': None, 'id': 2}}]"]
当您使用query
方法进行标量过滤时,空值的过滤结果都是 false,表示不会选择它们。
# 查看先前插入的数据:
# {"id": 1, "vector": [0.1, 0.2, ..., 0.128], "age": 30}
# {"id": 2, "vector": [0.2, 0.3, ..., 0.129], "age": None}
# {"id": 3, "vector": [0.3, 0.4, ..., 0.130], "age": None} # 省略的age列被视为None
results = client.query(collection_name="my_collection",filter="age >= 0",output_fields=["id", "age"]
)# Example output:
# [
# {"id": 1, "age": 30}
# ]
# Note: ‘ age ’为‘ null ’ (id 2和3)的实体将不会出现在结果中。
要返回具有null
值的实体,可在不使用任何标量过滤条件的情况下进行如下查询,query
方法在不带任何过滤条件的情况下使用时,会检索 Collections 中的所有实体,包括具有空值的实体。要限制返回实体的数量,必须指定limit
参数。
null_results = client.query(collection_name="my_collection",filter="", # 不带任何过滤条件的查询output_fields=["id", "age"],limit=10
)# Example output:
# [{"id": 2, "age": None}, {"id": 3, "age": None}]
2.3 默认值
默认值是分配给标量字段的预设值。如果在插入时没有为有默认值的字段提供值,系统会自动使用默认值。
2.3.1 设置默认值
创建 Collections 时,使用default_value
参数定义字段的默认值。下面的示例显示了如何将age
的默认值设置为18
,将status
的默认值设置为"active"
:
schema = client.create_schema(auto_id=False,enable_dynamic_schema=True,
)schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=5)
schema.add_field(field_name="age", datatype=DataType.INT64, default_value=18)
schema.add_field(field_name="status", datatype=DataType.VARCHAR, default_value="active", max_length=10)index_params = client.prepare_index_params()
index_params.add_index(field_name="vector", index_type="AUTOINDEX", metric_type="L2")client.create_collection(collection_name="my_collection", schema=schema, index_params=index_params)
2.3.2 插入实体
插入数据时,如果省略有默认值的字段或将其值设为空,系统就会使用默认值:
data = [{"id": 1, "vector": [0.1, 0.2, ..., 0.128], "age": 30, "status": "premium"},{"id": 2, "vector": [0.2, 0.3, ..., 0.129]}, # ‘ age ’和‘ status ’使用默认值{"id": 3, "vector": [0.3, 0.4, ..., 0.130], "age": 25, "status": None}, # “status”使用默认值{"id": 4, "vector": [0.4, 0.5, ..., 0.131], "age": None, "status": "inactive"} # ‘ age ’使用默认值
]client.insert(collection_name="my_collection", data=data)
2.3.3 使用默认值进行搜索和查询
在向量搜索和标量过滤过程中,包含默认值的实体与其他实体的处理方式相同。您可以将默认值作为search
和query
操作符的一部分。例如,在search
操作符中,将age
设置为默认值18
的实体将包含在结果中:
res = client.search(collection_name="my_collection",data=[[0.1, 0.2, 0.4, 0.3, 0.5]],search_params={"params": {"nprobe": 16}},filter="age == 18", # “age”字段的默认值为18limit=10,output_fields=["id", "age", "status"]
)print(res)# Output
# data: ["[{'id': 2, 'distance': 0.050000004, 'entity': {'id': 2, 'age': 18, 'status': 'active'}}, {'id': 4, 'distance': 0.45000002, 'entity': {'id': 4, 'age': 18, 'status': 'inactive'}}]"]
在query
操作符中,可以直接通过默认值进行匹配或过滤:
# 查询‘ age ’等于默认值(18)的所有实体
default_age_results = client.query(collection_name="my_collection",filter="age == 18",output_fields=["id", "age", "status"]
)# 查询“status”等于默认值(“active”)的所有实体
default_status_results = client.query(collection_name="my_collection",filter='status == "active"',output_fields=["id", "age", "status"]
)
2.3.4 适用规则
下表总结了可空列和默认值在不同配置组合下的行为。这些规则决定了 Milvus 在尝试插入空值或未提供字段值时如何处理数据。
可归零 | 默认值 | 默认值类型 | 用户输入 | 结果 | 示例 |
---|---|---|---|---|---|
✅ | ✅ | 非空 | 无/空 | 使用默认值 | 字段: 用户输入:空 结果:存储为 |
✅ | ❌ | - | 无/空 | 存储为空 | 字段: 用户输入:空 结果:存储为空 |
❌ | ✅ | 非空 | 无/空 | 使用默认值 | 字段: 用户输入:空 结果:存储为 |
❌ | ❌ | - | 无/空 | 抛出错误 | 字段: 用户输入:空 结果:操作被拒绝,系统提示错误 |
❌ | ✅ | 空 | 无/空 | 抛出错误 | 字段: 用户输入:空 结果:操作符被拒绝,系统提示错误 |