إضافة حقول إلى مجموعة موجودةCompatible with Milvus 2.6.x

يسمح لك Milvus بإضافة حقول جديدة ديناميكيًا إلى المجموعات الموجودة، مما يسهل تطوير مخطط بياناتك مع تغير احتياجات تطبيقك. يوضح لك هذا الدليل كيفية إضافة حقول في سيناريوهات مختلفة باستخدام أمثلة عملية.

الاعتبارات

قبل إضافة حقول إلى مجموعتك، ضع هذه النقاط المهمة في الاعتبار:

  • يمكنك إضافة حقول قياسية (INT64 ، VARCHAR ، ، FLOAT ، DOUBLE ، إلخ). لا يمكن إضافة الحقول المتجهة إلى المجموعات الموجودة.

  • يجب أن تكون الحقول الجديدة قابلة للإلغاء (nullable=صحيح) لاستيعاب الكيانات الموجودة التي لا تحتوي على قيم للحقل الجديد.

  • تؤدي إضافة حقول إلى المجموعات المحملة إلى زيادة استخدام الذاكرة.

  • يوجد حد أقصى لإجمالي الحقول لكل مجموعة. لمزيد من التفاصيل، راجع حدود ميلفوس.

  • يجب أن تكون أسماء الحقول فريدة بين الحقول الثابتة.

  • لا يمكنك إضافة حقل $meta لتمكين وظيفة الحقل الديناميكي للمجموعات التي لم يتم إنشاؤها في الأصل باستخدام enable_dynamic_field=True.

المتطلبات الأساسية

يفترض هذا الدليل أن لديك

  • مثيل Milvus قيد التشغيل

  • تم تثبيت Milvus SDK

  • مجموعة موجودة

ارجع إلى إنشاء مجموعة لإنشاء مجموعة والعمليات الأساسية.

الاستخدام الأساسي

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"

السيناريو 1: إضافة حقول قابلة للإلغاء بسرعة

أبسط طريقة لتوسيع مجموعتك هي إضافة حقول قابلة للإلغاء. هذا مثالي عندما تحتاج إلى إضافة سمات جديدة بسرعة إلى بياناتك.

# 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 "Authorization: Bearer <token>" \
  -d '{
    "collectionName": "product_catalog",
    "schema": {
      "fieldName": "created_timestamp",
      "dataType": "Int64",
      "nullable": true
    }
  }'

السلوك المتوقع:

  • ستحتويالكيانات الحالية على NULL للحقل الجديد

  • يمكن أن تحتويالكيانات الجديدة على قيم فارغة أو فعلية

  • يحدثتوافر الحقل على الفور تقريبًا بأقل تأخير بسبب المزامنة الداخلية للمخطط

  • يمكن الاستعلام عنها مباشرة بعد فترة المزامنة القصيرة

# 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
}

السيناريو 2: إضافة حقول بقيم افتراضية

عندما تريد أن يكون للكيانات الموجودة قيمة أولية ذات معنى بدلاً من NULL، حدد القيم الافتراضية.

# 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 "Authorization: Bearer <token>" \
  -d '{
    "collectionName": "product_catalog",
    "schema": {
      "fieldName": "priority_level",
      "dataType": "VarChar",
      "nullable": true,
      "defaultValue": "standard",
      "elementTypeParams": {
        "max_length": "20"
      }
    }
  }'

السلوك المتوقع:

  • ستحصلالكيانات الحالية على القيمة الافتراضية ("standard") للحقل المضاف حديثًا

  • يمكن للكياناتالجديدة تجاوز القيمة الافتراضية أو استخدامها إذا لم يتم توفير قيمة

  • يحدثتوفر الحقل على الفور تقريبًا بأقل تأخير

  • يمكن الاستعلام عنه مباشرة بعد فترة المزامنة القصيرة

# 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
}

الأسئلة الشائعة

هل يمكنني تمكين وظيفة المخطط الديناميكي عن طريق إضافة حقل $meta ؟

لا، لا يمكنك استخدام add_collection_field لإضافة حقل $meta لتمكين وظيفة الحقل الديناميكي. على سبيل المثال، لن يعمل الرمز أدناه:

# ❌ 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 "Authorization: Bearer <token>" \
  -d '{
    "collectionName": "existing_collection",
    "schema": {
      "fieldName": "$meta",
      "dataType": "JSON",
      "nullable": true
    }
  }'

لتمكين وظيفة المخطط الديناميكي:

  • مجموعة جديدة: اضبط enable_dynamic_field على True عند إنشاء المجموعة. لمزيد من التفاصيل، راجع إنشاء مجموعة

  • المجموعة الحالية: قم بتعيين الخاصية على مستوى المجموعة dynamicfield.enabled إلى صواب. لمزيد من التفاصيل، راجع تعديل المجموعة.

ماذا يحدث عند إضافة حقل بنفس اسم مفتاح الحقل الديناميكي؟

عندما يتم تمكين الحقل الديناميكي في مجموعتك ($meta موجود)، يمكنك إضافة حقول ثابتة لها نفس اسم مفاتيح الحقول الديناميكية الموجودة. سيخفي الحقل الثابت الجديد مفتاح الحقل الديناميكي، ولكن يتم الاحتفاظ بالبيانات الديناميكية الأصلية.

لتجنب التعارضات المحتملة في أسماء الحقول، فكر في اسم الحقل المراد إضافته بالرجوع إلى الحقول الموجودة ومفاتيح الحقول الديناميكية قبل إضافته فعليًا.

سيناريو مثال:

# 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 "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 "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 "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
    }]
  }"

السلوك المتوقع:

  • ستحتويالكيانات الحالية على NULL للحقل الثابت الجديد extra_info

  • يجب أن تستخدمالكيانات الجديدة نوع بيانات الحقل الثابت (INT64)

  • يتم الاحتفاظبقيم مفاتيح الحقل الديناميكي الأصلي ويمكن الوصول إليها عبر بناء الجملة $meta

  • يخفي الحقل الثابت مفتاح الحقل الديناميكي في الاستعلامات العادية

الوصول إلى كل من القيم الثابتة والديناميكية

# 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 "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 "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 "Authorization: Bearer ${AUTH_TOKEN}" \
  -d "{
    \"collectionName\": \"${COLLECTION_NAME}\",
    \"filter\": \"id == 2\",
    \"outputFields\": [\"extra_info\"]
  }"

كم من الوقت يستغرق الحقل الجديد ليصبح متاحًا؟

تصبح الحقول المضافة متاحة على الفور تقريبًا، ولكن قد يكون هناك تأخير قصير بسبب بث تغيير المخطط الداخلي عبر مجموعة Milvus. تضمن هذه المزامنة أن تكون جميع العقد على علم بتحديث المخطط قبل معالجة الاستعلامات التي تتضمن الحقل الجديد.