Bidang Array
Tipe Array digunakan untuk menyimpan bidang yang berisi beberapa nilai dengan tipe data yang sama. Tipe ini menyediakan cara yang fleksibel untuk menyimpan atribut dengan banyak elemen, sehingga sangat berguna dalam skenario di mana sekumpulan data terkait perlu disimpan. Di Milvus, Anda dapat menyimpan bidang Array di samping data vektor, sehingga memungkinkan kueri yang lebih kompleks dan persyaratan pemfilteran.
Sebagai contoh, dalam sistem rekomendasi musik, bidang Array dapat menyimpan daftar tag untuk sebuah lagu; dalam analisis perilaku pengguna, bidang ini dapat menyimpan peringkat pengguna untuk lagu. Di bawah ini adalah contoh bidang Array yang khas.
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3]
}
Dalam contoh ini, tags
dan ratings
keduanya adalah bidang Array. Bidang tags
adalah larik string yang mewakili genre lagu seperti pop, rock, dan klasik, sedangkan bidang ratings
adalah larik bilangan bulat yang mewakili peringkat pengguna untuk lagu tersebut, mulai dari 1 hingga 5. Bidang Array ini menyediakan cara yang fleksibel untuk menyimpan data multi-nilai, sehingga lebih mudah untuk melakukan analisis terperinci selama kueri dan pemfilteran.
Menambahkan bidang Array
Untuk menggunakan bidang Array di Milvus, tentukan jenis bidang yang relevan saat membuat skema koleksi. Proses ini meliputi.
Mengatur
datatype
ke tipe data Array yang didukung,ARRAY
.Menggunakan parameter
element_type
untuk menentukan tipe data dari elemen-elemen di dalam larik. Ini bisa berupa tipe data skalar apa pun yang didukung oleh Milvus, sepertiVARCHAR
atauINT64
. Semua elemen dalam Larik yang sama harus memiliki tipe data yang sama.Menggunakan parameter
max_capacity
untuk mendefinisikan kapasitas maksimum larik, yaitu jumlah maksimum elemen yang dapat ditampung.
Berikut ini cara mendefinisikan skema koleksi yang menyertakan bidang Array.
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
]
}"
Dalam contoh ini.
tags
adalah larik string denganelement_type
disetel keVARCHAR
, yang mengindikasikan bahwa elemen dalam larik harus berupa string.max_capacity
disetel ke 10, yang berarti larik dapat berisi hingga 10 elemen.ratings
adalah larik bilangan bulat denganelement_type
diatur keINT64
, yang menunjukkan bahwa elemen harus berupa bilangan bulat.max_capacity
diatur ke 5, yang memungkinkan hingga 5 peringkat.Kami juga menambahkan bidang kunci utama
pk
dan bidang vektorembedding
.
Bidang utama dan bidang vektor wajib ada saat Anda membuat koleksi. Bidang utama mengidentifikasi setiap entitas secara unik, sedangkan bidang vektor sangat penting untuk pencarian kemiripan. Untuk lebih jelasnya, lihat Bidang Utama & AutoID, Vektor Padat, Vektor Biner, atau Vektor Jarang.
Mengatur parameter indeks
Menetapkan parameter indeks untuk bidang Array bersifat opsional, namun dapat meningkatkan efisiensi pencarian secara signifikan.
Pada contoh berikut, kita membuat AUTOINDEX
untuk field tags
, yang berarti Milvus akan secara otomatis membuat indeks skalar yang sesuai berdasarkan tipe data.
# 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"
}
]'
Selain AUTOINDEX
, Anda dapat menentukan jenis indeks skalar lain seperti INVERTED
atau BITMAP
. Untuk jenis indeks yang didukung, lihat Indeks Skalar.
Selain itu, Anda harus membuat indeks untuk bidang vektor sebelum membuat koleksi. Dalam contoh ini, kita menggunakan AUTOINDEX
untuk menyederhanakan penyiapan indeks vektor.
# 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"
}
]'
Membuat koleksi
Gunakan skema dan parameter indeks yang ditentukan untuk membuat koleksi.
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
}"
Menyisipkan data
Setelah membuat koleksi, Anda dapat menyisipkan data yang menyertakan bidang Array.
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"
}'
Dalam contoh ini.
Setiap entri data mencakup bidang utama (
pk
), sedangkantags
danratings
adalah bidang Array yang digunakan untuk menyimpan tag dan peringkat.embedding
adalah bidang vektor 3 dimensi yang digunakan untuk pencarian kemiripan vektor.
Pencarian dan kueri
Bidang larik memungkinkan pemfilteran skalar selama pencarian, meningkatkan kemampuan pencarian vektor Milvus. Anda dapat membuat kueri berdasarkan properti bidang Array di samping pencarian kemiripan vektor.
Menyaring kueri
Anda dapat memfilter data berdasarkan properti bidang Array, seperti mengakses elemen tertentu atau memeriksa apakah elemen array memenuhi kondisi tertentu.
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"]}}}}]}
Dalam kueri ini, Milvus menyaring entitas yang elemen pertama larik ratings
kurang dari 4, dan mengembalikan entitas yang sesuai dengan kondisi tersebut.
Pencarian vektor dengan pemfilteran Array
Dengan menggabungkan kemiripan vektor dengan pemfilteran Array, Anda dapat memastikan bahwa data yang diambil tidak hanya mirip secara semantik tetapi juga memenuhi kondisi tertentu, sehingga hasil pencarian lebih akurat dan selaras dengan kebutuhan bisnis.
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"]}}}}]}
Dalam contoh ini, Milvus mengembalikan 5 entitas teratas yang paling mirip dengan vektor kueri, dengan elemen pertama larik tags
adalah "pop"
.
Selain itu, Milvus mendukung operator pemfilteran Array tingkat lanjut seperti ARRAY_CONTAINS
, ARRAY_CONTAINS_ALL
, ARRAY_CONTAINS_ANY
, dan ARRAY_LENGTH
untuk lebih meningkatkan kemampuan kueri. Untuk lebih jelasnya, lihat Pemfilteran Metadata.
Batasan
Tipe Data: Semua elemen dalam bidang Array harus memiliki tipe data yang sama, seperti yang ditentukan oleh
element_type
.Kapasitas Larik: Jumlah elemen dalam bidang Array harus kurang dari atau sama dengan kapasitas maksimum yang ditentukan saat Array dibuat, seperti yang ditentukan oleh
max_capacity
.Penanganan String: Nilai string dalam bidang Array disimpan apa adanya, tanpa pelarian atau konversi semantik. Misalnya,
'a"b'
,"a'b"
,'a\'b'
, dan"a\"b"
disimpan seperti yang dimasukkan, sedangkan'a'b'
dan"a"b"
dianggap sebagai nilai yang tidak valid.