Eine Entität, viele Vektoren: Suche auf Entitäts- und Elementebene mit Milvus 3.0 StructArray

  • Engineering
August 19, 2026
Chenjie Tang

Die meisten Schemata für Vektor-Datenbanken gehen von einer einfachen Annahme aus: eine Entität, ein Embedding. Ein Produkt erhält einen Vektor, ebenso ein Dokument. Eine Benutzerabfrage wird eingebettet und über eine Suche nach ungefähren nächsten Nachbarn (ANN) mit diesen Vektoren verglichen. Dieses Modell funktioniert für die erste Generation von Vektorsuch-Anwendungsfällen, darunter RAG, semantische Suche und Empfehlungssysteme.

Reale KI-Daten passen jedoch selten in diese Annahme. Ein Video enthält Clips, Einstellungen oder Einzelbilder (Keyframes), jeweils mit eigenem Embedding, Zeitbereich, Bildunterschrift, Szenenlabel und Konfidenzwert. Ein Produkt kann mehrere Bilder und Blickwinkel haben. Ein langes Dokument enthält Passagen oder Abschnitte, deren lokale Bedeutung wichtiger sein kann als ein einzelnes Embedding des gesamten Dokuments. Beliebte Late-Interaction-Modelle zeigen dieselbe Einschränkung in noch feinerer Granularität: ColBERT erzeugt einen Vektor pro Token, ColPali einen Vektor pro visuellem Patch.

In jedem Fall bleibt die übergeordnete Entität die Einheit, die die Anwendung speichert, anzeigt, absichert und zurückgibt. Doch Relevanz, Filterung und Ergebnis-Erklärung hängen oft von Elementen innerhalb dieser Entität ab.

Mit dem neuen StructArray-Feature erhält Milvus ein natives Datenmodell für diese Form: Eine Entität enthält ein geordnetes Array von schema-definierten Struct-Elementen, und jedes Element kann skalare Metadaten, Vektor-Embeddings oder beides tragen. Milvus kann Felder filtern, die zum selben Element gehören, zwei Embedding-Listen auf Entitätsebene vergleichen oder einzelne Elemente durchsuchen und den passenden Offset zurückgeben.

Dieser Artikel verwendet ein Video-Suchbeispiel, um das Datenmodell zu erklären, und führt es dann durch Schema-Design, Filterung, Granularitäten der Vektorsuche, EmbeddingList-Indexstrategien, Hybrid-Ergebnis-Kollaps und das physische Layout, das das Feature ausführbar macht.

Warum ein Modell mit einem Vektor und einer flachen Zeile nicht mehr ausreicht

Stellen Sie sich eine Benutzerin vor, die in einem Videokatalog nach „eine Person, die in einer Küche Gemüse schneidet“ sucht. Das relevante Signal kann in einem acht Sekunden langen Clip liegen, nicht in einem Embedding des gesamten Videos. Jeden Clip, jedes Objekt und jede Aktion in einen einzigen Vektor zu komprimieren, kann zwar das allgemeine Thema bewahren, verwischt aber lokale Details.

Dieselbe Diskrepanz tritt auch in anderen Workloads auf:

  • Die Relevanz eines Produkts kann von einem von mehreren Bildern oder Blickwinkeln abhängen.
  • Ein Dokument kann wegen einer einzigen Passage übereinstimmen, nicht wegen seines Gesamtthemas.
  • Ein Agent-Speicher kann mehrere Beobachtungen enthalten, von denen nur eine für die aktuelle Aufgabe relevant ist.
  • Ein ColBERT- oder ColPali-Datensatz enthält eine Liste variabler Länge mit Token- bzw. Patch-Vektoren statt eines einzelnen dichten Vektors.

Eine Alternative besteht darin, jeden Clip, jedes Bild oder jede Passage in eine separate Datenbankzeile zu zerlegen. Das ermöglicht lokale Suche, trennt aber jedes Fragment von seiner übergeordneten Entität. Die Metadaten der übergeordneten Entität können über mehrere Zeilen hinweg wiederholt werden, und das Abrufen auf Entitätsebene erfordert dann Gruppierung, Deduplizierung und erneutes Ranking nach der Fragmentsuche.

Verschachtelte Speicherung allein löst das Abfrageproblem nicht. JSON kann Objekte speichern, gibt Milvus aber kein vordefiniertes Unterfeld-Schema für Vektor- und Skalar-Indizierung. Parallele Arrays können Bildunterschriften, Szenenlabels und Konfidenzwerte speichern, aber die Anwendung muss die Offset-Ausrichtung selbst aufrechterhalten. Die Datenbank kann nicht sicher ableiten, dass scene_type[3] und label_confidence[3] denselben Clip beschreiben, wenn diese Beziehung nicht Teil des Datenmodells ist.

StructArray kodiert diese Beziehung direkt. Es hält lokale Elemente innerhalb der übergeordneten Entität und macht ihre ausgerichteten Unterfelder für Schema-Validierung, Indizierung, Filterung und Vektorsuche zugänglich.

Was ist StructArray und wie sieht sein Datenmodell aus?

Ein StructArray – auch bekannt als Array von Structs – speichert in jeder Entität eine geordnete Menge von Struct-Elementen. Ein StructArray-Feld ist ein Array, dessen Elemente alle einem vordefinierten Struct-Schema folgen. Für eine Video-Collection könnte die logische Form so aussehen:

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

Hier gilt:

  • clips ist das übergeordnete StructArray-Feld.
  • clip_embedding_list, clip_embedding, start_sec und die anderen Attribute sind Unterfelder.
  • clips[0] ist der erste Clip.
  • Jedes Unterfeld bei Offset 0 gehört zu demselben Clip.
  • Jedes Unterfeld bei Offset 3 gehört zu einem anderen Clip.

Die beiden Vektor-Unterfelder dienen unterschiedlichen Suchmodi. clips[clip_embedding_list] wird mit einer MAX_SIM*-Metrik für die EmbeddingList-Suche auf Entitätsebene indiziert, während clips[clip_embedding] mit einer regulären Vektor-Metrik für die Suche auf Elementebene indiziert wird. Da ein Vektorfeld oder Vektor-Unterfeld nur einen Index akzeptiert, muss eine Collection, die beide Modi benötigt, die beiden Unterfelder getrennt definieren und indizieren.

Dieses Modell unterstützt drei unterschiedliche Abfragesemantiken.

1. EmbeddingList-Suche gibt übergeordnete Entitäten zurück

Die Vektoren in clips[clip_embedding_list] bilden eine Embedding-Liste für das Video. Auch die Abfrage ist eine EmbeddingList. Milvus vergleicht die Abfrageliste mit jeder gespeicherten Liste über eine MAX_SIM*-Metrik und liefert ein Ergebnis auf Entitätsebene.

Plaintext
clips[clip_embedding_list] = [
    embedding_0,
    embedding_1,
    embedding_2,
    ...
]

2. Die MATCH_*-Familie filtert übergeordnete Entitäten

MATCH_ANY, MATCH_ALL, MATCH_LEAST, MATCH_MOST und MATCH_EXACT werten ein Prädikat gegen Struct-Elemente aus, zählen, wie viele Elemente es erfüllen, und entscheiden, ob die übergeordnete Entität den Filter passiert.

Zum Beispiel:

Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)

Beide Skalarbedingungen müssen beim selben Clip-Offset erfüllt sein. Milvus kombiniert nicht ein Küchenlabel aus einem Clip mit einem hohen Konfidenzwert aus einem anderen.

3. Suche auf Elementebene gibt den Offset des passenden Elements zurück

Ein regulärer Abfragevektor kann jeden Vektor in clips[clip_embedding] unabhängig durchsuchen. Jeder Treffer identifiziert die übergeordnete Entität und den nullbasierten Offset des passenden Struct-Elements. Ein element_filter kann einschränken, welche Elemente an dieser Vektorsuche teilnehmen.

Diese Operationen teilen eine gemeinsame Grundannahme: Milvus weiß, welche Vektor- und Skalarwerte zum selben Element gehören und welche Elemente zur selben Entität gehören.

StructArray ist kein allgemeines System für beliebige Verschachtelungen. Das aktuelle Modell ist ein Array aus Struct-Elementen mit unterstützten Skalar- und Vektor-Unterfeldern. Diese Grenze macht Unterfeld-Indizierung und elementbewusste Ausführung praktikabel.

Schema, Indizes und Insert-Pfad aufbauen

Das folgende vereinfachte PyMilvus-Beispiel erstellt eine Video-Collection mit einem Top-Level-Vektor und einem StructArray für Clips. Es verwendet getrennte Clip-Vektor-Unterfelder, damit dieselbe Collection beide Suchmodi demonstrieren kann.

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)

Vektor-Unterfelder müssen vor der Suche indiziert werden. Da die Metrik-Familie den Suchmodus bestimmt, erhält jedes Vektor-Unterfeld seinen eigenen Index:

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)

Skalar-Indizes sind optional, aber Unterfelder, die häufig in Filterung auf großer Skala vorkommen, sollten einen kompatiblen Skalar-Index verwenden. Zum Beispiel kann clips[scene_type] einen invertierten Index verwenden, während ein numerisches Unterfeld wie clips[label_confidence] einen für numerische Filterung geeigneten Index nutzen kann.

Fügen Sie Daten in ihrer natürlichen Entitätsform ein: eine Videzeile mit einem Array aus Clip-Objekten. Um das Beispiel kompakt zu halten, schreibt es denselben Clip-Vektor in beide Vektor-Unterfelder.

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”)

An der API-Grenze bleibt clips ein Array aus strukturierten Objekten. Innerhalb von Milvus folgt jedes Unterfeld dem typisierten Pfad, der für seinen eigenen Index, seine Filterung und sein Ausgabeverhalten erforderlich ist. Dieser Unterschied ist beim Einfügen transparent, aber für alles Weitere fundamental.

Gleich-Element-Filterung ist der Unterschied zwischen Struktur und parallelen Arrays

Der Hauptvorteil der Filterung liegt nicht in kürzerer Syntax für verschachtelte Felder. Es geht um die korrekte Korrelation zwischen Skalar-Unterfeldern.

Angenommen, die Anwendung benötigt Videos, die einen Küchenclip mit Label-Konfidenz über 0.8 enthalten. Es reicht nicht, dass ein Video irgendeinen Küchenclip und irgendeinen Clip mit hoher Konfidenz enthält; derselbe Clip muss beide Bedingungen erfüllen.

Die StructArray-MATCH_*-Familie drückt das direkt aus:

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 wertet das Prädikat bei jedem Element-Offset aus und wendet dann den Quantor des Operators an, um zu entscheiden, ob die übergeordnete Entität den Filter passiert:

  • MATCH_ANY: Mindestens ein Element erfüllt die Bedingung.
  • MATCH_ALL: Jedes Element erfüllt die Bedingung.
  • MATCH_LEAST: Mindestens threshold Elemente erfüllen die Bedingung.
  • MATCH_MOST: Höchstens threshold Elemente erfüllen die Bedingung.
  • MATCH_EXACT: Genau threshold Elemente erfüllen die Bedingung.

Wären dieselben Daten als zwei unabhängige Arrays gespeichert, würde der folgende Ausdruck diese Korrelation nicht bewahren:

Plaintext
array_contains(clips[scene_type], "kitchen")
AND
array_contains(clips[label_confidence], 0.9)

Die beiden Werte könnten bei unterschiedlichen Offsets liegen. Das kann für unabhängige Attribute zulässig sein, ist aber falsch, wenn beide Bedingungen denselben Clip, dasselbe Produktbild oder dieselbe Dokumentpassage beschreiben.

StructArray macht die Elementidentität zu einem Teil des Datenbank-Prädikats statt zu einer Konvention, die die Anwendung durchsetzen muss.

Zwei Granularitäten der Vektorsuche, zwei Ergebnisidentitäten

Sobald eine Entität mehrere Vektoren speichert, muss das Abrufsystem eine Modellierungsfrage klären, bevor die ANN-Suche beginnt:

Sollen die Vektoren gemeinsam als eine Repräsentation der übergeordneten Entität bewertet werden, oder soll jeder Elementvektor unabhängig konkurrieren?

StructArray unterstützt beide Modelle, aber sie verwenden unterschiedliche Abfrageformen, Metrik-Familien, Vektor-Unterfelder und Ergebnisidentitäten.

EmbeddingList-Suche: Eine Liste von Abfragevektoren findet eine Entität

Eine EmbeddingList-Abfrage enthält mehrere Vektoren. Ein Abfragevideo kann in mehrere Clips unterteilt sein; eine Produktabfrage kann mehrere Referenzbilder enthalten; eine ColBERT-Abfrage enthält einen Vektor pro Abfrage-Token.

Für jede Entität vergleicht Milvus die Abfrageliste mit der gespeicherten Embedding-Liste der Entität. Bei der MaxSim-basierten Bewertung wählt jeder Abfragevektor seine beste Übereinstimmung in der Entitätsliste, und Milvus aggregiert diese besten Übereinstimmungswerte zu einem Entitäts-Score. Der endgültige Treffer repräsentiert die übergeordnete Entität, nicht ein bestimmtes Struct-Element.

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, )

Diese Suche beantwortet: Welche Videos sind die beste Gesamtübereinstimmung für diese Menge von Abfrage-Clips?

Sie passt zu Video-zu-Video-Retrieval, Multi-Bild-Produktsuche, ColBERT- und ColPali-basiertem Retrieval sowie anderen Fällen, in denen sowohl die Abfrage als auch die gespeicherte Entität durch mehrere Vektoren repräsentiert werden.

Suche auf Elementebene: Ein Abfragevektor findet einen Clip innerhalb einer Entität

Die Suche auf Elementebene verwendet einen regulären Abfragevektor. Jeder Vektor in clips[clip_embedding] nimmt als unabhängiger Kandidat an der ANN-Suche teil. Jeder Treffer identifiziert die übergeordnete Entität und den Offset des passenden Elements.

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"],
)

Um nur ausgewählte Clips zu durchsuchen, hängen Sie einen element_filter an, dessen Skalarbedingungen auf denselben Clip angewendet werden:

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"],
)

Der Filter wählt nicht zuerst einen Küchenclip aus und durchsucht dann einen anderen Clip mit hoher Konfidenz. Sowohl die Prädikate als auch der Vektorkandidat beziehen sich auf dasselbe Struct-Element.

Eine nicht gruppierte Antwort kann so aussehen:

Plaintext
id = 1, offset = 1, distance = 0.91
id = 8, offset = 4, distance = 0.88
id = 1, offset = 3, distance = 0.84

Dieselbe Entität kann mehrfach erscheinen, weil mehrere Clips übereinstimmen können. Das ist nützlich, wenn die Anwendung nicht nur zeigen muss, welches Video oder Dokument relevant ist, sondern auch, welcher Clip oder welche Passage den Treffer erzeugt hat.

AspektEmbeddingList-SucheSuche auf Elementebene
AbfrageeingabeEin oder mehrere Abfragevektoren in einer EmbeddingListEin regulärer Abfragevektor
Beispiel-Zielclips[clip_embedding_list]clips[clip_embedding]
Metrik-FamilieMAX_SIM*Reguläre Metriken wie COSINE, IP oder L2
ANN-KandidateneinheitDie Embedding-Liste der übergeordneten EntitätJeder Struct-Elementvektor
ErgebnisidentitätÜbergeordnete EntitätÜbergeordnete Entität plus Element-Offset
Typischer AnwendungsfallEine Multi-Vektor-Abfrage gegen eine Multi-Vektor-Entität abgleichenDen relevantesten Clip, das relevanteste Bild, die relevanteste Passage, den relevantesten Patch oder Fakt finden

Um beide Modi in einer Collection zu unterstützen, definieren und indizieren Sie getrennte Vektor-Unterfelder. Abfrageform, Metrik-Familie und Zielindex müssen übereinstimmen.

EmbeddingList-Indizierung ist eine Qualitäts-Kosten-Entscheidung

Mit einem Embedding pro Entität findet ein ANN-Index Entitäten in der Nähe eines Abfragevektors. Die EmbeddingList-Suche ist teurer, weil die Relevanz von paarweisen Interaktionen zwischen zwei Vektorlisten abhängt.

Die Berechnung von exaktem MaxSim gegen jeden Vektor in jeder Entität erzeugt das sauberste Referenz-Ranking, aber ein vollständiger Scan ist für Online-Retrieval meist zu teuer. Milvus verwendet daher ein zweistufiges Modell:

  1. Eine approximative Strategie gewinnt Kandidaten-Entitäten auf übergeordneter Ebene.
  2. Wenn emb_list_rerank aktiviert ist, berechnet Milvus MaxSim über diese Kandidaten neu, um das endgültige Ranking zu erzeugen.

Mehr Kandidaten in der ersten Stufe abzurufen erhöht in der Regel die Chance, dass die wahren Top-Ergebnisse den Reranker erreichen, erhöht aber auch Latenz und Rechenaufwand. Die drei Strategien unterscheiden sich hauptsächlich darin, wie sie diese Kandidatenmenge erzeugen.

StrategieKandidatenrepräsentation in der ersten StufeGuter Ausgangspunkt, wennHauptabwägung
TokenANNJeder Vektor in jeder Embedding-Liste wird indiziert. Abfragevektoren führen ANN unabhängig aus; Treffer werden vor dem MaxSim-Reranking wieder zu übergeordneten Entitäten aggregiert.Qualität Priorität hat, die Listen kurz oder mittellang sind und einzelne Vektoren diskriminativ sind.Indexgröße und Suchaufwand in der ersten Stufe wachsen mit der Listengröße und der Anzahl der Abfragevektoren.
MUVERAJede Embedding-Liste wird durch Zufallsprojektionen in einen festdimensionalen Vektor kodiert, dann wird gewöhnliche ANN ausgeführt.TokenANN zu schwer ist und Kompression ohne Trainingspipeline bevorzugt wird.Die Kodierung verliert Informationen; stärkere Projektionseinstellungen erhöhen die kodierte Dimensionalität und die ANN-Kosten.
LEMUREin Modell wird trainiert, das eine Embedding-Liste auf einen festdimensionalen Vektor der übergeordneten Entität abbildet.Embeddings weniger diskriminativ sind, Listen groß sind oder der Workload visuell oder multimodal ist.Es erfordert Training und kann empfindlich auf Korpusverteilung und Dokumentlängen-Bias reagieren.

Keine Strategie ist für jeden Workload am besten. Beginnen Sie mit der Zieldaten- und Abfrageverteilung:

  • Verwenden Sie TokenANN als qualitätsorientierte Baseline, wenn die Datensatzgröße es erlaubt.
  • Probieren Sie MUVERA aus, wenn TokenANNs Index oder Kandidatengewinnung mit wachsender Listengröße zu teuer wird und Sie eine Trainingspipeline vermeiden möchten.
  • Bewerten Sie LEMUR, wenn der Embedding-Raum verrauscht oder schwach diskriminativ ist oder wenn der Workload visuell oder multimodal ist.
  • Messen Sie Recall oder nDCG zusammen mit Latenz und Indexgröße. Eine Strategie, die für kurze Texte funktioniert, kann sich bei langen Dokumentlängen oder Tausenden visueller Patches anders verhalten.

StructArray löst ein Problem: wie man ausgerichtete, filterbare, vektortragende Elemente innerhalb einer einzelnen Entität darstellt. Die EmbeddingList-Strategie löst ein anderes: wie man MaxSim zu akzeptablen Kosten für ein bestimmtes Modell und einen bestimmten Korpus approximiert.

Hybrid-Suche macht die Ergebnisidentität explizit

Produktionsnahes Retrieval folgt selten einem einzigen Vektorpfad. Eine Videoanfrage kann ein Top-Level-Video-Embedding, ein oder mehrere Clip-Level-Embeddings, ein Bildunterschriften- oder Transkriptsignal und einen Reranker kombinieren.

Sobald Kandidaten auf Elementebene in diese Pipeline gelangen, muss die Engine entscheiden, was einen endgültigen Kandidaten identifiziert.

Zusammensetzung der Hybrid-AnfrageBereich des endgültigen KandidatenErgebnisidentität
Alle Teil-Suchen sind auf Elementebene und zielen auf Vektor-Unterfelder unter demselben StructArrayElementebenePrimärschlüssel plus StructArray-Feld plus Element-Offset
Ein Top-Level-Vektorfeld ist enthaltenEntitätsebenePrimärschlüssel
Eine EmbeddingList-Anfrage ist enthaltenEntitätsebenePrimärschlüssel
Element-Level-Anfragen zielen auf verschiedene StructArray-FelderEntitätsebenePrimärschlüssel

Die erste Konfiguration bewahrt die Elementidentität, weil Offset 3 für jede Teil-Suche unter einem gegebenen übergeordneten StructArray auf dasselbe Struct-Element verweist. Das passt zu einer Anwendung, die nach der Fusion mehrerer Element-Level-Signale den relevantesten Clip oder die relevanteste Passage zurückgeben möchte.

Die anderen Konfigurationen mischen Kandidatengranularitäten oder Element-Namensräume. Ein Element-Treffer muss daher vor dem endgültigen Reranking zu einem Score auf Entitätsebene kollabiert werden. Milvus unterstützt mehrere Kollaps-Strategien:

Kollaps-StrategieEntitäts-Score aus den zurückgegebenen Element-TreffernWichtige Bedingung
maxBester Element-ScoreFunktioniert mit unterstützten regulären Vektor-Metriken
sumSumme aller zurückgegebenen Element-ScoresMit positiv korrelierten Metriken wie IP oder COSINE verwenden
avgDurchschnitt der zurückgegebenen Element-ScoresFunktioniert mit unterstützten regulären Vektor-Metriken
topk_sumSumme der besten K zurückgegebenen Element-ScoresErfordert ein positives topk; mit IP oder COSINE verwenden
topk_avgDurchschnitt der besten K zurückgegebenen Element-ScoresErfordert ein positives topk

Der Kollaps arbeitet nur auf den Element-Treffern, die von dieser ANN-Teil-Suche zurückgegeben werden; er scannt nicht jedes Element in der Entität nach dem Abruf. Das limit der Anfrage steuert daher, welche Element-Treffer der Kollaps-Funktion zur Verfügung stehen.

Diese Wahl prägt die Retrieval-Semantik, nicht nur die Ausgabeformatierung. Wenn die Anwendung einen Clip oder eine Passage präsentiert, ist es natürlich, den Offset durch die Fusion zu bewahren. Wenn sie ein Video, Produkt oder Dokument präsentiert, ist der Kollaps auf Entitätsebene natürlich. Wenn Signale auf unterschiedlichen Granularitätsebenen arbeiten, benötigt das System eine explizite Element-zu-Entität-Bewertungsregel.

StructArray verschiebt dieses Identitäts- und Kollaps-Problem von ad hoc Nachbearbeitung in das Suchausführungsmodell.

Wie Milvus StructArray ausführt, ohne es als Blob zu behandeln

Das benutzerseitige Modell ist ARRAY<STRUCT>. Den gesamten Wert jedoch als einen undurchsichtigen Blob zu speichern, würde Unterfeld-Indizes, Filterung und selektive Ausgabe ineffizient machen.

Milvus verwendet ein Design mit logischem Elternfeld und physischen Kind-Spalten.

Auf Schemaebene ist clips das logische Elternfeld. Es definiert Eigenschaften wie das Struct-Schema, die maximale Kapazität und die Nullbarkeit. Seine Unterfelder werden in Pfade wie clips[clip_embedding_list], clips[clip_embedding], clips[scene_type] und clips[label_confidence] normalisiert.

Skalar-Unterfelder folgen pro Entität den Speicherpfaden für Skalar-Arrays, während Vektor-Unterfelder den Pfaden für Vektor-Arrays folgen. Jedes Unterfeld kann dann den für seinen Typ geeigneten Datenpfad nutzen: Skalar-Filterung und Skalar-Indizes für Metadaten, Vektor-Indizes und ANN-Suche für Embeddings.

Beim Ingest expandiert der Proxy die verschachtelte Struct-Liste in typisierte Kind-Spalten. Während der Ausführung hält Milvus die Beziehung zwischen jedem physischen Element und seiner übergeordneten Entität aufrecht. Konzeptionell sieht diese Beziehung so aus:

Plaintext
entity 0 -> elements [0, 1, 2]
entity 1 -> elements [3]
entity 2 -> elements []
entity 3 -> elements [4, 5, 6, 7]

Wenn die Suche auf Elementebene eine physische Element-ID zurückgibt, bildet Milvus sie auf die übergeordnete Entität und den Element-Offset ab. Wenn element_filter eine Bitmap auf Elementebene erzeugt, richtet die Engine sie mit Sichtbarkeit der übergeordneten Entität, Löschungen und anderen Filtern ab.

Beim Zurückgeben von Ergebnissen verwendet Milvus das logische Schema und gemeinsame Offsets, um die StructArray-Form zu rekonstruieren, die die Anwendung eingefügt hat. Das System kann über typisierte Kind-Spalten ausführen, während die Benutzerin weiterhin natürliche verschachtelte Objekte liest und schreibt. Dieses physische Layout macht StructArray zu mehr als typisiertem JSON: Die verschachtelte Beziehung nimmt am Index- und Ausführungsmodell teil.

Wo StructArray passt – und wo nicht

StructArray ist eine gute Wahl, wenn alle folgenden Punkte zutreffen:

  • Die Anwendung hat eine bedeutungsvolle übergeordnete Entität, wie ein Video, Produkt, Dokument, eine visuelle Seite oder einen Speichereintrag.
  • Jede übergeordnete Entität enthält eine geordnete Menge variabler Länge von lokalen Elementen.
  • Diese Elemente benötigen eigene skalare Metadaten, Vektoren oder beides.
  • Suche oder Filterung muss die Beziehung zwischen Unterfeldern beim selben Element-Offset bewahren.
  • Die Anwendung benötigt Multi-Vektor-Retrieval auf Entitätsebene, Treffer auf Elementebene oder beides.

StructArray ist nicht automatisch für jede Collection besser. Ein kurzes Dokument oder eine einfache Abfrage kann mit einem einzigen dichten Embedding gut bedient sein. Multi-Vektor-Indizierung fügt Speicher- und Suchkosten hinzu, daher sollte die zusätzliche Repräsentation ihren Platz durch verbesserte Retrieval-Qualität oder nützlichere Ergebnisgranularität verdienen.

Auch die aktuellen Schema- und Ausführungsgrenzen sind wichtig:

  • Struct wird als Elementtyp eines Array unterstützt, nicht als Top-Level-Collection-Feld.
  • Alle Elemente in einem StructArray teilen sich ein vordefiniertes Schema.
  • max_capacity ist erforderlich und begrenzt die Anzahl der Elemente pro Entität.
  • Verschachtelte Struct-, Array-, ArrayOfStruct- und JSON-Unterfelder werden innerhalb eines StructArray nicht unterstützt.
  • Ein Vektor-Unterfeld akzeptiert einen Index. Verwenden Sie getrennte Vektor-Unterfelder für EmbeddingList- und Element-Level-Suche, wenn beide benötigt werden.
  • Vektor-Unterfelder müssen vor der Suche indiziert werden. Skalar-Unterfelder, die stark in Filtern verwendet werden, sollten angemessen indiziert werden.
  • Das Unterfeld-Schema ist nach der Erstellung des StructArray-Felds festgelegt. Planen Sie die Elementattribute daher vor dem Produktions-Rollout.

Diese Einschränkungen machen das Modell enger als die beliebige Verschachtelung einer Dokumentdatenbank, geben Milvus aber genug Struktur, um über Elementidentität zu argumentieren, jedes Unterfeld zu indizieren und auf zwei Suchgranularitäten auszuführen.

StructArray macht lokale Evidenz erstklassig, ohne die Entität zu verlieren

StructArray gibt Milvus ein Retrieval-Objekt, das flache Schemata nur schwer darstellen können: eine übergeordnete Entität mit einer geordneten Menge strukturierter Elemente. Die Beziehungen zwischen diesen Elementen nehmen an Filterung, Indizierung und Suche teil, statt nur im Speicher zu existieren.

Jedes Element behält seine eigenen Metadaten und Embeddings. Die Elemente können Skalar-Prädikate auf demselben Element erfüllen, gemeinsam an der EmbeddingList-Suche auf Entitätsebene teilnehmen oder unabhängig in der Suche auf Elementebene konkurrieren. Gleichzeitig bleiben sie an die übergeordnete Entität gebunden, deren Metadaten, Berechtigungen und Anwendungsidentität ihnen Kontext geben.

Für Video-Clips, Produktbilder, Dokumentpassagen, visuelle Patches und Speicherfragmente kann lokale Evidenz durchsucht und gefiltert werden, ohne die Entität zu verlieren, zu der sie gehört. Die verbleibenden Designentscheidungen sind explizit: Wählen Sie die Suchgranularität, geben Sie jedem Vektor-Unterfeld die passende Metrik und den passenden Index, und entscheiden Sie, ob Hybrid-Ergebnisse Element-Offsets bewahren oder zu Entitäten kollabieren sollen.

StructArray in Milvus 3.0 ausprobieren

StructArray ist in Milvus 3.0 verfügbar. Beginnen Sie mit der StructArray-Übersicht. Wenn Sie Multi-Vektor-Retrieval auf Entitätsebene evaluieren, lesen Sie den EmbeddingList-Strategieleitfaden. Für Ergebnisgranularität und Kollaps-Verhalten siehe Hybrid Search mit StructArray.

Für den breiteren Release-Kontext siehe den Milvus-3.0-Launch-Blog, die Release Notes und das milvus-io/milvus-Repository.

Zilliz Cloud unterstützt ebenfalls StructArray- und EmbeddingList-Suche für verwaltete Deployments. Prüfen Sie den Zilliz-Cloud-StructArray-Leitfaden für dienstspezifische Grenzen. In Zilliz Cloud sind Skalar-Operatoren auf StructArray derzeit für On-Demand-Cluster dokumentiert.

Um ein Schema- oder Retrieval-Design mit dem Team zu besprechen, treten Sie der Milvus-Discord-Community bei oder buchen Sie eine Milvus-Office-Hours-Sitzung.

    Try Managed Milvus for Free

    Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

    Get Started

    Like the article? Spread the word

    Weiterlesen