Abbinamento sociCompatible with Milvus v3.0.2+

Panoramica

Il filtro di appartenenza filtra le entità verificando se il valore di un campo scalare appartiene a un insieme fornito dal chiamante. Il client crea un blob binario compatto del filtro di appartenenza e lo passa come valore del modello di espressione.

Sostituisce gli elenchi di membri di grandi dimensioni ( IN [...] ). Un filtro " IN " serializza ogni membro nella richiesta, pertanto insiemi di membri sufficientemente grandi possono superare il limite di ricezione gRPC del proxy. Con " membership_match", il proxy convalida il blob e lo incorpora nel piano di query senza ricostruire un elenco di membri.

Membership Match workflow: the client builds a reusable blob, the proxy validates it, and QueryNodes evaluate it. Flusso di lavoro di Membership Match: il client crea un blob riutilizzabile, il proxy lo convalida e i QueryNodes lo valutano.

Il diagramma mostra la divisione fissa dei compiti. Il client crea il blob una sola volta e lo riutilizza in tutte le richieste; il proxy verifica i limiti relativi all’involucro e alle dimensioni e incorpora il blob nel piano di query così com’è; infine, i QueryNodes valutano il piano rispetto ai dati grezzi o a un indice.

L’operatore dispone di due codifiche con diverse garanzie di correttezza:

  • Bloom (type=bloom): appartenenza approssimativa. Sono possibili falsi positivi; non sono previsti falsi negativi da un filtro valido. Uso tipico: filtri di pubblico o di esclusione ad alto volume in cui sono accettabili occasionali falsi positivi.
  • Roaring (type=roaring): appartenenza esatta per valori interi. Uso tipico: ACL, visibilità dei tenant, filtri di conformità e operazioni di eliminazione in base a un insieme di ID.

Non utilizzare l’appartenenza Bloom per decisioni di autorizzazione o eliminazioni distruttive. Un falso positivo può esporre un’entità o eliminare un’entità che non fa parte dell’insieme previsto.

Lista di controllo operativa

  • Verificare il tipo di campo di destinazione e costruire il blob a partire dallo stesso dominio di valori.
  • Selezionare Bloom solo quando i falsi positivi sono accettabili.
  • Selezionare "Roaring" per le operazioni relative a ACL, conformità, isolamento dei tenant ed eliminazione.
  • Creare blob di grandi dimensioni una sola volta e riutilizzarli; non ricrearli all’interno di ogni richiesta.
  • Calcolare le dimensioni del blob in byte serializzati, non in numero di elementi.
  • Su un cluster con versioni miste, completare l'aggiornamento graduale, iniziando dai proxy, prima di abilitare l'membership_match.
  • Testare l'espressione su una raccolta non di produzione prima di abilitare un flusso di lavoro distruttivo.

Sintassi dell’espressione

membership_match(<field>, {<template>}, type=bloom)
membership_match(<field>, {<template>}, type=roaring)
membership_match(<field>, {<template>})
not membership_match(<field>, {<template>}, type=bloom)

Il secondo argomento è un segnaposto del modello. L'SDK fornisce il valore del segnaposto come oggetto ` bytes ` in formato protobuf contenente il blob del filtro generato dal client. Mantenere esplicito il ` type ` nelle espressioni di produzione in modo che il contratto di correttezza previsto sia visibile nel codice e nei log.

L’argomento type è facoltativo. Ogni blob contiene un’intestazione magica autodescrittiva, MBF1 per Bloom e MRB1 per Roaring, e il server rileva l’intestazione quando type viene omesso. Un type esplicito che entra in conflitto con l’intestazione del blob viene rifiutato come errore di richiesta, mentre le intestazioni sconosciute o troncate determinano uno stato di fallimento chiuso.

Scegliere la codifica

Utilizzare Bloom quando è accettabile un falso positivo. Bloom accetta campi interi con segno (INT8/16/32/64), campi VARCHAR e percorsi JSON. Un filtro Bloom non è una lista di autorizzazioni esatta.

fpr (tasso di falsi positivi, fpr) è la probabilità che un filtro Bloom segnali un valore come membro quando tale valore non è mai stato inserito: fpr=0.005 significa che, approssimativamente, un valore non membro su 200 viene erroneamente segnalato come presente. Un fpr più basso produce un blob più grande.

Utilizzare Roaring quando ogni corrispondenza deve essere esatta. Roaring accetta solo campi interi con segno; gli elementi vengono estesi con segno a INT64 prima della serializzazione, quindi un INT8 pari a -1 e un INT64 pari a -1 sono lo stesso elemento. Non utilizzarlo per UUID o altri campi stringa.

DimensioneBloom (type=bloom)Roaring (type=roaring)
PrecisioneApprossimativa; possibili falsi positivi; nessun falso negativoEsatto; nessun falso positivo e nessun falso negativo
CampiINT8/16/32/64, VARCHAR, percorsi JSONSolo INT8/16/32/64 con segno
Dimensione del blobNumero di membri e FPR; circa 11 bit per membro a FPR 0,005; corpo a potenza di dueDistribuzione dei valori; gli insiemi densi si comprimono in dimensioni molto più ridotte; i valori casuali a 64 bit raggiungono il limite massimo del contenitore pari a 262.144
EliminazioneRifiutata in fase di analisiConsentito
Uso tipicoFiltri di pubblico ed esclusioneACL, conformità, isolamento dei tenant, eliminazione in base a un insieme di ID

I percorsiJSON sono tipizzati in modo rigoroso, riga per riga: una stringa JSON viene interpretata come un valore VARCHAR, un intero JSON come un valore INT64 e un doppio JSON non corrisponde mai a un membro intero, a differenza dell'IN, dove 5.0 è uguale a 5. Se i tuoi scrittori codificano i numeri interi come float, normalizzali in fase di scrittura oppure utilizza un campo scalare tipizzato.

Per entrambe le codifiche, costruisci l’insieme dei membri a partire dallo stesso dominio di valori del campo di destinazione. Non creare un filtro Bloom a partire dalla stringa "10086" e non applicarlo a un campo INT64. L'intestazione del blob registra i domini dei valori inseriti e un blob con dominio errato viene rifiutato dal proxy con un errore di parametro, anziché non trovare alcuna corrispondenza in modo silenzioso. Riutilizza un blob già creato tra una richiesta e l'altra, anziché ricostruirlo per ogni query.

Per insiemi di poche migliaia di elementi o meno, il semplice IN rimane l’opzione più semplice. Il server non sceglie tra IN, Bloom e Roaring. La selezione della codifica è responsabilità del chiamante.

Per scegliere una codifica:

  1. Valutare se il carico di lavoro tollera i falsi positivi. In caso contrario, Bloom non è mai un'opzione.
  2. Per un'appartenenza esatta, utilizzare Roaring con un campo intero con segno. I valori densi o raggruppati si comprimono nei blob più piccoli.
  3. Verificare i limiti di dimensione. I valori casuali uniformi a 64 bit potrebbero superare il limite massimo di 262.144 per i container di grandi dimensioni o i limiti di dimensione. In tal caso, utilizzare una lista IN tradizionale che rientri nei limiti della richiesta.
  4. Per l’appartenenza esatta su campi non interi, utilizzare IN. Non esiste una codifica di appartenenza esatta per questi campi.

La corrispondenza di appartenenza non è disponibile tramite l’API HTTP RESTful. I parametri dei modelli di espressione HTTP accettano solo valori booleani, numerici, stringhe e array; non esiste un tipo binario bytes per il blob. Utilizza un SDK basato su gRPC per passare i blob di appartenenza.

Modelli di richiesta SDK

I seguenti frammenti di codice si concentrano sulla creazione di blob di appartenenza e sul loro passaggio come parametri dei modelli di filtro. Si presume che le collezioni e i campi esistano già e siano caricati, che i client SDK siano connessi e che i vettori di query, gli elenchi dei membri e altre variabili a cui si fa riferimento siano definiti. La configurazione delle collezioni e la struttura completa dell’applicazione sono omesse.

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

Eliminazione con Roaring

Utilizzare un blob Roaring esatto quando si eliminano entità selezionate da un insieme esterno di ID interi. Verificare l’insieme dei membri e il tipo di campo della collezione prima di eseguire l’eliminazione. Non utilizzare Bloom: l’appartenenza approssimativa non è un predicato di eliminazione sicuro e le forme Bloom vengono rifiutate nelle espressioni di eliminazione in fase di analisi sintattica.

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 } });

La semantica di eliminazione segue una corrispondenza esatta a tre valori:

Valore del campomembership_match(…)non membership_match(…)
NULLnessuna corrispondenza (NULL)nessuna corrispondenza (NULL)
nell'insieme dei membricorrispondenzanessuna corrispondenza
fuori dall'insiemenessuna corrispondenzacorrispondenza

La tabella va letta riga per riga: un comando Delete con membership_match rimuove le entità all'interno dell'insieme, un comando Delete con not membership_match rimuove le entità al di fuori dell'insieme, mentre le entità NULL sopravvivono in entrambe le polarità.

  • Le entità con un valore di campo NULL non vengono mai eliminate, in nessuna delle due polarità.
  • Una bitmap vuota non elimina nulla. " not membership_match(<field>, {<empty>}, type=roaring) " elimina ogni entità con un valore di campo non NULL, ovvero il complemento esatto, che può essere un insieme di grandi dimensioni per definizione.
  • not membership_match è il complemento esatto dell’insieme dei membri rispetto alle entità non NULL, non un’approssimazione.

Sui segmenti sigillati caricati solo con indice, senza dati scalari grezzi, la valutazione utilizza una ricerca inversa limitata quando l’indice lo supporta. Quando non sono disponibili né dati di campo grezzi né un indice qualificante, la richiesta fallisce con FieldNotLoaded invece di restituire entità non filtrate.

Limiti operativi

ImpostazionePredefinitoConfigurabileCosa limita
proxy.maxMembershipFilterSize64 MiBmilvus.yaml proxyun corpo blob
proxy.maxMembershipFilterPlanSize128 MiBmilvus.yaml proxyaggregato per richiesta
proxy.grpc.serverMaxRecvSize128 MiBmilvus.yaml proxyintera richiesta
Limite massimo per container Roaring262.144fissato nel codiceper filtro
Stima della memoria decodificata di Roaring64 MiBfissa nel codiceper filtro
Limite del formato MBF1128 MiBfissato nel codicelimite massimo del formato blob
Bloom fpr0,005generatore clienttarget dei falsi positivi

Le impostazioni " proxy.* " sono configurazioni del server Milvus: vengono impostate dall'operatore del cluster in milvus.yaml nella sezione " proxy " e non possono essere modificate per singola richiesta o per espressione al momento del filtraggio. Le ammissioni Roaring e il limite del formato MBF1 sono fissati nell’implementazione e non possono essere configurati in nessun modo. La riga " fpr " è l’unico controllo lato client: viene scelta al momento della creazione del blob, con un intervallo accettato compreso tra 0,0001 e 0,05.

Il corpo del blob ammette al massimo circa 48,6 milioni di membri INT64 all’fpr predefinito, con l’intestazione dell’involucro fissa da 32 byte consentita in aggiunta. Il budget a livello di richiesta copre la dimensione serializzata aggregata dei piani contenenti membri più la dimensione decodificata stimata aggregata dei filtri Roaring, e le sotto-richieste HybridSearch e i filtri di valutazione lo condividono. I corpi dei blob Bloom sono potenze di due: un numero di membri appena superiore al limite di un livello raddoppia il blob, e un fpr leggermente più alto spesso rientra nel livello immediatamente inferiore. Quando il proxy rifiuta un blob Bloom di dimensioni eccessive, l’errore suggerisce un’ fpr e che rientri nei limiti.

  • I risultati delle espressioni di appartenenza non vengono memorizzati nella cache tra una richiesta e l’altra.
  • I cluster con versioni miste falliscono in modalità chiusa: i QueryNode che eseguono codice più vecchio rifiutano i piani che contengono nodi del piano di appartenenza. Aggiornare prima i proxy, quindi completare l’aggiornamento graduale prima di abilitare l’operatore.
  • I log del server oscurano i byte dei blob. Il testo dell’espressione contiene solo il nome del modello; il payload binario viene trasmesso fuori banda nei valori del modello.

Tradotto daDeepL

Prova Milvus gestito gratuitamente

Zilliz Cloud è senza problemi, basato su Milvus e 10 volte più veloce.

Inizia
Feedback

Questa pagina è stata utile?