Emparejamiento de sociosCompatible with Milvus v3.0.2+
Descripción general
La coincidencia de pertenencia filtra entidades comprobando si el valor de un campo escalar pertenece a un conjunto proporcionado por el llamante. El cliente crea un blob binario compacto de filtro de pertenencia y lo pasa como valor de una plantilla de expresión.
Sustituye a las grandes listas de « IN [...] ». Un filtro « IN » serializa cada miembro en la solicitud, por lo que los conjuntos de miembros suficientemente grandes pueden superar el límite de recepción de gRPC del proxy. Con « membership_match », el proxy valida el blob y lo integra en el plan de consulta sin reconstruir una lista de miembros.
Flujo de trabajo de Membership Match: el cliente crea un blob reutilizable, el proxy lo valida y los QueryNodes lo evalúan.
El diagrama muestra la división fija del trabajo. El cliente crea el blob una vez y lo reutiliza en todas las solicitudes; el proxy valida los límites de la envoltura y el tamaño e incorpora el blob tal cual en el plan de consulta; y los QueryNodes evalúan el plan con datos sin procesar o con un índice.
El operador dispone de dos codificaciones con diferentes garantías de exactitud:
- Bloom (
type=bloom): pertenencia aproximada. Son posibles los falsos positivos; no se esperan falsos negativos con un filtro válido. Uso típico: filtros de audiencia de gran volumen o de exclusión en los que se aceptan falsos positivos ocasionales. - Roaring (
type=roaring): pertenencia exacta para valores enteros. Uso típico: listas de control de acceso (ACL), visibilidad de inquilinos, filtros de cumplimiento normativo y operaciones de eliminación por conjunto de ID.
No utilices la pertenencia de Bloom para decisiones de autorización ni para eliminaciones destructivas. Un falso positivo puede exponer una entidad o eliminar una entidad que no se encuentre en el conjunto previsto.
Lista de comprobación operativa
- Confirma el tipo de campo de destino y crea el blob a partir de ese mismo dominio de valores.
- Seleccione «Bloom» únicamente cuando los falsos positivos sean aceptables.
- Seleccione «Roaring» para operaciones de ACL, cumplimiento normativo, aislamiento de inquilinos y eliminación.
- Crea blobs grandes una sola vez y reutilízalos; no los vuelvas a crear en cada solicitud.
- Calcule el tamaño del blob en bytes serializados, no en número de elementos.
- En un clúster de versiones mixtas, finaliza la actualización progresiva (primero los proxies) antes de habilitar «
membership_match». - Pruebe la expresión en una colección que no sea de producción antes de habilitar un flujo de trabajo destructivo.
Sintaxis de la expresión
membership_match(<field>, {<template>}, type=bloom)
membership_match(<field>, {<template>}, type=roaring)
membership_match(<field>, {<template>})
not membership_match(<field>, {<template>}, type=bloom)
El segundo argumento es un marcador de posición de la plantilla. El SDK proporciona el valor del marcador de posición como un objeto ` bytes ` de Protobuf que contiene el blob de filtro creado por el cliente. Mantén el ` type ` explícito en las expresiones de producción para que el contrato de corrección previsto sea visible en el código y en los registros.
El argumento type es opcional. Cada blob lleva un encabezado mágico autodescriptivo, MBF1 para Bloom y MRB1 para Roaring, y el servidor detecta el encabezado cuando se omite type. Un type explícito que entre en conflicto con el encabezado del blob se rechaza como un error de solicitud, y los encabezados desconocidos o truncados dan lugar a un estado de «fail-closed».
Elige la codificación
Utiliza Bloom cuando sea aceptable un falso positivo. Bloom admite campos de enteros con signo (INT8/16/32/64), campos VARCHAR y rutas JSON. Un filtro Bloom no es una lista de permitidos exacta.
fpr (tasa de falsos positivos) es la probabilidad de que un filtro Bloom señale un valor como miembro cuando dicho valor nunca se ha insertado: fpr=0.005 significa que, aproximadamente, uno de cada 200 valores que no son miembros se señala erróneamente como presente. Una fpr más baja produce un blob más grande.
Utiliza «Roaring» cuando todas las coincidencias deban ser exactas. «Roaring» solo acepta campos de enteros con signo; los miembros se amplían en signo a INT64 antes de la serialización, por lo que un INT8 -1 y un INT64 -1 son el mismo miembro. No lo utilices para UUID u otros campos de cadena.
| Dimensión | Bloom (type=bloom) | Roaring (type=roaring) |
|---|---|---|
| Precisión | Aproximada; posibles falsos positivos; sin falsos negativos | Exacto; sin falsos positivos ni falsos negativos |
| Campos | INT8/16/32/64, VARCHAR, rutas JSON | Solo INT8/16/32/64 con signo |
| Tamaño del blob | Número de miembros y FPR; unos 11 bits por miembro con un FPR de 0,005; cuerpo en forma de potencia de dos | Distribución de valores; los conjuntos densos se comprimen a tamaños mucho menores; los valores aleatorios de 64 bits alcanzan el límite máximo del contenedor de 262 144 |
| Eliminación | Rechazado en el momento del análisis sintáctico | Permitido |
| Uso típico | Filtros de audiencia y exclusión | ACL, cumplimiento normativo, aislamiento de inquilinos, eliminación por conjunto de ID |
Las rutasJSON son de tipado estricto, fila por fila: una cadena JSON se evalúa como un valor VARCHAR, un entero JSON como un valor INT64, y un doble JSON nunca coincide con un miembro entero, a diferencia del « IN » exacto, donde 5,0 es igual a 5. Si tus escritores codifican los enteros como flotantes, normalízalos al escribirlos o utiliza un campo escalar tipado.
Para cualquiera de las dos codificaciones, crea el conjunto de miembros a partir del mismo dominio de valores que el campo de destino. No construyas un filtro Bloom a partir de la cadena « "10086" » y lo apliques a un campo « INT64 ». La cabecera del blob registra los dominios de valores insertados, y el proxy rechaza un blob con un dominio incorrecto con un error de parámetro, en lugar de realizar una coincidencia silenciosa sin resultado. Reutiliza un blob ya creado en todas las solicitudes en lugar de volver a crearlo para cada consulta.
Para conjuntos de unos pocos miles de elementos o menos, la codificación simple « IN » sigue siendo la opción más sencilla. El servidor no elige entre IN, Bloom y Roaring. La selección de la codificación es responsabilidad del solicitante.
Para elegir una codificación:
- Determina si la carga de trabajo tolera falsos positivos. Si no es así, Bloom nunca es una opción.
- Para una pertenencia exacta, utiliza Roaring con un campo de entero con signo. Los valores densos o agrupados se comprimen en los blobs más pequeños.
- Comprueba los límites de tamaño. Los valores aleatorios uniformes de 64 bits pueden superar el límite máximo de 262 144 del contenedor o los límites de tamaño. Si es así, utiliza una lista IN tradicional que se ajuste a los límites de la solicitud.
- Para la pertenencia exacta en campos no enteros, utilice «IN». No existe una codificación de pertenencia exacta para estos campos.
La coincidencia de pertenencia no está disponible a través de la API HTTP RESTful. Los parámetros de las plantillas de expresión HTTP solo aceptan valores booleanos, numéricos, de cadena y de matriz; no existe un tipo binario bytes para el blob. Utiliza un SDK basado en gRPC para pasar blobs de pertenencia.
Patrones de solicitud del SDK
Los siguientes fragmentos de código se centran en la creación de blobs de pertenencia y en su transmisión como parámetros de plantillas de filtro. Se da por supuesto que las colecciones y los campos ya existen y están cargados, que los clientes del SDK están conectados y que los vectores de consulta, las listas de miembros y otras variables a las que se hace referencia están definidas. Se omite la configuración de la colección y el andamiaje completo de la aplicación.
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);
Eliminación con Roaring
Utiliza un blob Roaring exacto al eliminar entidades seleccionadas por un conjunto externo de ID enteros. Revisa el conjunto de miembros y el tipo de campo de la colección antes de ejecutar la eliminación. No utilices Bloom: la pertenencia aproximada no es un predicado de eliminación seguro, y las formas de Bloom se rechazan en las expresiones de eliminación en el momento del análisis sintáctico.
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 semántica de eliminación sigue una coincidencia exacta de tres valores:
| Valor del campo | membership_match(…) | no membership_match(…) |
|---|---|---|
| NULL | sin coincidencia (NULL) | sin coincidencia (NULL) |
| en el conjunto de miembros | coincidencia | sin coincidencia |
| fuera del conjunto | sin coincidencia | coincide |
La tabla se lee fila por fila: un «Delete» con « membership_match » elimina las entidades incluidas en el conjunto, un «Delete» con « not membership_match » elimina las entidades fuera del conjunto, y las entidades NULL se conservan en ambas polaridades.
- Las entidades con un valor de campo NULL nunca se eliminan, sea cual sea la polaridad.
- Un mapa de bits vacío no elimina nada. «
not membership_match(<field>, {<empty>}, type=roaring)» elimina todas las entidades con un valor de campo distinto de NULL, es decir, el complemento exacto, que puede ser un conjunto grande por diseño. not membership_matches el complemento exacto del conjunto de miembros sobre las entidades no NULL, no una aproximación.
En segmentos sellados cargados solo por índice, sin datos escalares sin procesar, la evaluación utiliza una búsqueda inversa acotada cuando el índice lo admite. Cuando no se dispone ni de datos de campo sin procesar ni de un índice válido, la solicitud falla con « FieldNotLoaded » en lugar de devolver entidades sin filtrar.
Límites operativos
| Configuración | Por defecto | Configurable | Lo que limita |
|---|---|---|---|
proxy.maxMembershipFilterSize | 64 MiB | milvus.yaml proxy | un cuerpo de blob |
proxy.maxMembershipFilterPlanSize | 128 MiB | milvus.yaml proxy | agregado por solicitud |
proxy.grpc.serverMaxRecvSize | 128 MiB | milvus.yaml proxy | solicitud completa |
| Límite máximo de contenedores de Roaring | 262 144 | corregido en el código | por filtro |
| Estimación de memoria descodificada de Roaring | 64 MiB | fijo en el código | por filtro |
| Límite del formato MBF1 | 128 MiB | fijado en el código | límite máximo del formato blob |
Bloom fpr | 0,005 | generador de cliente | objetivo de falsos positivos |
Los ajustes de « proxy.* » (Límite de formato de Roaring y MBF1) forman parte de la configuración del servidor Milvus: los establece el operador del clúster en milvus.yaml, en la sección « proxy », y no se pueden ajustar por solicitud o por expresión en el momento del filtrado. Las admisiones «Roaring» y el límite del formato MBF1 están fijados en la implementación y no se pueden configurar en ningún sitio. La fila « fpr » es el único control del lado del cliente: se elige al crear el blob, con un rango aceptado de 0,0001 a 0,05.
El cuerpo del blob admite como máximo unos 48,6 millones de miembros INT64 con el fpr predeterminado, a lo que hay que añadir la cabecera fija de 32 bytes permitida. El presupuesto para toda la solicitud cubre el tamaño serializado agregado de los planes que contienen miembros, más el tamaño estimado descodificado agregado de los filtros Roaring; las subsolicitudes de HybridSearch y los filtros de puntuación lo comparten. Los cuerpos de los blobs Bloom son potencias de dos: un recuento de miembros que supere ligeramente el límite de un nivel duplica el tamaño del blob, y un fpr ligeramente superior suele encajar en el nivel inmediatamente inferior. Cuando el proxy rechaza un blob Bloom de tamaño excesivo, el error sugiere un « fpr » que se ajusta a los requisitos.
- Los resultados de la expresión de pertenencia no se almacenan en caché entre solicitudes.
- Los clústeres de versiones mixtas fallan en modo cerrado: los QueryNodes que ejecutan código más antiguo rechazan los planes que contienen nodos de plan de pertenencia. Actualiza primero los proxies y, a continuación, completa la actualización progresiva antes de habilitar el operador.
- Los registros del servidor ocultan los bytes de los blobs. El texto de la expresión contiene únicamente el nombre de la plantilla; la carga útil binaria se transmite fuera de banda en los valores de la plantilla.