حقل المصفوفة
يُستخدم نوع المصفوفة لتخزين الحقول التي تحتوي على قيم متعددة من نفس نوع البيانات. يوفر طريقة مرنة لتخزين السمات ذات العناصر المتعددة، مما يجعلها مفيدة بشكل خاص في السيناريوهات التي تحتاج إلى حفظ مجموعة من البيانات ذات الصلة. في Milvus، يمكنك تخزين حقول المصفوفات جنبًا إلى جنب مع البيانات المتجهة، مما يتيح متطلبات استعلام وتصفية أكثر تعقيدًا.
على سبيل المثال، في نظام التوصية بالموسيقى، يمكن لحقل المصفوفة تخزين قائمة علامات لأغنية ما؛ وفي تحليل سلوك المستخدم، يمكن تخزين تقييمات المستخدمين للأغاني. فيما يلي مثال لحقل مصفوفة نموذجي.
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3]
}
في هذا المثال، tags
و ratings
كلاهما حقلا مصفوفة. الحقل tags
عبارة عن مصفوفة سلاسل تمثل أنواع الأغاني مثل البوب والروك والكلاسيكية، بينما الحقل ratings
عبارة عن مصفوفة أعداد صحيحة تمثل تقييمات المستخدمين للأغنية، والتي تتراوح من 1 إلى 5. توفر حقول المصفوفات هذه طريقة مرنة لتخزين البيانات متعددة القيم، مما يسهل إجراء تحليل مفصل أثناء الاستعلامات والتصفية.
إضافة حقل مصفوفة
لاستخدام حقول المصفوفات في Milvus، قم بتعريف نوع الحقل ذي الصلة عند إنشاء مخطط المجموعة. تتضمن هذه العملية.
تعيين
datatype
إلى نوع بيانات المصفوفات المدعوم،ARRAY
.استخدام المعلمة
element_type
لتحديد نوع بيانات العناصر في المصفوفة. يمكن أن يكون هذا أي نوع بيانات قياسي مدعوم من قبل Milvus، مثلVARCHAR
أوINT64
. يجب أن تكون جميع العناصر في نفس المصفوفة من نفس نوع البيانات.استخدام المعلمة
max_capacity
لتحديد السعة القصوى للمصفوفة، أي الحد الأقصى لعدد العناصر التي يمكن أن تحتويها.
إليك كيفية تعريف مخطط مجموعة يتضمن حقول المصفوفات.
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
schema = client.create_schema(
auto_id=False,
enable_dynamic_fields=True,
)
# Add an Array field with elements of type VARCHAR
schema.add_field(field_name="tags", datatype=DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=10, max_length=65535)
# Add an Array field with elements of type INT64
schema.add_field(field_name="ratings", datatype=DataType.ARRAY, element_type=DataType.INT64, max_capacity=5)
# Add primary field
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)
# Add vector field
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)
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")
.build());
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);
schema.addField(AddFieldReq.builder()
.fieldName("tags")
.dataType(DataType.Array)
.elementType(DataType.VarChar)
.maxCapacity(10)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("ratings")
.dataType(DataType.Array)
.elementType(DataType.Int64)
.maxCapacity(5)
.maxLength(65535)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("pk")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("embedding")
.dataType(DataType.FloatVector)
.dimension(3)
.build());
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const schema = [
{
name: "tags",
data_type: DataType.Array,
element_type: DataType.VarChar,
max_capacity: 10,
max_length: 65535
},
{
name: "rating",
data_type: DataType.Array,
element_type: DataType.Int64,
max_capacity: 5,
},
{
name: "pk",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "embedding",
data_type: DataType.FloatVector,
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
]
}"
في هذا المثال
tags
هي مصفوفة سلاسل مع ضبطelement_type
علىVARCHAR
، مما يشير إلى أن العناصر في المصفوفة يجب أن تكون سلاسل.max_capacity
مضبوطة على 10، مما يعني أن المصفوفة يمكن أن تحتوي على ما يصل إلى 10 عناصر.ratings
هي مصفوفة عدد صحيح مع تعيينelement_type
إلىINT64
، مما يشير إلى أن العناصر يجب أن تكون أعدادًا صحيحة.max_capacity
مضبوط على 5، مما يسمح بما يصل إلى 5 تصنيفات.نضيف أيضًا حقل مفتاح أساسي
pk
وحقل متجهembedding
.
يكون الحقل الأساسي وحقل المتجه إلزاميًا عند إنشاء مجموعة. يعرّف الحقل الأساسي كل كيان بشكل فريد، في حين أن الحقل المتجه مهم للبحث عن التشابه. للمزيد من التفاصيل، راجع الحقل الأساسي والمعرف التلقائي أو المتجه الكثيف أو المتجه الثنائي أو المتجه المتناثر.
تعيين معلمات الفهرس
يعد تعيين معلمات الفهرس لحقول المصفوفة أمرًا اختياريًا ولكن يمكن أن يحسن كفاءة الاسترجاع بشكل كبير.
في المثال التالي، نقوم بإنشاء AUTOINDEX
للحقل tags
، مما يعني أن ميلفوس سيقوم تلقائيًا بإنشاء فهرس قياسي مناسب بناءً على نوع البيانات.
# Prepare index parameters
index_params = client.prepare_index_params() # Prepare IndexParams object
index_params.add_index(
field_name="tags", # Name of the Array field to index
index_type="AUTOINDEX", # Index type
index_name="inverted_index" # Index name
)
import io.milvus.v2.common.IndexParam;
import java.util.*;
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("tags")
.indexName("inverted_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.build());
const indexParams = [{
index_name: 'inverted_index',
field_name: 'tags',
index_type: IndexType.AUTOINDEX,
)];
export indexParams='[
{
"fieldName": "tags",
"indexName": "inverted_index",
"indexType": "AUTOINDEX"
}
]'
بالإضافة إلى AUTOINDEX
، يمكنك تحديد أنواع الفهارس العددية الأخرى مثل INVERTED
أو BITMAP
. لمعرفة أنواع الفهارس المدعومة، راجع الفهارس العددية.
علاوة على ذلك، يجب عليك إنشاء فهرس للحقل المتجه قبل إنشاء المجموعة. في هذا المثال، نستخدم في هذا المثال AUTOINDEX
لتبسيط إعداد الفهرس المتجه.
# Add vector index
index_params.add_index(
field_name="embedding",
index_type="AUTOINDEX", # Use automatic indexing to simplify complex index settings
metric_type="COSINE" # Specify similarity metric type, such as L2, COSINE, or IP
)
indexes.add(IndexParam.builder()
.fieldName("embedding")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build());
indexParams.push({
index_name: 'embedding_index',
field_name: 'embedding',
index_type: IndexType.AUTOINDEX,
});
export indexParams='[
{
"fieldName": "tags",
"indexName": "inverted_index",
"indexType": "AUTOINDEX"
},
{
"fieldName": "embedding",
"metricType": "COSINE",
"indexType": "AUTOINDEX"
}
]'
إنشاء مجموعة
استخدم المخطط المحدد ومعلمات الفهرس لإنشاء مجموعة.
client.create_collection(
collection_name="my_array_collection",
schema=schema,
index_params=index_params
)
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_array_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
client.create_collection({
collection_name: "my_array_collection",
schema: schema,
index_params: indexParams
})
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_array_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
إدراج البيانات
بعد إنشاء المجموعة، يمكنك إدراج البيانات التي تتضمن حقول المصفوفة.
data = [
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3],
"pk": 1,
"embedding": [0.12, 0.34, 0.56]
},
{
"tags": ["jazz", "blues"],
"ratings": [4, 5],
"pk": 2,
"embedding": [0.78, 0.91, 0.23]
},
{
"tags": ["electronic", "dance"],
"ratings": [3, 3, 4],
"pk": 3,
"embedding": [0.67, 0.45, 0.89]
}
]
client.insert(
collection_name="my_array_collection",
data=data
)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
rows.add(gson.fromJson("{\"tags\": [\"pop\", \"rock\", \"classic\"], \"ratings\": [5, 4, 3], \"pk\": 1, \"embedding\": [0.1, 0.2, 0.3]}", JsonObject.class));
rows.add(gson.fromJson("{\"tags\": [\"jazz\", \"blues\"], \"ratings\": [4, 5], \"pk\": 2, \"embedding\": [0.4, 0.5, 0.6]}", JsonObject.class));
rows.add(gson.fromJson("{\"tags\": [\"electronic\", \"dance\"], \"ratings\": [3, 3, 4], \"pk\": 3, \"embedding\": [0.7, 0.8, 0.9]}", JsonObject.class));
InsertResp insertR = client.insert(InsertReq.builder()
.collectionName("my_array_collection")
.data(rows)
.build());
const data = [
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3],
"pk": 1,
"embedding": [0.12, 0.34, 0.56]
},
{
"tags": ["jazz", "blues"],
"ratings": [4, 5],
"pk": 2,
"embedding": [0.78, 0.91, 0.23]
},
{
"tags": ["electronic", "dance"],
"ratings": [3, 3, 4],
"pk": 3,
"embedding": [0.67, 0.45, 0.89]
}
];
client.insert({
collection_name: "my_array_collection",
data: data,
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"data": [
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3],
"pk": 1,
"embedding": [0.12, 0.34, 0.56]
},
{
"tags": ["jazz", "blues"],
"ratings": [4, 5],
"pk": 2,
"embedding": [0.78, 0.91, 0.23]
},
{
"tags": ["electronic", "dance"],
"ratings": [3, 3, 4],
"pk": 3,
"embedding": [0.67, 0.45, 0.89]
}
],
"collectionName": "my_array_collection"
}'
في هذا المثال
يتضمن كل إدخال بيانات حقلًا أساسيًا (
pk
)، بينماtags
وratings
هما حقلا مصفوفة يستخدمان لتخزين العلامات والتقييمات.embedding
هو حقل متجه ثلاثي الأبعاد يستخدم لعمليات البحث عن التشابه المتجه.
البحث والاستعلام
تعمل حقول المصفوفات على تمكين التصفية القياسية أثناء عمليات البحث، مما يعزز قدرات البحث المتجهية في ميلفوس. يمكنك الاستعلام بناءً على خصائص حقول المصفوفات إلى جانب عمليات البحث عن التشابه المتجه.
تصفية الاستعلامات
يمكنك تصفية البيانات استنادًا إلى خصائص حقول المصفوفة، مثل الوصول إلى عنصر معين أو التحقق مما إذا كان عنصر المصفوفة يفي بشرط معين.
filter = 'ratings[0] < 4'
res = client.query(
collection_name="my_array_collection",
filter=filter,
output_fields=["tags", "ratings", "embedding"]
)
print(res)
# Output
# data: ["{'pk': 3, 'tags': ['electronic', 'dance'], 'ratings': [3, 3, 4], 'embedding': [np.float32(0.67), np.float32(0.45), np.float32(0.89)]}"]
import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;
String filter = "ratings[0] < 4";
QueryResp resp = client.query(QueryReq.builder()
.collectionName("my_array_collection")
.filter(filter)
.outputFields(Arrays.asList("tags", "ratings", "embedding"))
.build());
System.out.println(resp.getQueryResults());
// Output
//
// [QueryResp.QueryResult(entity={ratings=[3, 3, 4], pk=3, embedding=[0.7, 0.8, 0.9], tags=[electronic, dance]})]
client.query({
collection_name: 'my_array_collection',
filter: 'ratings[0] < 4',
output_fields: ['tags', 'ratings', 'embedding']
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_array_collection",
"filter": "ratings[0] < 4",
"outputFields": ["tags", "ratings", "embedding"]
}'
# {"code":0,"cost":0,"data":[{"embedding":[0.67,0.45,0.89],"pk":3,"ratings":{"Data":{"LongData":{"data":[3,3,4]}}},"tags":{"Data":{"StringData":{"data":["electronic","dance"]}}}}]}
في هذا الاستعلام، يقوم Milvus بتصفية الكيانات التي يكون فيها العنصر الأول من المصفوفة ratings
أقل من 4، مع إرجاع الكيانات التي تطابق الشرط.
البحث المتجه مع تصفية المصفوفة
من خلال الجمع بين تشابه المتجهات مع تصفية المصفوفة، يمكنك التأكد من أن البيانات المسترجعة ليست متشابهة في الدلالات فحسب، بل تفي أيضًا بشروط محددة، مما يجعل نتائج البحث أكثر دقة وتتماشى مع احتياجات العمل.
filter = 'tags[0] == "pop"'
res = client.search(
collection_name="my_array_collection",
data=[[0.3, -0.6, 0.1]],
limit=5,
search_params={"params": {"nprobe": 10}},
output_fields=["tags", "ratings", "embedding"],
filter=filter
)
print(res)
# Output
# data: ["[{'id': 1, 'distance': 1.1276001930236816, 'entity': {'ratings': [5, 4, 3], 'embedding': [0.11999999731779099, 0.3400000035762787, 0.5600000023841858], 'tags': ['pop', 'rock', 'classic']}}]"]
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
String filter = "tags[0] == \"pop\"";
SearchResp resp = client.search(SearchReq.builder()
.collectionName("my_array_collection")
.annsField("embedding")
.data(Collections.singletonList(new FloatVec(new float[]{0.3f, -0.6f, 0.1f})))
.topK(5)
.outputFields(Arrays.asList("tags", "ratings", "embedding"))
.filter(filter)
.build());
System.out.println(resp.getSearchResults());
// Output
//
// [[SearchResp.SearchResult(entity={ratings=[5, 4, 3], embedding=[0.1, 0.2, 0.3], tags=[pop, rock, classic]}, score=-0.2364331, id=1)]]
client.search({
collection_name: 'my_array_collection',
data: [0.3, -0.6, 0.1],
limit: 5,
output_fields: ['tags', 'ratings', 'embdding'],
filter: 'tags[0] == "pop"'
});
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_array_collection",
"data": [
[0.3, -0.6, 0.1]
],
"annsField": "embedding",
"limit": 5,
"filter": "tags[0] == \"pop\"",
"outputFields": ["tags", "ratings", "embedding"]
}'
# {"code":0,"cost":0,"data":[{"distance":-0.24793813,"embedding":[0.12,0.34,0.56],"id":1,"ratings":{"Data":{"LongData":{"data":[5,4,3]}}},"tags":{"Data":{"StringData":{"data":["pop","rock","classic"]}}}}]}
في هذا المثال، يقوم Milvus بإرجاع أفضل 5 كيانات الأكثر تشابهًا مع متجه الاستعلام، حيث يكون العنصر الأول في مصفوفة tags
هو "pop"
.
بالإضافة إلى ذلك، يدعم ميلفوس عوامل تصفية المصفوفات المتقدمة مثل ARRAY_CONTAINS
و ARRAY_CONTAINS_ALL
و ARRAY_CONTAINS_ANY
و ARRAY_LENGTH
لتعزيز قدرات الاستعلام. لمزيد من التفاصيل، راجع تصفية البيانات الوصفية.
الحدود
نوع البيانات: يجب أن تحتوي جميع العناصر في حقل المصفوفة على نفس نوع البيانات، كما هو محدد في
element_type
.سعة المصفوفة: يجب أن يكون عدد العناصر في حقل المصفوفة أقل من أو يساوي السعة القصوى المحددة عند إنشاء المصفوفة، كما هو محدد في
max_capacity
.التعامل مع السلسلة: يتم تخزين قيم السلسلة في حقول المصفوفات كما هي، دون هروب دلالي أو تحويل. على سبيل المثال، يتم تخزين
'a"b'
و"a'b"
و'a\'b'
و"a\"b"
كما تم إدخالها، بينما'a'b'
و"a"b"
تعتبر قيمًا غير صالحة.