Correspondência de membrosCompatible with Milvus v3.0.2+
Visão geral
A correspondência de pertença filtra entidades verificando se o valor de um campo escalar pertence a um conjunto fornecido pelo chamador. O cliente cria um blob binário compacto de filtro de pertença e passa-o como valor de um modelo de expressão.
Substitui as grandes listas de « IN [...] ». Um filtro « IN » serializa cada membro na solicitação, pelo que conjuntos de membros suficientemente grandes podem exceder o limite de receção do proxy gRPC. Com « membership_match », o proxy valida o blob e incorpora-o no plano de consulta sem reconstruir uma lista de membros.
Fluxo de trabalho de correspondência de membros: o cliente cria um blob reutilizável, o proxy valida-o e os QueryNodes avaliam-no.
O diagrama mostra a divisão de tarefas fixa. O cliente cria o blob uma vez e reutiliza-o em várias solicitações; o proxy valida os limites de envelope e tamanho e incorpora o blob no plano de consulta tal como está; e os QueryNodes avaliam o plano em relação aos dados brutos ou a um índice.
O operador dispõe de duas codificações com diferentes garantias de correção:
- Bloom (
type=bloom): Afiliação aproximada. São possíveis falsos positivos; não se esperam falsos negativos com um filtro válido. Utilização típica: filtros de público em grande volume ou de exclusão, em que falsos positivos ocasionais são aceitáveis. - Roaring (
type=roaring): Pertença exata para valores inteiros. Utilização típica: ACLs, visibilidade de inquilinos, filtros de conformidade e operações de eliminação por conjunto de IDs.
Não utilize a pertença de Bloom para decisões de autorização ou eliminações destrutivas. Um falso positivo pode expor uma entidade ou eliminar uma entidade que não se encontre no conjunto pretendido.
Lista de verificação operacional
- Confirme o tipo de campo de destino e crie o blob a partir desse mesmo domínio de valores.
- Selecione «Bloom» apenas quando os falsos positivos forem aceitáveis.
- Selecione «Roaring» para operações de ACL, conformidade, isolamento de inquilinos e eliminação.
- Crie blobs de grande dimensão uma única vez e reutilize-os; não os recrie em cada pedido.
- Calcule o tamanho do blob em bytes serializados, e não na contagem de membros.
- Num cluster com versões mistas, conclua a atualização progressiva, começando pelos proxies, antes de ativar o «
membership_match». - Teste a expressão numa coleção que não seja de produção antes de ativar um fluxo de trabalho destrutivo.
Sintaxe da expressão
membership_match(<field>, {<template>}, type=bloom)
membership_match(<field>, {<template>}, type=roaring)
membership_match(<field>, {<template>})
not membership_match(<field>, {<template>}, type=bloom)
O segundo argumento é um marcador de posição do modelo. O SDK fornece o valor do marcador de posição como um blob de filtro ( bytes ) no formato protobuf, contendo o blob de filtro criado pelo cliente. Mantenha o type explícito nas expressões de produção, para que o contrato de correção pretendido fique visível no código e nos registos.
O argumento type é opcional. Cada blob contém um cabeçalho mágico autodescritivo — MBF1 para Bloom e MRB1 para Roaring — e o servidor deteta o cabeçalho quando type é omitido. Um type explícito que entre em conflito com o cabeçalho do blob é rejeitado como um erro de pedido, e cabeçalhos desconhecidos ou truncados resultam num estado de falha fechada.
Escolha a codificação
Utilize o Bloom quando um falso positivo for aceitável. O Bloom aceita campos de inteiros com sinal (INT8/16/32/64), campos VARCHAR e caminhos JSON. Um filtro Bloom não é uma lista de permissão exata.
fpr (taxa de falsos positivos) é a probabilidade de um filtro Bloom reportar um valor como membro quando esse valor nunca foi inserido: fpr=0.005 significa que, aproximadamente, um em cada 200 valores que não são membros é erroneamente reportado como presente. Uma fpr mais baixa produz um blob maior.
Utilize o Roaring quando todas as correspondências tiverem de ser exatas. O Roaring aceita apenas campos de inteiros com sinal; os membros são estendidos em sinal para INT64 antes da serialização, pelo que um INT8 -1 e um INT64 -1 são o mesmo membro. Não o utilize para UUID ou outros campos de cadeia de caracteres.
| Dimensão | Bloom (type=bloom) | Roaring (type=roaring) |
|---|---|---|
| Precisão | Aproximada; possíveis falsos positivos; sem falsos negativos | Exato; sem falsos positivos nem falsos negativos |
| Campos | INT8/16/32/64, VARCHAR, caminhos JSON | Apenas INT8/16/32/64 com sinal |
| Tamanho do blob | Número de membros e FPR; cerca de 11 bits por membro com FPR de 0,005; corpo em potência de dois | Distribuição de valores; conjuntos densos comprimem-se para tamanhos muito menores; valores aleatórios de 64 bits atingem o limite máximo de 262 144 do contentor |
| Eliminação | Rejeitado na fase de análise | Permitido |
| Utilização típica | Filtros de público e exclusão | ACL, conformidade, isolamento de inquilinos, eliminação por conjunto de IDs |
Os caminhosJSON são estritamente tipados, linha a linha: uma cadeia JSON é analisada como um valor VARCHAR, um inteiro JSON como um valor INT64 e um duplo JSON nunca corresponde a um membro inteiro, ao contrário do IN exato, onde 5,0 é igual a 5. Se os seus programadores codificarem inteiros como float, normalize-os na gravação ou utilize um campo escalar tipado.
Para qualquer uma das codificações, crie o conjunto de membros a partir do mesmo domínio de valores que o campo de destino. Não construa um filtro Bloom a partir da string "10086" e aplique-o a um campo INT64. O cabeçalho do blob regista os domínios de valores inseridos, e um blob com domínio incorreto é rejeitado pelo proxy com um erro de parâmetro, em vez de corresponder silenciosamente a nada. Reutilize um blob já construído em várias solicitações, em vez de o reconstruir para cada consulta.
Para conjuntos com alguns milhares de elementos ou menos, o « IN » simples continua a ser a opção mais simples. O servidor não escolhe entre IN, Bloom e Roaring. A seleção da codificação é da responsabilidade do chamador.
Para escolher uma codificação:
- Determine se a carga de trabalho tolera falsos positivos. Se não tolerar, o Bloom nunca é uma opção.
- Para uma adesão exata, utilize Roaring com um campo de inteiro assinado. Valores densos ou agrupados comprimem-se nos blobs mais pequenos.
- Verifique os limites de tamanho. Valores aleatórios uniformes de 64 bits podem exceder o limite máximo de 262 144 do high-container ou os limites de tamanho. Se isso acontecer, utilize uma lista IN tradicional que se adapte aos limites da solicitação.
- Para a adesão exata em campos não inteiros, utilize «IN». Não existe uma codificação de adesão exata para estes campos.
A correspondência de pertença não está disponível através da API HTTP RESTful. Os parâmetros do modelo de expressão HTTP aceitam apenas valores booleanos, numéricos, de cadeia de caracteres e de matriz; não existe um tipo binário bytes para o blob. Utilize um SDK baseado em gRPC para passar blobs de pertença.
Padrões de pedido do SDK
Os trechos de código a seguir centram-se na criação de blobs de pertença e na sua transmissão como parâmetros de modelos de filtro. Partem do princípio de que as coleções e os campos já existem e estão carregados, que os clientes do SDK estão ligados e que os vetores de consulta, as listas de membros e outras variáveis referenciadas estão definidas. A configuração da coleção e a estrutura completa da aplicação foram omitidas.
from pymilvus import MilvusClient, build_bloom_filter, build_roaring_bitmap
client = MilvusClient(uri="http://localhost:19530")
bloom_blob = build_bloom_filter(["creator-a", "creator-b"], fpr=0.005)
client.search(collection_name="posts", data=[query_vector], anns_field="embedding", limit=10,
filter="membership_match(creator_id, {members}, type=bloom)",
filter_params={"members": bloom_blob})
roaring_blob = build_roaring_bitmap([1001, 1002, 1003])
client.query(collection_name="docs", filter="membership_match(user_id, {members}, type=roaring)",
filter_params={"members": roaring_blob}, output_fields=["user_id", "title"])
bloomBlob, err := milvusclient.NewBloomFilterBlob([]int64{1001, 1002}, fpr)
if err != nil { return err }
search := milvusclient.NewSearchOption("posts", topK, vectors).
WithFilter("membership_match(creator_id, {members}, type=bloom)").
WithTemplateParam("members", bloomBlob)
roaringBlob, err := milvusclient.NewRoaringBitmapBlob([]int64{1001, 1002})
if err != nil { return err }
query := milvusclient.NewQueryOption("docs").
WithFilter("membership_match(user_id, {members}, type=roaring)").
WithTemplateParam("members", roaringBlob)
byte[] bloomBlob = BloomFilterUtils.buildBloomFilter(userIds, BloomFilterUtils.DEFAULT_FPR);
QueryReq request = QueryReq.builder().collectionName("docs")
.filter("membership_match(user_id, {members}, type=bloom)")
.filterTemplateValues(Collections.singletonMap("members", bloomBlob)).build();
byte[] roaringBlob = RoaringBitmapUtils.buildRoaringBitmap(userIds);
const bloomBlob = buildBloomFilter(userIds);
await client.query({ collection_name: 'docs',
filter: 'membership_match(user_id, {members}, type=bloom)',
exprValues: { members: bloomBlob } });
const roaringBlob = buildRoaringBitmap(integerIds);
Eliminação com Roaring
Utilize um blob Roaring exato ao eliminar entidades selecionadas por um conjunto externo de IDs inteiros. Verifique o conjunto de membros e o tipo de campo da coleção antes de emitir a eliminação. Não utilize Bloom: a pertença aproximada não é um predicado de eliminação seguro e as formas Bloom são rejeitadas nas expressões de eliminação durante a análise.
roaring_blob = build_roaring_bitmap([1001, 1002, 1003])
client.delete(collection_name="docs", filter="membership_match(id, {members}, type=roaring)",
filter_params={"members": roaring_blob})
roaringBlob, err := milvusclient.NewRoaringBitmapBlob([]int64{1001, 1002, 1003})
if err != nil { return err }
_, err = client.Delete(ctx, milvusclient.NewDeleteOption("docs").
WithExpr("membership_match(id, {members}, type=roaring)").
WithTemplateParam("members", roaringBlob))
byte[] roaringBlob = RoaringBitmapUtils.buildRoaringBitmap(userIds);
DeleteReq request = DeleteReq.builder().collectionName("docs")
.filter("membership_match(id, {members}, type=roaring)")
.filterTemplateValues(Collections.singletonMap("members", roaringBlob)).build();
const roaringBlob = buildRoaringBitmap(integerIds);
await client.delete({ collection_name: 'docs',
filter: 'membership_match(id, {members}, type=roaring)',
exprValues: { members: roaringBlob } });
A semântica de eliminação segue uma correspondência exata de três valores:
| Valor do campo | membership_match(…) | não membership_match(…) |
|---|---|---|
| NULL | sem correspondência (NULL) | sem correspondência (NULL) |
| no conjunto de membros | correspondência | sem correspondência |
| fora do conjunto | sem correspondência | correspondência |
A tabela é interpretada linha a linha: um «Delete» com « membership_match » remove as entidades dentro do conjunto, um «Delete» com « not membership_match » remove as entidades fora do conjunto e as entidades NULL sobrevivem a ambas as polaridades.
- As entidades com um valor de campo NULL nunca são eliminadas, independentemente da polaridade.
- Um bitmap vazio não elimina nada. «
not membership_match(<field>, {<empty>}, type=roaring)» elimina todas as entidades com um valor de campo diferente de NULL, o complemento exato, que pode ser um conjunto grande por definição. not membership_matché o complemento exato do conjunto de membros sobre as entidades não-NULL, não uma aproximação.
Em segmentos selados carregados apenas por índice, sem dados escalares brutos, a avaliação utiliza uma pesquisa inversa limitada quando o índice o suporta. Quando nem os dados de campo brutos nem um índice qualificativo estão disponíveis, o pedido falha com « FieldNotLoaded » em vez de devolver entidades não filtradas.
Limites operacionais
| Definição | Padrão | Configurável | O que limita |
|---|---|---|---|
proxy.maxMembershipFilterSize | 64 MiB | milvus.yaml proxy | um corpo de blob |
proxy.maxMembershipFilterPlanSize | 128 MiB | milvus.yaml proxy | agregado por pedido |
proxy.grpc.serverMaxRecvSize | 128 MiB | milvus.yaml proxy | pedido completo |
| Limite máximo de contentores em uso | 262 144 | corrigido no código | por filtro |
| Estimativa de memória descodificada do Roaring | 64 MiB | fixo no código | por filtro |
| Limite do formato MBF1 | 128 MiB | fixo no código | limite máximo do formato blob |
Bloom fpr | 0,005 | gerador de cliente | alvo de falsos positivos |
As definições « proxy.* » são configurações do servidor Milvus: são definidas pelo operador do cluster em milvus.yaml, na secção « proxy », e não podem ser ajustadas por pedido ou por expressão no momento da filtragem. As admissões «Roaring» e o limite do formato MBF1 estão fixados na implementação e não podem ser configurados em nenhum local. A linha « fpr » é o único controlo do lado do cliente: é selecionada quando o blob é criado, com um intervalo aceitável de 0,0001 a 0,05.
O corpo do blob admite, no máximo, cerca de 48,6 milhões de membros INT64 no fpr predefinido, com o cabeçalho de envelope fixo de 32 bytes permitido no topo. O orçamento para toda a solicitação abrange o tamanho serializado agregado dos planos que contêm membros, mais o tamanho decodificado estimado agregado dos filtros Roaring, sendo que as sub-solicitações HybridSearch e os filtros de pontuação o partilham. Os corpos dos blobs Bloom são potências de dois: uma contagem de membros que ultrapasse ligeiramente o limite de um nível duplica o blob, e um fpr ligeiramente superior cabe frequentemente no nível imediatamente inferior. Quando o proxy rejeita um blob Bloom de tamanho excessivo, o erro sugere um « fpr » que se encaixa.
- Os resultados da expressão de pertença não são armazenados em cache entre pedidos.
- Os clusters de versões mistas falham em modo fechado: os QueryNodes que executam código mais antigo rejeitam planos que contenham nós de plano de pertença. Atualize primeiro os proxies e, em seguida, conclua a atualização progressiva antes de ativar o operador.
- Os registos do servidor ocultam os bytes do blob. O texto da expressão contém apenas o nome do modelo; a carga binária é transmitida fora da banda nos valores do modelo.