الحقول القابلة للإلغاء
تدعم Milvus الحقول القابلة للفراغ، والتي تسمح بأن تكون قيمة الحقل مفقودة أو يتم تعيينها صراحةً إلى NULL. يتم تعريف قابلية الإلغاء على مستوى المخطط ويتم تطبيقها بشكل متسق عبر عمليات استيعاب البيانات والفهرسة والبحث والاستعلام.
استخدم الحقول القابلة للإلغاء عندما:
- يتم إدخال البيانات من أنظمة خارجية تسمح بالقيم المفقودة.
- تكون بعض البيانات الوصفية اختيارية أو متاحة فقط لجزء من مجموعة البيانات.
- يتم إنشاء تضمينات المتجهات بشكل غير متزامن وإدراجها لاحقًا.
الحدود
لا تدعم حقول المتجهات التي تسمح بقيم فارغة
IS NULLأو تعبيرات التصفيةIS NOT NULL. لا يمكنك تصفية الكيانات بشكل صريح استنادًا إلى ما إذا كانت قيمة الحقل المتجه NULL.حقولصفيف الهياكل لا تدعم القيم الفارغة. لا يمكنك وضع علامة على حقل صفيف الهياكل أو أي حقل متداخل داخله على أنه قابل للإلغاء.
يتم تعريف السمة القابلة للإلغاء عند إنشاء الحقل ولا يمكن تعديلها بعد ذلك. لا يمكنك تمكين أو تعطيل قابلية البطلان لحقل موجود.
لا يمكن استخدام الحقول التي تم وضع علامة لاغية عليها كمفاتيح تقسيم. يجب أن تحتوي حقول مفاتيح التقسيم دائمًا على قيم صحيحة وغير فارغة. لمزيد من المعلومات، راجع استخدام مفتاح التقسيم.
ما هو الحقل القابل للإلغاء؟
في Milvus، يتم التحكم فيما إذا كان الحقل مسموحًا بتخزين قيمة فارغة أم لا بواسطة سمة حقل على مستوى المخطط تسمى nullable.
عندما يتم تعريف الحقل بـ nullable=True ، يسمح ميلفوس بأن تكون قيمة الحقل مفقودة أثناء استيعاب البيانات. في الممارسة العملية، يتعامل Milvus مع المدخلين التاليين على أنهما متكافئان ويخزن قيمة الحقل على أنها NULL:
- يتم حذف الحقل من كيان الإدخال.
- يتم تعيين الحقل بشكل صريح إلى NULL (على سبيل المثال،
Noneفي Python).
إذا لم يتم تعريف الحقل على أنه غير قابل للإلغاء (السلوك الافتراضي)، يجب على كل كيان توفير قيمة صالحة لهذا الحقل. سيؤدي حذف الحقل أو تعيين قيمة فارغة بشكل صريح إلى فشل عملية الإدراج أو الاستيراد.
يتم دعم السمة القابلة للإلغاء لكل من الحقول القياسية والمتجهة في مخطط المجموعة. ومع ذلك، لا تدعم حقول صفيف الهياكل السمة القابلة للإلغاء.
تحدد قابلية الإلغاء ما إذا كانت قيمة الحقل مفقودة أم لا؛ ولا تحدد القيمة المستخدمة عندما يكون الحقل مفقودًا.
- إذا تم تكوين حقل قابل للإلغاء بدون قيمة افتراضية، فإن حذف الحقل ينتج عنه قيمة فارغة مخزنة.
- إذا تم تكوين قيمة افتراضية، فقد يخزن ميلفوس القيمة الافتراضية بدلاً من ذلك. لمزيد من التفاصيل، راجع القيم الافتراضية.
تعريف حقل قابل للإلغاء في مخطط المجموعة
لاستخدام الحقول القابلة للإلغاء، يجب تمكين السمة القابلة للإلغاء عند تعريف مخطط المجموعة.
في هذا المثال، يحدد مخطط المجموعة حقلاً متجهًا باسم embedding مع nullable=True. يسمح هذا للكيانات في المجموعة بحذف قيمة المتجه أو تعيينها صراحةً إلى NULL أثناء استيعاب البيانات.
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
# Define schema fields
schema = client.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True) # Primary field
schema.add_field(
field_name="embedding",
datatype=DataType.FLOAT_VECTOR,
dim=4,
nullable=True, # Enable the nullable attribute; defaults to False
)
client.create_collection(
collection_name="my_collection",
schema=schema,
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.token("root:Milvus")
.build());
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.build();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("embedding")
.dataType(DataType.FloatVector)
.dimension(4)
.isNullable(true)
.build());
client.createCollection(CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.build());
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({
address: "http://localhost:19530",
token: "root:Milvus",
});
await client.createCollection({
collection_name: "my_collection",
fields: [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
autoID: false,
},
{
name: "embedding",
data_type: DataType.FloatVector,
dim: 4,
nullable: true,
},
],
});
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
schema := entity.NewSchema()
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true),
).WithField(entity.NewField().
WithName("embedding").
WithDataType(entity.FieldTypeFloatVector).
WithDim(4).
WithNullable(true),
)
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema))
if err != nil {
fmt.Println(err.Error())
// handle error
}
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"
export pkField='{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
}'
export embeddingField='{
"fieldName": "embedding",
"dataType": "FloatVector",
"typeParams": {"dim": "4"},
"nullable": true
}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
\"collectionName\": \"my_collection\",
\"schema\": {
\"fields\": [
$pkField,
$embeddingField
]
}
}"
في هذا المخطط
- يتم تمييز الحقل
embeddingبشكل صريح على أنه قابل للإلغاء. - يجوز للكيانات حذف الحقل
embeddingأو تعيين قيمة فارغة له أثناء الإدراج. - يتم تحديد قرار السماح بالقيم الفارغة في وقت إنشاء المجموعة.
للتوضيح، تركز الأمثلة التالية على الحقل المتجه القابل للفراغ (embedding). يعد تحديد الحقول القياسية القابلة للإلغاء اختياريًا وغير مطلوب لاتباع بقية هذا الدليل.
اختياري: تعريف الحقل القياسي القابل للإلغاء
يمكن أيضًا تعريف الحقول العددية على أنها قابلة للإلغاء باستخدام نفس السمة nullable واتباع نفس القواعد أثناء الاستيعاب. على سبيل المثال:
schema.add_field(
field_name="age",
datatype=DataType.INT64,
nullable=True,
)
schema.addField(AddFieldReq.builder()
.fieldName("age")
.dataType(DataType.Int64)
.isNullable(true)
.build());
// Add to the fields array when calling createCollection:
// { name: "age", data_type: DataType.Int64, nullable: true },
schema.WithField(entity.NewField().
WithName("age").
WithDataType(entity.FieldTypeInt64).
WithNullable(true),
)
# Add another field object to the schema "fields" array, for example:
# { "fieldName": "age", "dataType": "Int64", "nullable": true }
سلوك الإدراج مع القيم المفقودة أو الفارغة
بمجرد تعريف الحقل على أنه قابل للإلغاء في مخطط المجموعة، يسمح Milvus بأن تكون قيمة الحقل مفقودة أو يتم تعيينها صراحةً إلى NULL أثناء استيعاب البيانات.
يقوم المثال أدناه بإدراج ثلاثة كيانات في المجموعة التي تم إنشاؤها في تعريف حقل قابل للفراغ في مخطط المجموعة، مما يوضح هذه الحالات المختلفة.
data = [
{
"id": 1,
"embedding": [0.1, 0.2, 0.3, 0.4],
},
{
"id": 2,
"embedding": None, # Explicitly set to NULL
},
{
"id": 3, # Field omitted → stored as NULL
},
]
client.insert(
collection_name="my_collection",
data=data,
)
import com.google.gson.Gson;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import java.util.Arrays;
import java.util.List;
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.addProperty("id", 1);
row1.add("embedding", gson.toJsonTree(Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f)));
JsonObject row2 = new JsonObject();
row2.addProperty("id", 2);
row2.add("embedding", JsonNull.INSTANCE); // Explicitly set to NULL
JsonObject row3 = new JsonObject();
row3.addProperty("id", 3); // Field omitted; stored as NULL
List<JsonObject> data = Arrays.asList(row1, row2, row3);
client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(data)
.build());
const data = [
{ id: 1, embedding: [0.1, 0.2, 0.3, 0.4] },
{ id: 2, embedding: null },
{ id: 3 },
];
await client.insert({
collection_name: "my_collection",
data: data,
});
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()
rows := []any{
map[string]any{"id": int64(1), "embedding": []float32{0.1, 0.2, 0.3, 0.4}},
map[string]any{"id": int64(2), "embedding": nil},
map[string]any{"id": int64(3)},
}
_, err := client.Insert(ctx, milvusclient.NewRowBasedInsertOption("my_collection", rows...))
if err != nil {
fmt.Println(err.Error())
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"data": [
{"id": 1, "embedding": [0.1, 0.2, 0.3, 0.4]},
{"id": 2, "embedding": null},
{"id": 3}
]
}'
في هذا المثال
- معرف الكيان = 1 يوفر قيمة متجه صالحة.
- معرف الكيان = 2 يعين صراحةً قيمة فارغة للحقل
embedding. - معرّف الكيان = 3 يحذف الحقل
embeddingبالكامل؛ حيث يخزنه ميلفوس على أنه NULL.
سلوك الفهرس على الحقول القابلة للإلغاء
بعد إدراج البيانات، يمكنك إنشاء فهرس على حقل قابل للإلغاء كالمعتاد. الفرق الرئيسي هو كيفية تعامل ميلفوس مع القيم الفارغة أثناء إنشاء الفهرس:
- تتم إضافة الكيانات ذات القيم غير الفارغة فقط إلى الفهرس.
- يتم تخطي الكيانات ذات القيم الفارغة ولا تشارك في بناء الفهرس.
بالنسبة للحقل المتجه القابل للفراغ، هذا يعني أن الكيانات ذات المتجهات الصالحة فقط تصبح قابلة للبحث عن طريق تشابه المتجهات.
# Set index parameters
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="AUTOINDEX",
metric_type="COSINE",
)
# Create index
client.create_index(
collection_name="my_collection",
index_params=index_params,
)
# Load collection for future search operations
client.load_collection(collection_name="my_collection")
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.LoadCollectionReq;
import io.milvus.v2.service.index.request.CreateIndexReq;
import java.util.Collections;
IndexParam indexParam = IndexParam.builder()
.fieldName("embedding")
.indexName("embedding_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build();
client.createIndex(CreateIndexReq.builder()
.collectionName("my_collection")
.indexParams(Collections.singletonList(indexParam))
.build());
client.loadCollection(LoadCollectionReq.builder()
.collectionName("my_collection")
.build());
await client.createIndex({
collection_name: "my_collection",
field_name: "embedding",
index_name: "embedding_idx",
index_type: "AUTOINDEX",
metric_type: "COSINE",
});
await client.loadCollection({
collection_name: "my_collection",
});
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()
indexOption := milvusclient.NewCreateIndexOption("my_collection", "embedding",
index.NewAutoIndex(entity.COSINE))
_, err := client.CreateIndex(ctx, indexOption)
if err != nil {
fmt.Println(err.Error())
}
_, err = client.LoadCollection(ctx, milvusclient.NewLoadCollectionOption("my_collection"))
if err != nil {
fmt.Println(err.Error())
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"indexParams": [
{
"fieldName": "embedding",
"metricType": "COSINE",
"indexType": "AUTOINDEX"
}
]
}'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/load" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{"collectionName": "my_collection"}'
في هذه المرحلة
- يتم فهرسة الكيانات التي تحتوي على قيم تضمين صالحة وجاهزة للبحث.
- تظل الكيانات التي يكون تضمينها فارغًا في المجموعة، ولكن لا يتم تضمينها في فهرس المتجهات.
سلوك البحث مع الحقول القابلة للإلغاء
عند إجراء عمليات بحث على حقل قابل للإلغاء، يقوم Milvus بتقييم الكيانات التي تحتوي على قيم غير فارغة للحقل المستخدم في البحث فقط. يتم تخطي الكيانات التي يكون حقلها المتجه فارغًا تلقائيًا.
بالنسبة لحقل متجه قابل للإلغاء مثل embedding في هذا المثال:
- يتم تقييم وترتيب الكيانات ذات القيم المتجهة الصالحة فقط.
- لا تتسبب الكيانات ذات المتجهات الفارغة في حدوث أخطاء.
- إذا كان عدد المتجهات الصالحة أقل من المطلوب
topK(limit)، قد يُرجع ميلفوس نتائج أقل منlimit.
يقوم المثال التالي بإجراء بحث متجه على حقل المتجه القابل للفراغ embedding:
res = client.search(
collection_name="my_collection",
data=[[0.1, 0.2, 0.3, 0.4]],
anns_field="embedding",
limit=3,
search_params={"metric_type": "COSINE"},
output_fields=["embedding"],
)
print(res)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
import java.util.Arrays;
import java.util.Collections;
SearchResp res = client.search(SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(new FloatVec(Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f))))
.annsField("embedding")
.limit(3)
.outputFields(Collections.singletonList("embedding"))
.build());
System.out.println(res);
const res = await client.search({
collection_name: "my_collection",
data: [[0.1, 0.2, 0.3, 0.4]],
anns_field: "embedding",
limit: 3,
search_params: { metric_type: "COSINE" },
output_fields: ["embedding"],
});
console.log(res);
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
// Assumes `client` is the Milvus client from the Go schema example above.
ctx := context.Background()
query := []float32{0.1, 0.2, 0.3, 0.4}
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection",
3,
[]entity.Vector{entity.FloatVector(query)},
).WithANNSField("embedding").
WithOutputFields("embedding"))
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(resultSets)
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"data": [[0.1, 0.2, 0.3, 0.4]],
"annsField": "embedding",
"limit": 3,
"searchParams": {"metricType": "COSINE"},
"outputFields": ["embedding"]
}'
في هذا البحث
- يتم اعتبار الكيانات التي تحتوي على قيم
embeddingغير فارغة فقط مرشحة. - يتم استبعاد الكيانات ذات القيم الفارغة
embeddingمن التقييم. - يعتمد عدد النتائج التي تم إرجاعها على عدد المتجهات الصالحة الموجودة في المجموعة.
آثار الاستعلام والتصفية
تركز الأمثلة السابقة على حقول المتجهات. يصف هذا القسم كيف تتصرف القيم الفارغة في تعبيرات التصفية العددية.
يمكن تعريف الحقول العددية باستخدام nullable=True واتباع نفس قواعد الاستيعاب مثل الحقول المتجهة. ومع ذلك، يتم دائمًا تقييم القيم القياسية الفارغة إلى خطأ في تعبيرات التصفية.
على سبيل المثال، بالنظر إلى الحقل القياسي القابل للإلغاء age ، يختار المرشح التالي الكيانات التي يكون عمرها أكبر من 18 عامًا:
expr = "age > 18"
String filter = "age > 18";
const expr = "age > 18";
filter := "age > 18"
# Use in query/search filter parameter, for example:
# "filter": "age > 18"
تُستبعد الكيانات التي يكون فيها age هو NULL من النتائج لأن القيمة NULL لا تفي بشرط التصفية.
وبالمثل، لا تتطابق عمليات التحقق من المساواة مع القيم الفارغة. على سبيل المثال:
expr = 'status == "active"'
String filter = "status == \"active\"";
const expr = 'status == "active"';
filter := `status == "active"`
# "filter": "status == \"active\""
يتم استبعاد الكيانات التي يكون فيها status هو NULL من النتائج.
الحقول القابلة للإلغاء والقيم الافتراضية
عندما يتم تكوين كل من nullable و default_value لحقل ما، تحدد القواعد التالية كيفية تعامل ميلفوس مع المدخلات الفارغة أو قيم الحقل المفقودة أثناء الإدراج.
| ممكّن قابل للفراغ | القيمة الافتراضية | إدخال المستخدم (فارغة أو محذوفة) | النتيجة |
|---|---|---|---|
| نعم (غير فارغة) | نعم (غير فارغة) | فارغة أو محذوفة | يستخدم القيمة الافتراضية |
| نعم | غير موجودة | فارغة أو محذوفة | مخزنة ك NULLL |
| لا يوجد | نعم (غير فارغة) | فارغة أو محذوفة | يستخدم القيمة الافتراضية |
| لا يوجد | لا يوجد | فارغة أو محذوفة | إلقاء خطأ |
| لا يوجد | نعم (افتراضي غير موجود) | فارغة أو محذوفة | إلقاء خطأ |
الخلاصة الرئيسية:
- عندما يكون للحقل قيمة افتراضية غير فارغة، يتم استخدام تلك القيمة بغض النظر عما إذا كان
nullableممكّنًا أم لا. - عندما
nullable=Trueولكن لم يتم تعيين قيمة افتراضية، يتم تخزين الحقل NULL. - عند
nullable=Falseولم يتم تعيين قيمة افتراضية، يفشل الإدراج مع حدوث خطأ. - تعيين قيمة افتراضية فارغة في حقل غير قابل للفراغ غير صالح ويتسبب في حدوث خطأ.
للاطلاع على أمثلة كاملة واستخدام واجهة برمجة التطبيقات للقيم الافتراضية، راجع القيم الافتراضية.