Añadir campos a una colección existenteCompatible with Milvus 2.6.x
Milvus le permite añadir dinámicamente nuevos campos a colecciones existentes, facilitando la evolución de su esquema de datos a medida que cambian las necesidades de su aplicación. Esta guía le muestra cómo añadir campos en diferentes escenarios utilizando ejemplos prácticos.
Consideraciones
Antes de añadir campos a su colección, tenga en cuenta estos puntos importantes:
Puede añadir campos escalares (
INT64,VARCHAR,FLOAT,DOUBLE, etc.). Los campos vectoriales no pueden añadirse a colecciones existentes.Los nuevos campos deben ser anulables (nullable=True) para dar cabida a entidades existentes que no tengan valores para el nuevo campo.
Añadir campos a colecciones cargadas aumenta el uso de memoria.
Hay un límite máximo de campos totales por colección. Para más detalles, consulte Límites de Milvus.
Los nombres de campo deben ser únicos entre los campos estáticos.
No puede añadir un campo
$metapara habilitar la funcionalidad de campo dinámico para colecciones que no se crearon originalmente conenable_dynamic_field=True.
Requisitos previos
Esta guía asume que usted tiene
Una instancia de Milvus en ejecución
Milvus SDK instalado
Una colección existente
Consulte nuestra sección Crear colección para la creación de colecciones y operaciones básicas.
Uso básico
from pymilvus import MilvusClient, DataType
# Connect to your Milvus server
client = MilvusClient(
uri="http://localhost:19530" # Replace with your Milvus server URI
)
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.client.ConnectConfig;
ConnectConfig config = ConnectConfig.builder()
.uri("http://localhost:19530")
.build();
MilvusClientV2 client = new MilvusClientV2(config);
import { MilvusClient } from '@zilliz/milvus2-sdk-node';
const milvusClient = new MilvusClient({
address: 'localhost:19530'
});
// go
# restful
export CLUSTER_ENDPOINT="localhost:19530"
Escenario 1: Añadir rápidamente campos anulables
La forma más sencilla de ampliar una colección es añadiendo campos anulables. Esto es perfecto cuando necesitas añadir rápidamente nuevos atributos a tus datos.
# Add a nullable field to an existing collection
# This operation:
# - Returns almost immediately (non-blocking)
# - Makes the field available for use with minimal delay
# - Sets NULL for all existing entities
client.add_collection_field(
collection_name="product_catalog",
field_name="created_timestamp", # Name of the new field to add
data_type=DataType.INT64, # Data type must be a scalar type
nullable=True # Must be True for added fields
# Allows NULL values for existing entities
)
import io.milvus.v2.service.collection.request.AddCollectionFieldReq;
client.addCollectionField(AddCollectionFieldReq.builder()
.collectionName("product_catalog")
.fieldName("created_timestamp")
.dataType(DataType.Int64)
.isNullable(true)
.build());
await client.addCollectionField({
collection_name: 'product_catalog',
field: {
name: 'created_timestamp',
dataType: 'Int64',
nullable: true
}
});
// go
# restful
curl -X POST "http://localhost:19530/v2/vectordb/collections/fields/add" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer <token>" \
-d '{
"collectionName": "product_catalog",
"schema": {
"fieldName": "created_timestamp",
"dataType": "Int64",
"nullable": true
}
}'
Comportamiento esperado:
Lasentidades existentes tendrán NULL para el nuevo campo
Las nuevas entidades pueden tener valores NULL o reales
Ladisponibilidad del campo se produce casi inmediatamente con un retraso mínimo debido a la sincronización del esquema interno
Se puede consultar inmediatamente después del breve periodo de sincronización
# Example query result
{
'id': 1,
'created_timestamp': None # New field shows NULL for existing entities
}
// java
// nodejs
{
'id': 1,
'created_timestamp': None # New field shows NULL for existing entities
}
// go
# restful
{
"code": 0,
"data": {},
"cost": 0
}
Escenario 2: Añadir campos con valores por defecto
Cuando desee que las entidades existentes tengan un valor inicial significativo en lugar de NULL, especifique valores por defecto.
# Add a field with default value
# This operation:
# - Sets the default value for all existing entities
# - Makes the field available with minimal delay
# - Maintains data consistency with the default value
client.add_collection_field(
collection_name="product_catalog",
field_name="priority_level", # Name of the new field
data_type=DataType.VARCHAR, # String type field
max_length=20, # Maximum string length
nullable=True, # Required for added fields
default_value="standard" # Value assigned to existing entities
# Also used for new entities if no value provided
)
client.addCollectionField(AddCollectionFieldReq.builder()
.collectionName("product_catalog")
.fieldName("priority_level")
.dataType(DataType.VarChar)
.maxLength(20)
.isNullable(true)
.build());
await client.addCollectionField({
collection_name: 'product_catalog',
field: {
name: 'priority_level',
dataType: 'VarChar',
nullable: true,
default_value: 'standard',
}
});
// go
# restful
curl -X POST "http://localhost:19530/v2/vectordb/collections/fields/add" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer <token>" \
-d '{
"collectionName": "product_catalog",
"schema": {
"fieldName": "priority_level",
"dataType": "VarChar",
"nullable": true,
"defaultValue": "standard",
"elementTypeParams": {
"max_length": "20"
}
}
}'
Comportamiento esperado:
Lasentidades existentes tendrán el valor por defecto (
"standard") para el campo recién añadidoLas nuevas entidades pueden anular el valor por defecto o utilizarlo si no se proporciona ningún valor
La disponibilidad del campo se produce casi inmediatamente con un retraso mínimo
Se puede consultar inmediatamente después del breve periodo de sincronización
# Example query result
{
'id': 1,
'priority_level': 'standard' # Shows default value for existing entities
}
// java
{
'id': 1,
'priority_level': 'standard' # Shows default value for existing entities
}
// go
# restful
{
'id': 1,
'priority_level': 'standard' # Shows default value for existing entities
}
PREGUNTAS FRECUENTES
¿Puedo activar la funcionalidad de esquema dinámico añadiendo un campo $meta?
No, no puede utilizar add_collection_field para añadir un campo $meta para habilitar la funcionalidad de campo dinámico. Por ejemplo, el código siguiente no funcionará:
# ❌ This is NOT supported
client.add_collection_field(
collection_name="existing_collection",
field_name="$meta",
data_type=DataType.JSON # This operation will fail
)
// ❌ This is NOT supported
client.addCollectionField(AddCollectionFieldReq.builder()
.collectionName("existing_collection")
.fieldName("$meta")
.dataType(DataType.JSON)
.build());
// ❌ This is NOT supported
await client.addCollectionField({
collection_name: 'product_catalog',
field: {
name: '$meta',
dataType: 'JSON',
}
});
// go
# restful
# ❌ This is NOT supported
curl -X POST "http://localhost:19530/v2/vectordb/collections/fields/add" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer <token>" \
-d '{
"collectionName": "existing_collection",
"schema": {
"fieldName": "$meta",
"dataType": "JSON",
"nullable": true
}
}'
Para habilitar la funcionalidad de esquema dinámico:
Nueva colección: Establezca
enable_dynamic_fielden True al crear la colección. Para obtener más información, consulte Crear colecciónColección existente: Establezca la propiedad de nivel de colección
dynamicfield.enableden True. Para más detalles, consulte Modificar colección.
¿Qué ocurre si añado un campo con el mismo nombre que una clave de campo dinámico?
Cuando su colección tiene habilitado el campo dinámico ($meta existe), puede añadir campos estáticos que tengan el mismo nombre que las claves de campo dinámico existentes. El nuevo campo estático enmascarará la clave del campo dinámico, pero se conservarán los datos dinámicos originales.
Para evitar posibles conflictos en los nombres de los campos, considere el nombre del campo que va a añadir consultando los campos existentes y las claves de los campos dinámicos antes de añadirlo.
Ejemplo de escenario:
# Original collection with dynamic field enabled
# Insert data with dynamic field keys
data = [{
"id": 1,
"my_vector": [0.1, 0.2, ...],
"extra_info": "this is a dynamic field key", # Dynamic field key as string
"score": 99.5 # Another dynamic field key
}]
client.insert(collection_name="product_catalog", data=data)
# Add static field with same name as existing dynamic field key
client.add_collection_field(
collection_name="product_catalog",
field_name="extra_info", # Same name as dynamic field key
data_type=DataType.INT64, # Data type can differ from dynamic field key
nullable=True # Must be True for added fields
)
# Insert new data after adding static field
new_data = [{
"id": 2,
"my_vector": [0.3, 0.4, ...],
"extra_info": 100, # Now must use INT64 type (static field)
"score": 88.0 # Still a dynamic field key
}]
client.insert(collection_name="product_catalog", data=new_data)
import com.google.gson.*;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
Gson gson = new Gson();
JsonObject row = new JsonObject();
row.addProperty("id", 1);
row.add("my_vector", gson.toJsonTree(new float[]{0.1f, 0.2f, ...}));
row.addProperty("extra_info", "this is a dynamic field key");
row.addProperty("score", 99.5);
InsertResp insertR = client.insert(InsertReq.builder()
.collectionName("product_catalog")
.data(Collections.singletonList(row))
.build());
client.addCollectionField(AddCollectionFieldReq.builder()
.collectionName("product_catalog")
.fieldName("extra_info")
.dataType(DataType.Int64)
.isNullable(true)
.build());
JsonObject newRow = new JsonObject();
newRow.addProperty("id", 2);
newRow.add("my_vector", gson.toJsonTree(new float[]{0.3f, 0.4f, ...}));
newRow.addProperty("extra_info", 100);
newRow.addProperty("score", 88.0);
insertR = client.insert(InsertReq.builder()
.collectionName("product_catalog")
.data(Collections.singletonList(newRow))
.build());
// Original collection with dynamic field enabled
// Insert data with dynamic field keys
const data = [{
"id": 1,
"my_vector": [0.1, 0.2, ...],
"extra_info": "this is a dynamic field key", // Dynamic field key as string
"score": 99.5 // Another dynamic field key
}]
await client.insert({
collection_name: "product_catalog",
data: data
});
// Add static field with same name as existing dynamic field key
await client.add_collection_field({
collection_name: "product_catalog",
field_name: "extra_info", // Same name as dynamic field key
data_type: DataType.INT64, // Data type can differ from dynamic field key
nullable: true // Must be True for added fields
});
// Insert new data after adding static field
const new_data = [{
"id": 2,
"my_vector": [0.3, 0.4, ...],
"extra_info": 100, # Now must use INT64 type (static field)
"score": 88.0 # Still a dynamic field key
}];
await client.insert({
collection_name:"product_catalog",
data: new_data
});
// go
# restful
#!/bin/bash
export MILVUS_HOST="localhost:19530"
export AUTH_TOKEN="your_token_here"
export COLLECTION_NAME="product_catalog"
echo "Step 1: Insert initial data with dynamic fields..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/entities/insert" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"data\": [{
\"id\": 1,
\"my_vector\": [0.1, 0.2, 0.3, 0.4, 0.5],
\"extra_info\": \"this is a dynamic field key\",
\"score\": 99.5
}]
}"
echo -e "\n\nStep 2: Add static field with same name as dynamic field..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/collections/fields/add" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"schema\": {
\"fieldName\": \"extra_info\",
\"dataType\": \"Int64\",
\"nullable\": true
}
}"
echo -e "\n\nStep 3: Insert new data after adding static field..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/entities/insert" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"data\": [{
\"id\": 2,
\"my_vector\": [0.3, 0.4, 0.5, 0.6, 0.7],
\"extra_info\": 100,
\"score\": 88.0
}]
}"
Comportamiento esperado:
Lasentidades existentes tendrán NULL para el nuevo campo estático
extra_infoLas nuevas entidades deben utilizar el tipo de datos del campo estático (
INT64)Los valores originales de la clave del campo dinámico se conservan y son accesibles a través de la sintaxis
$metaEl campo estático enmascara la clave del campo dinámico en las consultas normales
Acceso tanto a los valores estáticos como a los dinámicos
# 1. Query static field only (dynamic field key is masked)
results = client.query(
collection_name="product_catalog",
filter="id == 1",
output_fields=["extra_info"]
)
# Returns: {"id": 1, "extra_info": None} # NULL for existing entity
# 2. Query both static and original dynamic values
results = client.query(
collection_name="product_catalog",
filter="id == 1",
output_fields=["extra_info", "$meta['extra_info']"]
)
# Returns: {
# "id": 1,
# "extra_info": None, # Static field value (NULL)
# "$meta['extra_info']": "this is a dynamic field key" # Original dynamic value
# }
# 3. Query new entity with static field value
results = client.query(
collection_name="product_catalog",
filter="id == 2",
output_fields=["extra_info"]
)
# Returns: {"id": 2, "extra_info": 100} # Static field value
// java
// 1. Query static field only (dynamic field key is masked)
let results = client.query({
collection_name: "product_catalog",
filter: "id == 1",
output_fields: ["extra_info"]
})
// Returns: {"id": 1, "extra_info": None} # NULL for existing entity
// 2. Query both static and original dynamic values
results = client.query({
collection_name:"product_catalog",
filter: "id == 1",
output_fields: ["extra_info", "$meta['extra_info']"]
});
// Returns: {
// "id": 1,
// "extra_info": None, # Static field value (NULL)
// "$meta['extra_info']": "this is a dynamic field key" # Original dynamic value
// }
// 3. Query new entity with static field value
results = client.query({
collection_name: "product_catalog",
filter: "id == 2",
output_fields: ["extra_info"]
})
// Returns: {"id": 2, "extra_info": 100} # Static field value
// go
# restful
#!/bin/bash
export MILVUS_HOST="localhost:19530"
export AUTH_TOKEN="your_token_here"
export COLLECTION_NAME="product_catalog"
echo "Query 1: Static field only (dynamic field masked)..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/entities/query" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"filter\": \"id == 1\",
\"outputFields\": [\"extra_info\"]
}"
echo -e "\n\nQuery 2: Both static and original dynamic values..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/entities/query" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"filter\": \"id == 1\",
\"outputFields\": [\"extra_info\", \"\$meta['extra_info']\"]
}"
echo -e "\n\nQuery 3: New entity with static field value..."
curl -X POST "http://${MILVUS_HOST}/v2/vectordb/entities/query" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-H "Authorization: Bearer ${AUTH_TOKEN}" \
-d "{
\"collectionName\": \"${COLLECTION_NAME}\",
\"filter\": \"id == 2\",
\"outputFields\": [\"extra_info\"]
}"
¿Cuánto tarda en estar disponible un nuevo campo?
Los campos añadidos están disponibles casi inmediatamente, pero puede haber un breve retraso debido a la difusión interna de los cambios de esquema a través del clúster Milvus. Esta sincronización asegura que todos los nodos son conscientes de la actualización del esquema antes de procesar las consultas que implican el nuevo campo.