Uma Entidade, Muitos Vetores: Busca em Nível de Entidade e Elemento com o StructArray do Milvus 3.0
A maioria dos esquemas de bancos de dados vetoriais parte de uma premissa simples: uma entidade, um embedding. Um produto recebe um vetor, assim como um documento. Uma consulta de usuário é convertida em um embedding e comparada com esses vetores por meio da busca por vizinhos mais próximos aproximados (ANN). Esse modelo funciona para a primeira geração de casos de uso da busca vetorial, incluindo RAG, busca semântica e sistemas de recomendação.
No entanto, dados reais de IA raramente se encaixam nessa premissa. Um vídeo contém clipes, cenas ou keyframes, cada um com seu próprio embedding, intervalo de tempo, legenda, rótulo de cena e pontuação de confiança. Um produto pode ter várias imagens e ângulos de visualização. Um documento longo contém passagens ou seções cujo significado local importa mais do que uma representação única do documento inteiro. Modelos populares de interação tardia expõem a mesma limitação em uma granularidade ainda mais fina: o ColBERT gera um vetor por token, enquanto o ColPali gera um vetor por patch visual.
Em cada caso, a entidade pai permanece como a unidade que a aplicação armazena, exibe, protege e retorna. No entanto, relevância, filtragem e explicação de resultados muitas vezes dependem de elementos dentro dessa entidade.
O novo recurso StructArray oferece ao Milvus um modelo de dados nativo para esse formato: uma entidade contém um array ordenado de elementos Struct definidos por esquema, e cada elemento pode carregar metadados escalares, embeddings vetoriais, ou ambos. O Milvus pode filtrar campos que pertencem ao mesmo elemento, comparar duas listas de embeddings no nível da entidade ou pesquisar elementos individuais e retornar o offset correspondente.
Este artigo usa um exemplo de busca de vídeos para explicar o modelo de dados e, em seguida, percorre o design do esquema, a filtragem, as granularidades da busca vetorial, as estratégias de índice EmbeddingList, a consolidação de resultados híbridos e a disposição física que torna o recurso executável.
Por que o modelo de um vetor e uma linha plana não é mais suficiente
Considere um usuário pesquisando em um catálogo de vídeos por “uma pessoa cortando vegetais em uma cozinha”. O sinal relevante pode estar em um clipe de oito segundos, não em um embedding do vídeo inteiro. Comprimir cada clipe, objeto e ação em um único vetor pode preservar o tópico geral, mas pode apagar detalhes locais.
A mesma incompatibilidade aparece em outras cargas de trabalho:
- A relevância de um produto pode vir de uma de várias imagens ou ângulos.
- Um documento pode ser considerado relevante por causa de uma passagem, e não pelo seu assunto geral.
- Uma memória de agente pode conter várias observações, e apenas uma delas importa para a tarefa atual.
- Um registro ColBERT ou ColPali contém uma lista de comprimento variável de vetores de tokens ou patches, em vez de um único vetor denso.
Uma alternativa é dividir cada clipe, imagem ou passagem em uma linha separada do banco de dados. Isso permite a busca local, mas também separa cada fragmento de sua entidade pai. Os metadados da entidade pai podem ser repetidos entre linhas, e a recuperação no nível da entidade passa a exigir agrupamento, deduplicação e reordenação após a busca por fragmentos.
Somente o armazenamento aninhado não resolve o problema de consulta. O JSON pode armazenar objetos, mas não oferece ao Milvus um esquema predefinido de subcampos para indexação vetorial e escalar. Arrays paralelos podem armazenar legendas, rótulos de cena e valores de confiança, mas a aplicação precisa manter o alinhamento por offset. O banco de dados não pode inferir com segurança que scene_type[3] e label_confidence[3] descrevem o mesmo clipe, a menos que essa relação faça parte do modelo de dados.
O StructArray codifica essa relação diretamente. Ele mantém os elementos locais dentro da entidade pai enquanto expõe seus subcampos alinhados à validação de esquema, indexação, filtragem e busca vetorial.
O que é StructArray e seu modelo de dados?
Um StructArray, também conhecido como array de structs, armazena um conjunto ordenado de elementos Struct em cada entidade. Um campo StructArray é um Array cujos elementos seguem todos um mesmo esquema Struct predefinido. Para uma coleção de vídeos, a forma lógica poderia ser assim:
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
>>
Aqui:
clipsé o campo StructArray pai.clip_embedding_list,clip_embedding,start_sece os demais atributos são subcampos.clips[0]é o primeiro clipe.- Todos os subcampos no offset
0pertencem a esse mesmo clipe. - Todos os subcampos no offset
3pertencem a outro clipe.
Os dois subcampos vetoriais atendem a modos de busca diferentes. clips[clip_embedding_list] é indexado com uma métrica MAX_SIM* para busca EmbeddingList no nível da entidade, enquanto clips[clip_embedding] é indexado com uma métrica vetorial regular para busca no nível do elemento. Como um campo ou subcampo vetorial aceita apenas um índice, uma coleção que precisa dos dois modos deve definir e indexar os dois subcampos separadamente.
Esse modelo suporta três semânticas de consulta distintas.
1. A busca EmbeddingList retorna entidades pai
Os vetores em clips[clip_embedding_list] formam uma lista de embeddings para o vídeo. A consulta também é um EmbeddingList. O Milvus compara a lista de consulta com cada lista armazenada usando uma métrica MAX_SIM* e retorna um resultado no nível da entidade.
Plaintext
clips[clip_embedding_list] = [
embedding_0,
embedding_1,
embedding_2,
...
]
2. A família MATCH_* filtra entidades pai
MATCH_ANY, MATCH_ALL, MATCH_LEAST, MATCH_MOST e MATCH_EXACT avaliam um predicado contra os elementos Struct, contam quantos elementos o satisfazem e decidem se a entidade pai passa no filtro.
Por exemplo:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
Ambas as condições escalares precisam ser verdadeiras no mesmo offset do clipe. O Milvus não combina um rótulo de cozinha de um clipe com um valor de alta confiança de outro.
3. A busca no nível do elemento retorna o offset do elemento correspondente
Um vetor de consulta regular pode pesquisar independentemente cada vetor em clips[clip_embedding]. Cada resultado identifica a entidade pai e o offset baseado em zero do elemento Struct correspondente. Um element_filter pode restringir quais elementos participam dessa busca vetorial.
Essas operações compartilham uma premissa: o Milvus sabe quais valores vetoriais e escalares pertencem ao mesmo elemento e quais elementos pertencem à mesma entidade.
O StructArray não é um sistema genérico de aninhamento arbitrário. Seu modelo atual é um Array de elementos Struct com subcampos escalares e vetoriais suportados. Esse limite torna a indexação de subcampos e a execução ciente de elementos viáveis.
Construa o esquema, os índices e o caminho de inserção
O exemplo simplificado de PyMilvus a seguir cria uma coleção de vídeos com um vetor de nível superior e um StructArray para clipes. Ele usa subcampos vetoriais de clipe separados para que a mesma coleção possa demonstrar ambos os modos de busca.
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)
Subcampos vetoriais precisam ser indexados antes da busca. Como a família de métricas determina o modo de busca, cada subcampo vetorial recebe seu próprio índice:
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)
Índices escalares são opcionais, mas subcampos que aparecem com frequência em filtros de grande escala devem usar um índice escalar compatível. Por exemplo, clips[scene_type] pode usar um índice invertido, enquanto um subcampo numérico como clips[label_confidence] pode usar um índice adequado à filtragem numérica.
Insira os dados em sua forma natural de entidade: uma linha de vídeo com um array de objetos de clipe. Para manter o exemplo compacto, ele grava o mesmo vetor de clipe nos dois subcampos vetoriais.
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”)
Na fronteira da API, clips permanece como um array de objetos estruturados. Dentro do Milvus, cada subcampo segue o caminho tipado necessário para seu próprio índice, filtro e comportamento de saída. Essa distinção é transparente no momento da inserção, mas fundamental para tudo o que vem a seguir.
A filtragem no mesmo elemento é a diferença entre estrutura e arrays paralelos
O principal benefício da filtragem não é uma sintaxe mais curta para campos aninhados. É a correlação correta entre subcampos escalares.
Suponha que a aplicação precise de vídeos contendo um clipe de cozinha com confiança de rótulo acima de 0.8. Não basta que um vídeo contenha algum clipe de cozinha e algum clipe de alta confiança; o mesmo clipe precisa satisfazer as duas condições.
A família MATCH_* do StructArray expressa isso diretamente:
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)
O Milvus avalia o predicado em cada offset de elemento e, em seguida, aplica o quantificador do operador para decidir se a entidade pai passa:
MATCH_ANY: pelo menos um elemento corresponde.MATCH_ALL: todos os elementos correspondem.MATCH_LEAST: pelo menosthresholdelementos correspondem.MATCH_MOST: no máximothresholdelementos correspondem.MATCH_EXACT: exatamentethresholdelementos correspondem.
Se os mesmos dados fossem armazenados como dois arrays independentes, a expressão a seguir não preservaria essa correlação:
Plaintext
array_contains(clips[scene_type], "kitchen")
AND
array_contains(clips[label_confidence], 0.9)
Os dois valores poderiam ocorrer em offsets diferentes. Isso pode ser válido para atributos não relacionados, mas é incorreto quando ambas as condições descrevem o mesmo clipe, imagem de produto ou passagem de documento.
O StructArray torna a identidade do elemento parte do predicado do banco de dados, em vez de uma convenção que a aplicação precisa impor.
Duas granularidades de busca vetorial, duas identidades de resultado
Quando uma entidade armazena múltiplos vetores, a recuperação precisa resolver uma questão de modelagem antes que a busca ANN comece:
Os vetores devem ser pontuados em conjunto como uma representação da entidade pai, ou cada vetor de elemento deve competir de forma independente?
O StructArray suporta ambos os modelos, mas eles usam diferentes formatos de consulta, famílias de métricas, subcampos vetoriais e identidades de resultado.
Busca EmbeddingList: uma lista de vetores de consulta encontra uma entidade
Uma consulta EmbeddingList contém múltiplos vetores. Um vídeo de consulta pode ser dividido em vários clipes; uma consulta de produto pode conter várias imagens de referência; uma consulta ColBERT contém um vetor por token da consulta.
Para cada entidade, o Milvus compara a lista de consulta com a lista de embeddings armazenada da entidade. Na pontuação estilo MaxSim, cada vetor de consulta seleciona sua melhor correspondência na lista da entidade, e o Milvus agrega essas pontuações de melhor correspondência em uma pontuação da entidade. O resultado final representa a entidade pai, não um elemento Struct específico.
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,
)
Essa busca responde: Quais vídeos são a melhor correspondência geral para este conjunto de clipes de consulta?
Ela se adequa à recuperação vídeo a vídeo, à busca de produtos com múltiplas imagens, à recuperação no estilo ColBERT e ColPali e a outros casos em que tanto a consulta quanto a entidade armazenada são representadas por múltiplos vetores.
Busca no nível do elemento: um vetor de consulta encontra um clipe dentro de uma entidade
A busca no nível do elemento usa um vetor de consulta regular. Todos os vetores em clips[clip_embedding] participam da busca ANN como candidatos independentes. Cada resultado identifica a entidade pai e o offset do elemento correspondente.
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"],
)
Para pesquisar apenas clipes selecionados, anexe um element_filter cujas condições escalares se apliquem ao mesmo clipe:
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"],
)
O filtro não seleciona primeiro um clipe de cozinha e depois pesquisa um clipe diferente de alta confiança. Tanto os predicados quanto o candidato vetorial referem-se ao mesmo elemento Struct.
Uma resposta não agrupada pode ter a seguinte aparência:
Plaintext
id = 1, offset = 1, distance = 0.91
id = 8, offset = 4, distance = 0.88
id = 1, offset = 3, distance = 0.84
A mesma entidade pode aparecer mais de uma vez porque vários clipes podem corresponder. Isso é útil quando a aplicação precisa mostrar não apenas qual vídeo ou documento é relevante, mas também qual clipe ou passagem gerou a correspondência.
| Aspecto | Busca EmbeddingList | Busca no nível do elemento |
|---|---|---|
| Entrada da consulta | Um ou mais vetores de consulta em um EmbeddingList | Um vetor de consulta regular |
| Exemplo de alvo | clips[clip_embedding_list] | clips[clip_embedding] |
| Família de métricas | MAX_SIM* | Métricas regulares como COSINE, IP ou L2 |
| Unidade candidata no ANN | A lista de embeddings da entidade pai | O vetor de cada elemento Struct |
| Identidade do resultado | Entidade pai | Entidade pai mais o offset do elemento |
| Caso de uso típico | Corresponder uma consulta multivetorial a uma entidade multivetorial | Encontrar o clipe, imagem, passagem, patch ou fato mais relevante |
Para suportar ambos os modos em uma única coleção, defina e indexe subcampos vetoriais separados. O formato da consulta, a família de métricas e o índice de destino precisam estar alinhados.
A indexação EmbeddingList é uma decisão de qualidade-custo
Com um embedding por entidade, um índice ANN encontra entidades próximas a um vetor de consulta. A busca EmbeddingList é mais cara porque a relevância depende de interações par a par entre duas listas de vetores.
Calcular o MaxSim exato contra cada vetor em cada entidade produz a classificação de referência mais limpa, mas uma varredura completa costuma ser cara demais para recuperação online. Por isso, o Milvus usa um modelo em dois estágios:
- Uma estratégia aproximada recupera entidades pai candidatas.
- Quando
emb_list_rerankestá habilitado, o Milvus recalcula o MaxSim sobre esses candidatos para produzir a classificação final.
Recuperar mais candidatos no primeiro estágio geralmente aumenta a chance de que os verdadeiros melhores resultados cheguem ao reranker, mas também aumenta a latência e o custo computacional. As três estratégias diferem principalmente em como produzem esse conjunto de candidatos.
| Estratégia | Representação do candidato no primeiro estágio | Bom ponto de partida quando | Principal trade-off |
|---|---|---|---|
| TokenANN | Indexa cada vetor em cada lista de embeddings. Os vetores de consulta executam ANN independentemente; as correspondências são agregadas de volta às entidades pai antes do reranking MaxSim. | A qualidade é a prioridade, as listas são curtas ou médias e os vetores individuais são discriminativos. | O tamanho do índice e o trabalho de busca no primeiro estágio crescem com o comprimento da lista e o número de vetores de consulta. |
| MUVERA | Codifica cada lista de embeddings em um vetor de dimensão fixa por meio de projeções aleatórias e, em seguida, executa ANN comum. | O TokenANN é pesado demais e a compressão sem pipeline de treinamento é preferível. | A codificação perde informação; configurações de projeção mais fortes aumentam a dimensionalidade codificada e o custo do ANN. |
| LEMUR | Treina um modelo que mapeia uma lista de embeddings para um vetor de entidade pai de dimensão fixa. | Os embeddings são menos discriminativos, as listas são grandes ou a carga de trabalho é visual ou multimodal. | Requer treinamento e pode ser sensível à distribuição do corpus e ao viés de comprimento de documentos. |
Nenhuma estratégia única é a melhor para toda carga de trabalho. Comece pelos dados-alvo e pela distribuição de consultas:
- Use o TokenANN como linha de base centrada em qualidade quando o tamanho do conjunto de dados permitir.
- Experimente o MUVERA quando o índice do TokenANN ou a recuperação de candidatos se tornar cara demais à medida que o comprimento da lista cresce, e você quiser evitar um pipeline de treinamento.
- Avalie o LEMUR quando o espaço de embeddings for ruidoso ou pouco discriminativo, ou quando a carga de trabalho for visual ou multimodal.
- Meça recall ou nDCG juntamente com latência e tamanho do índice. Uma estratégia que funciona para textos curtos pode se comportar de forma diferente com comprimentos de documento de cauda longa ou milhares de patches visuais.
O StructArray resolve um problema: como representar elementos alinhados, filtráveis e com vetores dentro de uma única entidade. A estratégia EmbeddingList aborda outro: como aproximar o MaxSim a um custo aceitável para um modelo e corpus específicos.
A busca híbrida torna explícita a identidade do resultado
A recuperação em produção raramente segue um único caminho vetorial. Uma solicitação de vídeo pode combinar um embedding de vídeo de nível superior, um ou mais embeddings de clipe, um sinal de legenda ou transcrição e um reranker.
Quando candidatos no nível do elemento entram nesse pipeline, o mecanismo precisa decidir o que identifica um candidato final.
| Composição da solicitação híbrida | Escopo do candidato final | Identidade do resultado |
|---|---|---|
| Todas as sub-buscas são no nível do elemento e visam subcampos vetoriais sob o mesmo StructArray | Nível do elemento | Chave primária mais campo StructArray mais offset do elemento |
| Um campo vetorial de nível superior é incluído | Nível da entidade | Chave primária |
| Uma solicitação EmbeddingList é incluída | Nível da entidade | Chave primária |
| Solicitações no nível do elemento visam diferentes campos StructArray | Nível da entidade | Chave primária |
A primeira configuração preserva a identidade do elemento porque o offset 3 refere-se ao mesmo elemento Struct para cada sub-busca sob um determinado StructArray pai. Isso se adequa a uma aplicação que deseja retornar o clipe ou a passagem mais relevante após fundir vários sinais no nível do elemento.
As demais configurações misturam granularidades de candidatos ou namespaces de elementos. Um resultado de elemento precisa, portanto, ser consolidado em uma pontuação no nível da entidade antes do reranking final. O Milvus suporta várias estratégias de consolidação:
| Estratégia de consolidação | Pontuação da entidade a partir dos resultados de elementos retornados | Condição importante |
|---|---|---|
max | Melhor pontuação de elemento | Funciona com métricas vetoriais regulares suportadas |
sum | Soma de todas as pontuações de elementos retornadas | Use com métricas de correlação positiva, como IP ou COSINE |
avg | Média das pontuações de elementos retornadas | Funciona com métricas vetoriais regulares suportadas |
topk_sum | Soma das melhores K pontuações de elementos retornadas | Requer um topk positivo; use com IP ou COSINE |
topk_avg | Média das melhores K pontuações de elementos retornadas | Requer um topk positivo |
A consolidação opera apenas sobre os resultados de elementos retornados por essa sub-busca ANN; ela não varre todos os elementos da entidade após a recuperação. O limit da solicitação, portanto, controla quais resultados de elementos ficam disponíveis para a função de consolidação.
Essa escolha molda a semântica da recuperação, não apenas a formatação da saída. Se a aplicação apresenta um clipe ou passagem, preservar o offset durante a fusão é natural. Se ela apresenta um vídeo, produto ou documento, a consolidação no nível da entidade é natural. Quando os sinais operam em granularidades diferentes, o sistema precisa de uma regra explícita de pontuação de elemento para entidade.
O StructArray move esse problema de identidade-e-consolidação do pós-processamento ad hoc para o modelo de execução da busca.
Como o Milvus executa o StructArray sem tratá-lo como um blob
O modelo voltado ao usuário é ARRAY<STRUCT>. No entanto, armazenar o valor inteiro como um único blob opaco tornaria ineficientes os índices de subcampos, os filtros e a saída seletiva.
O Milvus usa um design de pai lógico com colunas filhas físicas.
Na camada de esquema, clips é o campo pai lógico. Ele define propriedades como o esquema Struct, a capacidade máxima e a anulabilidade. Seus subcampos são normalizados em caminhos como clips[clip_embedding_list], clips[clip_embedding], clips[scene_type] e clips[label_confidence].
Subcampos escalares seguem caminhos de armazenamento de arrays escalares por entidade, enquanto subcampos vetoriais seguem caminhos de arrays vetoriais. Cada subcampo pode então usar o caminho de dados adequado ao seu tipo: filtragem escalar e índices escalares para metadados, e índices vetoriais e busca ANN para embeddings.
Na ingestão, o Proxy expande a lista aninhada de Struct em colunas filhas tipadas. Durante a execução, o Milvus mantém a relação entre cada elemento físico e sua entidade pai. Conceitualmente, essa relação tem esta aparência:
Plaintext
entity 0 -> elements [0, 1, 2]
entity 1 -> elements [3]
entity 2 -> elements []
entity 3 -> elements [4, 5, 6, 7]
Quando a busca no nível do elemento retorna um ID de elemento físico, o Milvus o mapeia de volta para a entidade pai e o offset do elemento. Quando element_filter produz um bitmap no nível do elemento, o mecanismo o alinha com a visibilidade da entidade pai, exclusões e outros filtros.
Ao retornar resultados, o Milvus usa o esquema lógico e os offsets compartilhados para reconstruir a forma StructArray que a aplicação inseriu. O sistema pode executar sobre colunas filhas tipadas enquanto o usuário continua lendo e escrevendo objetos aninhados naturais. Essa disposição física faz do StructArray mais do que um JSON tipado: a relação aninhada participa do modelo de índice e execução.
Onde o StructArray se encaixa e onde não se encaixa
O StructArray é uma ótima escolha quando todas as condições a seguir são verdadeiras:
- A aplicação tem uma entidade pai significativa, como vídeo, produto, documento, página visual ou registro de memória.
- Cada entidade pai contém um conjunto ordenado e de comprimento variável de elementos locais.
- Esses elementos precisam de seus próprios metadados escalares, vetores, ou ambos.
- A busca ou a filtragem deve preservar a relação entre subcampos no mesmo offset de elemento.
- A aplicação precisa de recuperação multivetorial no nível da entidade, resultados no nível do elemento, ou ambos.
O StructArray não é automaticamente melhor para toda coleção. Um documento curto ou uma consulta simples pode ser bem atendido por um único embedding denso. A indexação multivetorial adiciona custos de armazenamento e busca; portanto, a representação adicional deve conquistar seu lugar por meio de melhor qualidade de recuperação ou granularidade mais útil dos resultados.
Os limites atuais de esquema e execução também importam:
Structé suportado como tipo de elemento de umArray, não como campo de coleção de nível superior.- Todos os elementos em um StructArray compartilham um único esquema predefinido.
max_capacityé obrigatório e limita o número de elementos por entidade.- Subcampos aninhados
Struct,Array,ArrayOfStructeJSONnão são suportados dentro de um StructArray. - Um subcampo vetorial aceita um único índice. Use subcampos vetoriais separados para busca EmbeddingList e busca no nível do elemento quando ambos forem necessários.
- Subcampos vetoriais precisam ser indexados antes da busca. Subcampos escalares muito usados em filtros devem ser indexados adequadamente.
- O esquema de subcampos é fixado após a criação do campo StructArray; portanto, planeje os atributos dos elementos antes do lançamento em produção.
Essas restrições tornam o modelo mais estreito do que o aninhamento arbitrário de um banco de documentos, mas também dão ao Milvus estrutura suficiente para raciocinar sobre a identidade do elemento, indexar cada subcampo e executar em duas granularidades de busca.
O StructArray mantém a evidência local como elemento de primeira classe sem perder a entidade
O StructArray oferece ao Milvus um objeto de recuperação que esquemas planos têm dificuldade de representar: uma entidade pai com um conjunto ordenado de elementos estruturados. As relações entre esses elementos participam da filtragem, da indexação e da busca, em vez de existirem apenas no armazenamento.
Cada elemento mantém seus próprios metadados e embeddings. Os elementos podem satisfazer predicados escalares do mesmo elemento, participar juntos da busca EmbeddingList no nível da entidade ou competir independentemente na busca no nível do elemento. Ao mesmo tempo, eles permanecem vinculados à entidade pai cujos metadados, permissões e identidade na aplicação lhes dão contexto.
Para clipes de vídeo, imagens de produto, passagens de documento, patches visuais e fragmentos de memória, a evidência local pode ser pesquisada e filtrada sem perder a entidade à qual pertence. As demais escolhas de design são explícitas: selecione a granularidade da busca, atribua a cada subcampo vetorial a métrica e o índice correspondentes e decida se os resultados híbridos devem preservar os offsets dos elementos ou consolidar de volta para entidades.
Experimente o StructArray no Milvus 3.0
O StructArray está disponível no Milvus 3.0. Comece pela visão geral do StructArray. Se você está avaliando recuperação multivetorial no nível da entidade, leia o guia de estratégia EmbeddingList. Para granularidade de resultados e comportamento de consolidação, consulte Hybrid Search com StructArray.
Para o contexto mais amplo do lançamento, consulte o blog de lançamento do Milvus 3.0, as notas de versão e o repositório milvus-io/milvus.
O Zilliz Cloud também suporta o StructArray e a busca EmbeddingList para implantações gerenciadas. Revise o guia de StructArray do Zilliz Cloud para limites específicos do serviço. No Zilliz Cloud, operadores escalares em StructArray estão documentados atualmente para clusters On-Demand.
Para discutir um esquema ou um design de recuperação com a equipe, participe da comunidade Milvus no Discord ou agende uma sessão de 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



