Satu Entitas, Banyak Vektor: Pencarian Tingkat Entitas dan Elemen dengan Milvus 3.0 StructArray
Sebagian besar skema basis data vektor dimulai dengan asumsi sederhana: satu entitas, satu embedding. Sebuah produk mendapatkan satu vektor, begitu juga dokumen. Kueri pengguna di-embed dan dibandingkan dengan vektor-vektor tersebut melalui pencarian tetangga terdekat aproksimasi (ANN). Model ini bekerja untuk generasi pertama kasus penggunaan pencarian vektor, termasuk RAG, penelusuran semantik, dan sistem rekomendasi.
Data AI di dunia nyata, bagaimanapun, jarang cocok dengan asumsi itu. Sebuah video berisi klip, bidikan, atau bingkai kunci, masing-masing dengan embedding, rentang waktu, keterangan, label adegan, dan skor keyakinannya sendiri. Sebuah produk mungkin memiliki beberapa gambar dan sudut pandang. Dokumen panjang berisi bagian-bagian atau seksi yang makna lokalnya lebih penting daripada satu embedding dari keseluruhan dokumen. Model interaksi-terlambat yang populer juga menunjukkan keterbatasan yang sama pada granularitas yang lebih halus: ColBERT menghasilkan satu vektor per token, sementara ColPali menghasilkan satu vektor per patch visual.
Dalam setiap kasus, entitas induk tetap menjadi unit yang disimpan, ditampilkan, diamankan, dan dikembalikan oleh aplikasi. Namun relevansi, pemfilteran, dan penjelasan hasil sering kali bergantung pada elemen-elemen di dalam entitas tersebut.
Fitur StructArray yang baru memberi Milvus model data asli untuk bentuk ini: satu entitas berisi array terurut dari elemen Struct yang ditentukan skema, dan setiap elemen dapat membawa metadata skalar, embedding vektor, atau keduanya. Milvus dapat memfilter bidang-bidang yang termasuk ke dalam elemen yang sama, membandingkan dua daftar embedding di tingkat entitas, atau mencari elemen individual dan mengembalikan offset yang cocok.
Artikel ini menggunakan contoh pencarian video untuk menjelaskan model data, kemudian menelusurinya melalui desain skema, pemfilteran, granularitas pencarian vektor, strategi indeks EmbeddingList, penggabungan hasil hibrida, dan tata letak fisik yang membuat fitur tersebut dapat dieksekusi.
Mengapa model satu vektor dan satu baris datar tidak lagi cukup
Bayangkan pengguna mencari katalog video untuk "seseorang yang memotong sayuran di dapur." Sinyal yang relevan mungkin ada di satu klip delapan detik, bukan pada embedding seluruh video. Mengompresi setiap klip, objek, dan aksi menjadi satu vektor mungkin mempertahankan topik secara umum, tetapi dapat menghilangkan detail lokal.
Ketidakcocokan yang sama muncul di beban kerja lain:
- Relevansi sebuah produk mungkin berasal dari salah satu dari beberapa gambar atau sudut pandang.
- Dokumen mungkin cocok karena satu bagian tertentu, bukan karena subjek keseluruhannya.
- Memori agen mungkin berisi beberapa observasi, dan hanya satu di antaranya yang penting untuk tugas saat ini.
- Catatan ColBERT atau ColPali berisi daftar vektor token atau patch dengan panjang bervariasi, bukan satu vektor padat.
Salah satu alternatifnya adalah memecah setiap klip, gambar, atau bagian menjadi baris basis data terpisah. Ini memungkinkan pencarian lokal, tetapi juga memisahkan setiap fragmen dari entitas induknya. Metadata induk mungkin diulang di seluruh baris, dan pengambilan di tingkat entitas kemudian memerlukan pengelompokan, deduplikasi, dan peringkat ulang setelah pencarian fragmen.
Penyimpanan bersarang saja tidak menyelesaikan masalah kueri. JSON dapat menyimpan objek, tetapi tidak memberi Milvus skema subbidang yang telah ditentukan untuk pengindeksan vektor dan skalar. Array paralel dapat menyimpan keterangan, label adegan, dan nilai keyakinan, tetapi aplikasi harus menjaga keselarasan offset. Basis data tidak dapat dengan aman menyimpulkan bahwa scene_type[3] dan label_confidence[3] mendeskripsikan klip yang sama kecuali hubungan tersebut menjadi bagian dari model data.
StructArray mengodekan hubungan itu secara langsung. Ia menjaga elemen lokal di dalam entitas induk sambil mengekspos subbidang yang selaras ke validasi skema, pengindeksan, pemfilteran, dan pencarian vektor.
Apa itu StructArray dan model datanya?
StructArray, juga dikenal sebagai array of structs, menyimpan sekumpulan elemen Struct yang terurut di setiap entitas. Bidang StructArray adalah Array yang semua elemennya mengikuti satu skema Struct yang telah ditentukan. Untuk koleksi video, bentuk logisnya bisa terlihat seperti ini:
Plaintext
clips: ARRAY<STRUCT<
clip_embedding_list: FLOAT_VECTOR,
clip_embedding: FLOAT_VECTOR,
start_sec: DOUBLE,
end_sec: DOUBLE,
caption: VARCHAR,
scene_type: VARCHAR,
label_confidence: FLOAT
>>
Di sini:
clipsadalah bidang StructArray induk.clip_embedding_list,clip_embedding,start_sec, dan atribut lainnya adalah subbidang.clips[0]adalah klip pertama.- Setiap subbidang pada offset
0termasuk ke klip yang sama. - Setiap subbidang pada offset
3termasuk ke klip lain.
Kedua subbidang vektor melayani mode pencarian yang berbeda. clips[clip_embedding_list] diindeks dengan metrik MAX_SIM* untuk pencarian EmbeddingList di tingkat entitas, sedangkan clips[clip_embedding] diindeks dengan metrik vektor biasa untuk pencarian di tingkat elemen. Karena bidang vektor atau subbidang vektor hanya menerima satu indeks, koleksi yang membutuhkan kedua mode harus mendefinisikan dan mengindeks kedua subbidang secara terpisah.
Model ini mendukung tiga semantik kueri yang berbeda.
1. Pencarian EmbeddingList mengembalikan entitas induk
Vektor-vektor di clips[clip_embedding_list] membentuk satu daftar embedding untuk video. Kuerinya juga berupa EmbeddingList. Milvus membandingkan daftar kueri dengan setiap daftar tersimpan menggunakan metrik MAX_SIM* dan mengembalikan hasil di tingkat entitas.
Plaintext
clips[clip_embedding_list] = [
embedding_0,
embedding_1,
embedding_2,
...
]
2. Keluarga MATCH_* memfilter entitas induk
MATCH_ANY, MATCH_ALL, MATCH_LEAST, MATCH_MOST, dan MATCH_EXACT mengevaluasi predikat terhadap elemen Struct, menghitung berapa banyak elemen yang memenuhinya, dan memutuskan apakah entitas induk lolos filter.
Contohnya:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
Kedua kondisi skalar harus benar pada offset klip yang sama. Milvus tidak menggabungkan label dapur dari satu klip dengan nilai keyakinan tinggi dari klip lain.
3. Pencarian tingkat elemen mengembalikan offset elemen yang cocok
Vektor kueri biasa dapat mencari setiap vektor di clips[clip_embedding] secara independen. Setiap hasil mengidentifikasi entitas induk dan offset berbasis nol dari elemen Struct yang cocok. element_filter dapat membatasi elemen mana yang berpartisipasi dalam pencarian vektor tersebut.
Operasi-operasi ini berbagi satu premis: Milvus mengetahui nilai vektor dan skalar mana yang termasuk ke elemen yang sama, dan elemen mana yang termasuk ke entitas yang sama.
StructArray bukanlah sistem penyarangan arbitrer untuk tujuan umum. Modelnya saat ini adalah satu Array dari elemen Struct dengan subbidang skalar dan vektor yang didukung. Batasan itu membuat pengindeksan subbidang dan eksekusi yang sadar-elemen menjadi layak.
Bangun skema, indeks, dan jalur penyisipan
Contoh PyMilvus yang disederhanakan berikut membuat koleksi video dengan satu vektor tingkat atas dan StructArray untuk klip. Contoh ini menggunakan subbidang vektor klip yang terpisah sehingga koleksi yang sama dapat mendemonstrasikan kedua mode pencarian.
from pymilvus import DataType, MilvusClient
client = MilvusClient(uri=“http://localhost:19530”)
schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field(“id”, DataType.INT64, is_primary=True)
schema.add_field(“title”, DataType.VARCHAR, max_length=512)
schema.add_field(“video_embedding”, DataType.FLOAT_VECTOR, dim=768)
# Define the Struct schema explicitly.
clip_schema = client.create_struct_field_schema()
clip_schema.add_field(“clip_embedding_list”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“clip_embedding”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“start_sec”, DataType.DOUBLE)
clip_schema.add_field(“end_sec”, DataType.DOUBLE)
clip_schema.add_field(“caption”, DataType.VARCHAR, max_length=2048)
clip_schema.add_field(“scene_type”, DataType.VARCHAR, max_length=128)
clip_schema.add_field(“label_confidence”, DataType.FLOAT)
schema.add_field(
“clips”,
datatype=DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=clip_schema,
max_capacity=1024,
)
client.create_collection(“videos”, schema=schema)
Subbidang vektor harus diindeks sebelum pencarian. Karena keluarga metrik menentukan mode pencarian, setiap subbidang vektor mendapatkan indeksnya sendiri:
index_params = client.prepare_index_params()
# EmbeddingList search.
index_params.add_index(
field_name="clips[clip_embedding_list]",
index_type=“HNSW”,
metric_type=“MAX_SIM_COSINE”,
index_name=“clips_clip_embedding_list_maxsim_idx”,
params={“M”: 16, “efConstruction”: 200},
)
# Element-level search.
index_params.add_index(
field_name="clips[clip_embedding]",
index_type=“HNSW”,
metric_type=“COSINE”,
index_name=“clips_clip_embedding_cosine_idx”,
params={“M”: 16, “efConstruction”: 200},
)
client.create_index(“videos”, index_params=index_params)
Indeks skalar bersifat opsional, tetapi subbidang yang sering muncul dalam filter skala besar sebaiknya menggunakan indeks skalar yang kompatibel. Misalnya, clips[scene_type] dapat menggunakan indeks terbalik (inverted index), sementara subbidang numerik seperti clips[label_confidence] dapat menggunakan indeks yang cocok untuk pemfilteran numerik.
Sisipkan data dalam bentuk entitas alaminya: satu baris video dengan array objek klip. Agar contoh tetap ringkas, contoh ini menulis vektor klip yang sama ke kedua subbidang vektor.
rows = [
{
"id": 1,
"title": "cooking tutorial",
"video_embedding": video_vec,
"clips": [
{
"clip_embedding_list": clip_vec_1,
"clip_embedding": clip_vec_1,
"start_sec": 0.0,
"end_sec": 8.0,
"caption": "A person washes vegetables.",
"scene_type": "kitchen",
"label_confidence": 0.92,
},
{
"clip_embedding_list": clip_vec_2,
"clip_embedding": clip_vec_2,
"start_sec": 8.0,
"end_sec": 16.0,
"caption": "A person cuts carrots on a board.",
"scene_type": "kitchen",
"label_confidence": 0.96,
},
],
}
]
client.insert(“videos”, rows)
client.flush(“videos”)
client.load_collection(“videos”)
Di batas API, clips tetap berupa array objek terstruktur. Di dalam Milvus, setiap subbidang mengikuti jalur bertipe yang diperlukan untuk indeks, filter, dan perilaku keluarannya sendiri. Perbedaan itu transparan pada saat penyisipan, tetapi fundamental untuk semua yang terjadi selanjutnya.
Pemfilteran elemen yang sama adalah perbedaan antara struktur dan array paralel
Manfaat utama pemfilteran bukanlah sintaks yang lebih pendek untuk bidang bersarang. Manfaat utamanya adalah korelasi yang benar di seluruh subbidang skalar.
Misalkan aplikasi membutuhkan video yang berisi klip dapur dengan keyakinan label di atas 0.8. Tidak cukup jika video hanya berisi beberapa klip dapur dan beberapa klip dengan keyakinan tinggi; klip yang sama harus memenuhi kedua kondisi tersebut.
Keluarga MATCH_* StructArray mengekspresikan hal ini secara langsung:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
MATCH_ALL(clips, $[label_confidence] > 0.5)
MATCH_LEAST(clips, $[scene_type] == "sports", threshold=3)
MATCH_MOST(clips, $[label_confidence] < 0.2, threshold=1)
MATCH_EXACT(clips, $[scene_type] == "intro", threshold=1)
Milvus mengevaluasi predikat pada setiap offset elemen, lalu menerapkan kuantifier operator untuk memutuskan apakah entitas induk lolos:
MATCH_ANY: Setidaknya satu elemen cocok.MATCH_ALL: Semua elemen cocok.MATCH_LEAST: Setidaknyathresholdelemen cocok.MATCH_MOST: Paling banyakthresholdelemen cocok.MATCH_EXACT: Tepatthresholdelemen cocok.
Jika data yang sama disimpan sebagai dua array independen, ekspresi berikut tidak akan menjaga korelasi tersebut:
Plaintext
array_contains(clips[scene_type], "kitchen")
AND
array_contains(clips[label_confidence], 0.9)
Kedua nilai tersebut bisa terjadi pada offset yang berbeda. Ini mungkin valid untuk atribut yang tidak terkait, tetapi tidak benar ketika kedua kondisi mendeskripsikan klip, gambar produk, atau bagian dokumen yang sama.
StructArray menjadikan identitas elemen sebagai bagian dari predikat basis data, bukan konvensi yang harus ditegakkan oleh aplikasi.
Dua granularitas pencarian vektor, dua identitas hasil
Begitu sebuah entitas menyimpan banyak vektor, pengambilan harus menyelesaikan pertanyaan pemodelan sebelum pencarian ANN dimulai:
Haruskah vektor-vektor tersebut diberi skor bersama sebagai satu representasi dari entitas induk, atau haruskah setiap vektor elemen bersaing secara independen?
StructArray mendukung kedua model tersebut, tetapi keduanya menggunakan bentuk kueri, keluarga metrik, subbidang vektor, dan identitas hasil yang berbeda.
Pencarian EmbeddingList: daftar vektor kueri menemukan sebuah entitas
Kueri EmbeddingList berisi banyak vektor. Video kueri mungkin dibagi menjadi beberapa klip; kueri produk mungkin berisi beberapa gambar referensi; kueri ColBERT berisi satu vektor per token kueri.
Untuk setiap entitas, Milvus membandingkan daftar kueri dengan daftar embedding yang tersimpan di entitas. Di bawah skor bergaya MaxSim, setiap vektor kueri memilih kecocokan terbaiknya di daftar entitas, dan Milvus menggabungkan skor kecocokan terbaik tersebut menjadi skor entitas. Hasil akhir mewakili entitas induk, bukan satu elemen Struct tertentu.
from pymilvus.client.embedding_list import EmbeddingList
query = EmbeddingList()
query.add(query_clip_vec_1)
query.add(query_clip_vec_2)
client.search(
collection_name=“videos”,
data=[query],
anns_field="clips[clip_embedding_list]",
search_params={“metric_type”: “MAX_SIM_COSINE”},
limit=10,
)
Pencarian ini menjawab: Video mana yang merupakan kecocokan keseluruhan terbaik untuk kumpulan klip kueri ini?
Ini cocok untuk pengambilan video-ke-video, pencarian produk multi-gambar, pengambilan gaya ColBERT dan ColPali, serta kasus lain di mana kueri dan entitas tersimpan sama-sama diwakili oleh banyak vektor.
Pencarian tingkat elemen: satu vektor kueri menemukan klip di dalam entitas
Pencarian tingkat elemen menggunakan vektor kueri biasa. Setiap vektor di clips[clip_embedding] berpartisipasi dalam pencarian ANN sebagai kandidat independen. Setiap hasil mengidentifikasi entitas induk dan offset elemen yang cocok.
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
limit=10,
output_fields=["id", "title", "clips"],
)
Untuk hanya mencari klip tertentu, lampirkan element_filter yang kondisi skalarnya berlaku pada klip yang sama:
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
filter='element_filter(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)',
limit=10,
output_fields=["id", "title", "clips"],
)
Filter tidak terlebih dahulu memilih klip dapur lalu mencari klip dengan keyakinan tinggi yang berbeda. Baik predikat maupun kandidat vektor merujuk ke elemen Struct yang sama.
Respons tanpa pengelompokan mungkin terlihat seperti ini:
Plaintext
id = 1, offset = 1, distance = 0.91
id = 8, offset = 4, distance = 0.88
id = 1, offset = 3, distance = 0.84
Entitas yang sama dapat muncul lebih dari sekali karena beberapa klip dapat cocok. Ini berguna ketika aplikasi perlu menampilkan tidak hanya video atau dokumen mana yang relevan, tetapi juga klip atau bagian mana yang menghasilkan kecocokan tersebut.
| Aspek | Pencarian EmbeddingList | Pencarian tingkat elemen |
|---|---|---|
| Input kueri | Satu atau lebih vektor kueri dalam EmbeddingList | Satu vektor kueri biasa |
| Contoh target | clips[clip_embedding_list] | clips[clip_embedding] |
| Keluarga metrik | MAX_SIM* | Metrik biasa seperti COSINE, IP, atau L2 |
| Unit kandidat ANN | Daftar embedding entitas induk | Setiap vektor elemen Struct |
| Identitas hasil | Entitas induk | Entitas induk plus offset elemen |
| Kasus penggunaan umum | Mencocokkan kueri multi-vektor dengan entitas multi-vektor | Menemukan klip, gambar, bagian, patch, atau fakta yang paling relevan |
Untuk mendukung kedua mode dalam satu koleksi, definisikan dan indeks subbidang vektor yang terpisah. Bentuk kueri, keluarga metrik, dan indeks target harus selaras.
Pengindeksan EmbeddingList adalah keputusan kualitas-biaya
Dengan satu embedding per entitas, indeks ANN menemukan entitas di dekat vektor kueri. Pencarian EmbeddingList lebih mahal karena relevansi bergantung pada interaksi berpasangan antara dua daftar vektor.
Menghitung MaxSim eksak terhadap setiap vektor di setiap entitas menghasilkan peringkat referensi yang paling bersih, tetapi pemindaian penuh biasanya terlalu mahal untuk pengambilan daring. Karena itu, Milvus menggunakan model dua tahap:
- Strategi aproksimasi mengambil kandidat entitas induk.
- Ketika
emb_list_rerankdiaktifkan, Milvus menghitung ulang MaxSim pada kandidat tersebut untuk menghasilkan peringkat akhir.
Mengambil lebih banyak kandidat tahap pertama umumnya meningkatkan peluang hasil teratas yang sebenarnya mencapai reranker, tetapi juga meningkatkan latensi dan komputasi. Ketiga strategi terutama berbeda dalam cara mereka menghasilkan kumpulan kandidat tersebut.
| Strategi | Representasi kandidat tahap pertama | Titik awal yang baik ketika | Tradeoff utama |
|---|---|---|---|
| TokenANN | Mengindeks setiap vektor di setiap daftar embedding. Vektor kueri menjalankan ANN secara independen; kecocokan digabungkan kembali ke entitas induk sebelum reranking MaxSim. | Kualitas adalah prioritas, daftar pendek atau sedang, dan vektor individual bersifat diskriminatif. | Ukuran indeks dan kerja pencarian tahap pertama bertambah seiring panjang daftar dan jumlah vektor kueri. |
| MUVERA | Mengodekan setiap daftar embedding menjadi satu vektor berdimensi tetap melalui proyeksi acak, lalu menjalankan ANN biasa. | TokenANN terlalu berat dan kompresi tanpa pipeline pelatihan lebih disukai. | Pengodean kehilangan informasi; pengaturan proyeksi yang lebih kuat meningkatkan dimensi terenkode dan biaya ANN. |
| LEMUR | Melatih model yang memetakan daftar embedding menjadi vektor entitas induk berdimensi tetap. | Embedding kurang diskriminatif, daftar besar, atau beban kerja bersifat visual atau multimodal. | Membutuhkan pelatihan dan dapat sensitif terhadap distribusi korpus dan bias panjang dokumen. |
Tidak ada strategi tunggal yang terbaik untuk semua beban kerja. Mulailah dengan data target dan distribusi kueri:
- Gunakan TokenANN sebagai garis dasar yang mengutamakan kualitas ketika ukuran dataset memungkinkan.
- Coba MUVERA ketika indeks atau pengambilan kandidat TokenANN menjadi terlalu mahal seiring bertambahnya panjang daftar, dan Anda ingin menghindari pipeline pelatihan.
- Evaluasi LEMUR ketika ruang embedding berisik atau lemah secara diskriminatif, atau ketika beban kerja bersifat visual atau multimodal.
- Ukur recall atau nDCG bersama latensi dan ukuran indeks. Strategi yang bekerja untuk teks pendek dapat berperilaku berbeda dengan panjang dokumen ekor panjang atau ribuan patch visual.
StructArray menyelesaikan satu masalah: bagaimana merepresentasikan elemen pembawa vektor yang selaras dan dapat difilter di dalam satu entitas. Strategi EmbeddingList menyelesaikan masalah lain: bagaimana mengaproksimasi MaxSim dengan biaya yang dapat diterima untuk model dan korpus tertentu.
Pencarian hibrida membuat identitas hasil menjadi eksplisit
Pengambilan di lingkungan produksi jarang mengikuti satu jalur vektor. Permintaan video dapat menggabungkan embedding video tingkat atas, satu atau lebih embedding tingkat klip, sinyal keterangan atau transkrip, dan reranker.
Begitu kandidat tingkat elemen memasuki pipeline itu, mesin harus memutuskan apa yang mengidentifikasi kandidat akhir.
| Komposisi permintaan hibrida | Cakupan kandidat akhir | Identitas hasil |
|---|---|---|
| Semua sub-pencarian berada di tingkat elemen dan menargetkan subbidang vektor di bawah StructArray yang sama | Tingkat elemen | Kunci utama plus bidang StructArray plus offset elemen |
| Bidang vektor tingkat atas disertakan | Tingkat entitas | Kunci utama |
| Permintaan EmbeddingList disertakan | Tingkat entitas | Kunci utama |
| Permintaan tingkat elemen menargetkan bidang StructArray yang berbeda | Tingkat entitas | Kunci utama |
Konfigurasi pertama mempertahankan identitas elemen karena offset 3 merujuk ke elemen Struct yang sama untuk setiap sub-pencarian di bawah StructArray induk tertentu. Ini cocok untuk aplikasi yang ingin mengembalikan klip atau bagian yang paling relevan setelah menggabungkan beberapa sinyal tingkat elemen.
Konfigurasi lainnya mencampur granularitas kandidat atau namespace elemen. Oleh karena itu, hasil elemen harus digabungkan menjadi skor tingkat entitas sebelum reranking akhir. Milvus mendukung beberapa strategi penggabungan:
| Strategi penggabungan | Skor entitas dari hasil elemen yang dikembalikan | Kondisi penting |
|---|---|---|
max | Skor elemen terbaik | Bekerja dengan metrik vektor biasa yang didukung |
sum | Jumlah semua skor elemen yang dikembalikan | Gunakan dengan metrik berkorelasi positif seperti IP atau COSINE |
avg | Rata-rata skor elemen yang dikembalikan | Bekerja dengan metrik vektor biasa yang didukung |
topk_sum | Jumlah dari K skor elemen terbaik yang dikembalikan | Membutuhkan topk positif; gunakan dengan IP atau COSINE |
topk_avg | Rata-rata dari K skor elemen terbaik yang dikembalikan | Membutuhkan topk positif |
Penggabungan hanya beroperasi pada hasil elemen yang dikembalikan oleh sub-pencarian ANN tersebut; ia tidak memindai setiap elemen di entitas setelah pengambilan. Oleh karena itu, limit permintaan mengontrol hasil elemen mana yang tersedia untuk fungsi penggabungan.
Pilihan ini membentuk semantik pengambilan, bukan sekadar format keluaran. Jika aplikasi menampilkan klip atau bagian, mempertahankan offset melalui fusi adalah hal yang alami. Jika aplikasi menampilkan video, produk, atau dokumen, penggabungan tingkat entitas adalah hal yang alami. Ketika sinyal beroperasi pada granularitas yang berbeda, sistem membutuhkan aturan skoring elemen-ke-entitas yang eksplisit.
StructArray memindahkan masalah identitas-dan-penggabungan itu dari pemrosesan pasca ad hoc ke dalam model eksekusi pencarian.
Bagaimana Milvus mengeksekusi StructArray tanpa memperlakukannya sebagai gumpalan data
Model yang terlihat pengguna adalah ARRAY<STRUCT>. Namun, menyimpan seluruh nilai sebagai satu gumpalan buram akan membuat indeks subbidang, filter, dan keluaran selektif menjadi tidak efisien.
Milvus menggunakan desain induk-logis, anak-fisik-kolom (logical-parent, physical-child-column).
Di lapisan skema, clips adalah bidang induk logis. Ia mendefinisikan properti seperti skema Struct, kapasitas maksimum, dan kemampuan null. Subbidangnya dinormalisasi menjadi jalur seperti clips[clip_embedding_list], clips[clip_embedding], clips[scene_type], dan clips[label_confidence].
Subbidang skalar mengikuti jalur penyimpanan array skalar per entitas, sementara subbidang vektor mengikuti jalur array vektor. Setiap subbidang kemudian dapat menggunakan jalur data yang sesuai dengan tipenya: pemfilteran skalar dan indeks skalar untuk metadata, serta indeks vektor dan pencarian ANN untuk embedding.
Saat penyerapan, Proxy memperluas daftar Struct bersarang menjadi kolom anak bertipe. Selama eksekusi, Milvus menjaga hubungan antara setiap elemen fisik dan entitas induknya. Secara konseptual, hubungan itu terlihat seperti ini:
Plaintext
entity 0 -> elements [0, 1, 2]
entity 1 -> elements [3]
entity 2 -> elements []
entity 3 -> elements [4, 5, 6, 7]
Ketika pencarian tingkat elemen mengembalikan ID elemen fisik, Milvus memetakannya kembali ke entitas induk dan offset elemen. Ketika element_filter menghasilkan bitmap tingkat elemen, mesin menyelaraskannya dengan visibilitas entitas induk, penghapusan, dan filter lainnya.
Saat mengembalikan hasil, Milvus menggunakan skema logis dan offset bersama untuk merekonstruksi bentuk StructArray yang disisipkan aplikasi. Sistem dapat mengeksekusi di atas kolom anak bertipe sementara pengguna terus membaca dan menulis objek bersarang yang alami. Tata letak fisik ini membuat StructArray lebih dari sekadar JSON bertipe: hubungan bersarang berpartisipasi dalam model indeks dan eksekusi.
Di mana StructArray cocok, dan di mana tidak
StructArray sangat cocok ketika semua hal berikut benar:
- Aplikasi memiliki entitas induk yang bermakna, seperti video, produk, dokumen, halaman visual, atau catatan memori.
- Setiap induk berisi kumpulan elemen lokal yang terurut dan panjangnya bervariasi.
- Elemen-elemen tersebut membutuhkan metadata skalar, vektor, atau keduanya.
- Pencarian atau pemfilteran harus menjaga hubungan antara subbidang pada offset elemen yang sama.
- Aplikasi membutuhkan pengambilan multi-vektor tingkat entitas, hasil tingkat elemen, atau keduanya.
StructArray tidak otomatis lebih baik untuk setiap koleksi. Dokumen pendek atau kueri sederhana mungkin sudah cukup dilayani oleh satu embedding padat. Pengindeksan multi-vektor menambah biaya penyimpanan dan pencarian, sehingga representasi tambahan harus membuktikan nilainya melalui peningkatan kualitas pengambilan atau granularitas hasil yang lebih berguna.
Batasan skema dan eksekusi saat ini juga penting:
Structdidukung sebagai tipe elemen dariArray, bukan sebagai bidang koleksi tingkat atas.- Semua elemen dalam satu StructArray berbagi satu skema yang telah ditentukan.
max_capacitywajib diisi dan membatasi jumlah elemen per entitas.- Subbidang
Struct,Array,ArrayOfStruct, danJSONbersarang tidak didukung di dalam StructArray. - Subbidang vektor menerima satu indeks. Gunakan subbidang vektor terpisah untuk pencarian EmbeddingList dan tingkat elemen ketika keduanya diperlukan.
- Subbidang vektor harus diindeks sebelum pencarian. Subbidang skalar yang banyak digunakan dalam filter harus diindeks dengan tepat.
- Skema subbidang ditetapkan setelah bidang StructArray dibuat, jadi rencanakan atribut elemen sebelum peluncuran produksi.
Batasan-batasan ini membuat model lebih sempit daripada penyarangan arbitrer basis data dokumen, tetapi juga memberi Milvus struktur yang cukup untuk bernalar tentang identitas elemen, mengindeks setiap subbidang, dan mengeksekusi pada dua granularitas pencarian.
StructArray menjaga bukti lokal sebagai warga kelas satu tanpa kehilangan entitas
StructArray memberi Milvus objek pengambilan yang sulit direpresentasikan oleh skema datar: entitas induk dengan kumpulan elemen terstruktur yang terurut. Hubungan antara elemen-elemen tersebut berpartisipasi dalam pemfilteran, pengindeksan, dan pencarian, bukan hanya ada di penyimpanan.
Setiap elemen mempertahankan metadata dan embedding-nya sendiri. Elemen-elemen tersebut dapat memenuhi predikat skalar elemen yang sama, berpartisipasi bersama dalam pencarian EmbeddingList tingkat entitas, atau bersaing secara independen dalam pencarian tingkat elemen. Pada saat yang sama, mereka tetap terikat pada entitas induk yang metadata, izin, dan identitas aplikasinya memberi mereka konteks.
Untuk klip video, gambar produk, bagian dokumen, patch visual, dan fragmen memori, bukti lokal dapat dicari dan difilter tanpa kehilangan entitas tempatnya berada. Pilihan desain yang tersisa bersifat eksplisit: pilih granularitas pencarian, berikan metrik dan indeks yang cocok untuk setiap subbidang vektor, dan putuskan apakah hasil hibrida harus mempertahankan offset elemen atau digabungkan kembali ke entitas.
Coba StructArray di Milvus 3.0
StructArray tersedia di Milvus 3.0. Mulailah dengan Ringkasan StructArray. Jika Anda mengevaluasi pengambilan multi-vektor tingkat entitas, baca panduan strategi EmbeddingList. Untuk granularitas hasil dan perilaku penggabungan, lihat Pencarian Hibrida dengan StructArray.
Untuk konteks rilis yang lebih luas, lihat blog peluncuran Milvus 3.0, catatan rilis, dan repositori milvus-io/milvus.
Zilliz Cloud juga mendukung StructArray dan pencarian EmbeddingList untuk deployment terkelola. Tinjau panduan StructArray Zilliz Cloud untuk batasan khusus layanan. Di Zilliz Cloud, operator skalar pada StructArray saat ini didokumentasikan untuk klaster On-Demand.
Untuk mendiskusikan desain skema atau pengambilan dengan tim, bergabunglah dengan komunitas Discord Milvus atau pesan sesi Milvus Office Hours.
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



