Agregación de búsquedasCompatible with Milvus 3.0.x

Cuando un comprador busca «zapatillas de running negras para el entrenamiento diario», la búsqueda por vecino más cercano aproximado (ANN) clasifica los productos según la similitud vectorial y devuelve una lista plana de los Top-K. Los resultados pueden ser relevantes, pero repetitivos: en el ejemplo siguiente, cuatro de los seis primeros resultados son productos de la marca A, mientras que la marca B y la marca C aparecen una vez cada una.

Una lista plana no puede proporcionar directamente un resumen organizado por categorías. Es posible que una aplicación necesite comparar marcas según el número de candidatos retenidos o el precio medio, examinar un pequeño número de productos representativos de cada marca u organizar los resultados en varios niveles de categorías.

La agregación de búsquedas organiza los candidatos ANN seleccionados en grupos en función de campos escalares seleccionados. En este ejemplo, cada marca se convierte en un grupo independiente. Milvus puede calcular estadísticas para cada grupo, ordenar los grupos y asociarles productos representativos. La aplicación consume esta respuesta «por grupos primero» a través de result.agg_buckets.

A flat running-shoe search result becomes a set of comparable brand buckets Un resultado plano de búsqueda de zapatillas de correr se convierte en un conjunto de grupos de marcas comparables

La agregación de búsqueda no realiza una agregación exacta de toda la colección. La existencia de los grupos, los recuentos, las métricas, el orden y los resultados representativos dependen de los candidatos retenidos por la red neuronal artificial (ANN) y de las etapas de agrupación.

Cómo funciona

ANN candidates grouped by bucket keys and returned with counts, metrics, and representative hits Candidatos de la ANN agrupados por claves de grupo y devueltos con recuentos, métricas y resultados representativos

  1. Recuperación de candidatos. Milvus ejecuta una búsqueda ANN para encontrar las entidades más cercanas al vector de consulta. A continuación, la etapa de agrupación retiene un número limitado de candidatos para cada clave compuesta completa. Este presupuesto de candidatos por clave es el mayor TopHits.size en cualquier parte del árbol de agregación, o 1 cuando ningún nivel configura top_hits.

  2. Creación de cubos. SearchAggregation.fields define la clave del cubo. Cada combinación única de valores de campo crea una clave independiente. En la figura, fields=["brand"] crea las claves de cubo (Brand A), (Brand B) y (Brand C). Los candidatos retenidos con la misma clave pertenecen al mismo cubo y contribuyen a su count. SearchAggregation.size limita el número de cubos que devuelve Milvus.

  3. Calcular y devolver los resultados. Cada bucket devuelto contiene su clave y el recuento de candidatos retenidos. Milvus también puede calcular métricas configuradas, ordenar los buckets, devolver entidades representativas y crear buckets secundarios. Cada AggregationBucket en result.agg_buckets expone key, count, metrics, hits y sub_groups. Cuando la agregación de búsqueda está habilitada, la lista habitual de resultados de búsqueda está vacía.

En el diagrama, TopHits.size=4 proporciona un presupuesto de candidatos por clave de cuatro, por lo que los cuatro candidatos retenidos de la Marca A generan count: 4. La ficha completa de la Marca A muestra solo dos de los cuatro resultados representativos devueltos para que la figura resulte más compacta.

Con « sub_aggregation », Milvus repite los pasos 2 y 3 dentro de cada grupo principal. Los cambios en la recuperación de la red neuronal artificial (ANN) o en el presupuesto de candidatos por clave pueden modificar el número de grupos, las métricas, el orden, los resultados y los resultados anidados.

Límites

Antes de utilizar la agregación de búsqueda, ten en cuenta los siguientes límites:

  • Agregaciones anidadas: una solicitud puede contener una « SearchAggregation » raíz y hasta tres niveles anidados de « sub_aggregation », con un máximo de cuatro niveles en total. En todos los niveles, se pueden utilizar como máximo 10 campos para crear claves de grupo.

  • Campos utilizados para crear claves de bucket: « SearchAggregation.fields » admite campos booleanos, enteros, « VARCHAR » y « TIMESTAMPTZ ». No admite campos « FLOAT », « DOUBLE », « ARRAY », « JSON », « GEOMETRY », « TEXT », vectoriales ni dinámicos.

  • Campos métricos: count admite "*" o cualquier campo que no sea de tipoJSON ni dinámico, y omite los valores de NULL cuando se especifica un campo. sum y avg admiten campos de tipo entero y de coma flotante. min y max admiten además campos de tipo cadena y TIMESTAMPTZ.

  • Campos de ordenación de «Top Hits»: « TopHits.sort » admite campos comparables de tipo booleano, entero, de coma flotante, cadena y « TIMESTAMPTZ », además de « _score ». No admite « ARRAY », « JSON », « GEOMETRY », ni campos vectoriales o dinámicos.

  • Presupuesto de candidatos: El mayor valor de « TopHits.size » en cualquier parte del árbol de agregación es también el número de candidatos retenidos por cada clave compuesta completa. Si ningún nivel configura « top_hits », Milvus retiene un candidato por clave. El « count » del bucket y las métricas se calculan a partir de estos candidatos retenidos, por lo que cambiar « TopHits.size » puede modificarlos.

  • Campos de bucket nulos: un valor « NULL » forma su propia clave de bucket. Para excluir el bucket nulo, añade un filtro como « brand is not null » a la solicitud de búsqueda.

  • Campos repetidos: un mismo campo no puede aparecer en más de una lista de « SearchAggregation.fields ». Por ejemplo, si la agregación raíz utiliza fields=["category"], una agregación anidada sub_aggregation no puede utilizar también fields=["category"].

  • Combinaciones no admitidas: La agregación de búsqueda no se puede combinar con un ` offset` distinto de cero, iteradores de búsqueda, búsqueda híbrida, un resaltador o búsqueda por agrupación. Un valor de nivel superior de ` offset ` igual a ` 0 ` equivale a omitir el parámetro. En las solicitudes de búsqueda de REST v2, no se pueden especificar conjuntamente ` searchAggregation ` y ` ids `.

  • Entradas devueltas: Por defecto, Milvus rechaza una solicitud de «Search Aggregation» cuando el número máximo calculado de entradas de resultado de la solicitud supera las 10 000. Este umbral se controla mediante proxy.maxSearchAggregationResultEntries. Establezca el valor de configuración en 0 o en un número negativo para desactivar esta comprobación.

    Milvus calcula este máximo de la siguiente manera:

    number of query vectors × product of the effective search_size at every aggregation level × largest TopHits.size at any level

    Para este cálculo del lado del servidor, el valor efectivo de « search_size » en un nivel es el valor de « search_size » configurado explícitamente, o el valor de « size » de ese nivel cuando se omite « search_size ». La API de PyMilvus utilizada en esta guía no expone actualmente « search_size », por lo que las solicitudes de PyMilvus utilizan el valor de « size » de cada nivel para este cálculo. Utiliza 1 como último factor cuando ningún nivel configure TopHits. Por ejemplo, un vector de consulta, 10 buckets raíz, cinco buckets secundarios por cada bucket raíz y dos aciertos por cada bucket secundario dan como resultado un máximo calculado de:

    1 × 10 × 5 × 2 = 100

Utilizar la agregación de búsqueda

Elige un ejemplo en función de lo que quieras conseguir:

Vaya aDescripciónConfiguración clave
Comparar y ordenar bucketsCalcula estadísticas por bucket para compararlos y, a continuación, ordena los buckets obtenidos por métricas, recuentos o claves.fields, size, metrics, order
Mostrar resultados representativos de cada bucketDevuelve un número limitado de entidades de cada bucket y ordena dichas entidades de forma independiente por campos escalares o puntuación vectorial.top_hits, TopHits.size, TopHits.sort
Agrupar los resultados en varios nivelesOrganiza los resultados en niveles de grupos principales y secundarios para analizar varias dimensiones de forma secuencial.sub_aggregation

Los ejemplos que se muestran a continuación utilizan una colección de productos con campos de marca, categoría, color, precio y valoración. Todos los nombres de marcas, nombres de productos, precios, valoraciones y resultados de búsqueda son datos de ejemplo sintéticos. Expande la siguiente sección para crear la colección y definir las variables de búsqueda compartidas.

Configurar la colección de ejemplo

from pymilvus import DataType, MilvusClient, SearchAggregation, TopHits

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus",
)

collection_name = "product_search_aggregation"

if client.has_collection(collection_name):
    client.drop_collection(collection_name)

schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=5)
schema.add_field("name", DataType.VARCHAR, max_length=200)
schema.add_field("brand", DataType.VARCHAR, max_length=100)
schema.add_field("category", DataType.VARCHAR, max_length=100)
schema.add_field("color", DataType.VARCHAR, max_length=50)
schema.add_field("price", DataType.DOUBLE)
schema.add_field("rating", DataType.DOUBLE)
schema.add_field("in_stock", DataType.BOOL)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="embedding",
    index_type="AUTOINDEX",
    metric_type="COSINE",
)

client.create_collection(
    collection_name=collection_name,
    schema=schema,
    index_params=index_params,
    # Make preceding writes visible to searches from this client.
    consistency_level="Session",
)

client.insert(
    collection_name=collection_name,
    data=[
        {
            "id": 1,
            "embedding": [0.12, 0.42, 0.18, 0.66, 0.31],
            "name": "Runner A1",
            "brand": "Brand A",
            "category": "running_shoes",
            "color": "black",
            "price": 129.99,
            "rating": 4.7,
            "in_stock": True,
        },
        {
            "id": 2,
            "embedding": [0.10, 0.39, 0.20, 0.61, 0.29],
            "name": "Trail A2",
            "brand": "Brand A",
            "category": "running_shoes",
            "color": "blue",
            "price": 139.99,
            "rating": 4.6,
            "in_stock": True,
        },
        {
            "id": 3,
            "embedding": [0.14, 0.44, 0.19, 0.68, 0.33],
            "name": "Runner B1",
            "brand": "Brand B",
            "category": "running_shoes",
            "color": "white",
            "price": 159.99,
            "rating": 4.8,
            "in_stock": True,
        },
        {
            "id": 4,
            "embedding": [0.16, 0.41, 0.22, 0.62, 0.30],
            "name": "Runner C1",
            "brand": "Brand C",
            "category": "running_shoes",
            "color": "red",
            "price": 119.99,
            "rating": 4.4,
            "in_stock": False,
        },
        {
            "id": 5,
            "embedding": [0.48, 0.20, 0.59, 0.15, 0.71],
            "name": "Jacket A1",
            "brand": "Brand A",
            "category": "jackets",
            "color": "black",
            "price": 99.99,
            "rating": 4.5,
            "in_stock": True,
        },
        {
            "id": 6,
            "embedding": [0.45, 0.18, 0.55, 0.17, 0.69],
            "name": "Jacket B1",
            "brand": "Brand B",
            "category": "jackets",
            "color": "blue",
            "price": 89.99,
            "rating": 4.3,
            "in_stock": True,
        },
        {
            "id": 7,
            "embedding": [0.09, 0.38, 0.17, 0.60, 0.27],
            "name": "Runner A3",
            "brand": "Brand A",
            "category": "running_shoes",
            "color": "black",
            "price": 159.99,
            "rating": 4.8,
            "in_stock": True,
        },
        {
            "id": 8,
            "embedding": [0.13, 0.43, 0.21, 0.65, 0.32],
            "name": "Runner A4",
            "brand": "Brand A",
            "category": "running_shoes",
            "color": "black",
            "price": 149.99,
            "rating": 4.9,
            "in_stock": True,
        },
    ],
)

client.load_collection(collection_name)

query_vector = [0.11, 0.40, 0.19, 0.64, 0.30]
search_params = {
    "metric_type": "COSINE",
    "params": {},
}

La configuración anterior configura COSINE tanto para el índice vectorial como para los parámetros de búsqueda. Por lo tanto, los ejemplos posteriores utilizan {"_score": "desc"} para dar prioridad a una mayor similitud coseno. Para una métrica de distancia como L2, utilice {"_score": "asc"}.

Comparar y ordenar grupos

Utilice este patrón cuando necesite comparar grupos de entidades recuperadas utilizando estadísticas calculadas y controlar el orden en el que se devuelven los grupos. En este ejemplo, Milvus agrupa los productos recuperados por brand, calcula métricas de precio para cada grupo de marcas y ordena los grupos por precio medio.

Si tu objetivo es únicamente mejorar la diversidad de los resultados devolviendo una o más entidades por cada valor de campo, utiliza en su lugar la «Búsqueda por agrupación ».

La siguiente configuración crea hasta tres grupos de marcas, calcula métricas para cada grupo y ordena los grupos por precio medio:

aggregation = SearchAggregation(
    # Form one bucket for each distinct brand value.
    fields=["brand"],
    # Return up to three buckets at this aggregation level.
    size=3,
    # Calculate named metrics for every selected bucket.
    metrics={
        "product_count": {"count": "*"},
        "avg_price": {"avg": "price"},
        "min_price": {"min": "price"},
    },
    # Sort buckets by average price, highest first.
    order=[
        {"avg_price": "desc"},
        # If average prices are equal, sort by bucket key in ascending order.
        {"_key": "asc"},
    ],
)

Pasa el objeto al parámetro « search_aggregation » de « MilvusClient.search() »:

result = client.search(
    collection_name=collection_name,
    data=[query_vector],
    anns_field="embedding",
    search_params=search_params,
    output_fields=[
        "name",
        "brand",
        "category",
        "color",
        "price",
        "rating",
        "in_stock",
    ],
    search_aggregation=aggregation,
)

Cuando se establece « search_aggregation », PyMilvus no devuelve resultados de entidades ordinarias en « result[0] ». Lee la respuesta de los grupos en « result.agg_buckets[0] » en su lugar. El parámetro « output_fields » controla qué campos escalares aparecen en cada asignación « AggregationHit.fields » devuelta; Milvus puede seguir utilizando campos de origen de métricas y de ordenación que no figuren en « output_fields ».

Ver el ejemplo de salida del bucket

La siguiente salida se ha capturado a partir de la solicitud anterior y se ha serializado como JSON para facilitar su lectura. PyMilvus devuelve objetos ` AggregationBucket ` en lugar de JSON. El valor ` key ` es siempre una lista ordenada de componentes de clave, incluso cuando ` fields ` contiene solo un campo. Esto conserva el orden de los campos para las claves compuestas.

[
  {
    "key": [
      {
        "field_id": 103,
        "field_name": "brand",
        "value": "Brand B"
      }
    ],
    "count": 1,
    "metrics": {
      "product_count": 1,
      "avg_price": 159.99,
      "min_price": 159.99
    },
    "hits": [],
    "sub_groups": []
  },
  {
    "key": [
      {
        "field_id": 103,
        "field_name": "brand",
        "value": "Brand A"
      }
    ],
    "count": 1,
    "metrics": {
      "product_count": 1,
      "avg_price": 129.99,
      "min_price": 129.99
    },
    "hits": [],
    "sub_groups": []
  },
  {
    "key": [
      {
        "field_id": 103,
        "field_name": "brand",
        "value": "Brand C"
      }
    ],
    "count": 1,
    "metrics": {
      "product_count": 1,
      "avg_price": 119.99,
      "min_price": 119.99
    },
    "hits": [],
    "sub_groups": []
  }
]

Para el vector de consulta único de esta guía, lee los buckets de nivel superior devueltos en result.agg_buckets[0]. Cada bucket expone sus componentes de clave ordenados, el candidato retenido count, el valor calculado metrics, el valor representativo hits y los buckets anidados en sub_groups.

Lee la configuración de la siguiente manera:

ConfiguraciónQué controlaEn este ejemplo
fieldsCómo crea Milvus las claves de los bucketsCrea un depósito para cada valor distinto de « brand ».
sizeNúmero máximo de buckets devueltosDevuelve hasta tres buckets de marca.
metricsLas estadísticas calculadas para cada bucketCalcula el número de productos, el precio medio y el precio mínimo.
orderCómo ordena Milvus los segmentos devueltosOrdena por precio medio y, a continuación, utiliza la clave del grupo para desempatar.

Milvus ignora limit cuando se establece search_aggregation. Utiliza el valor raíz SearchAggregation.size para controlar el número de grupos de nivel superior.

Con esta configuración, Milvus devuelve los buckets «Marca B», «Marca A» y «Marca C» en orden descendente según el avg_price. El criterio « _key » solo se aplica cuando los buckets tienen el mismo precio medio. Dado que esta configuración no define « top_hits », la lista « hits » de cada bucket está vacía y el presupuesto candidato por clave es « 1 ». Por lo tanto, los recuentos y métricas mostrados describen un candidato retenido por marca. Configura « top_hits » con un « TopHits.size » mayor cuando la agregación necesite una ventana de métricas por clave más amplia.

Reglas de métricas y ordenación

Cada entrada de SearchAggregation.metrics asigna un alias definido por el usuario a {operation: source}:

FuenteOperaciones admitidasComportamiento
Cualquier campo que no sea «JSON » ni dinámicocountCuenta los candidatos retenidos cuyo campo de origen no sea « NULL ».
Campo de tipo entero o de coma flotantesum, « avg », « min », maxRealiza el cálculo sobre los valores retenidos no nulos.
Campo de cadena o « TIMESTAMPTZ »min, maxSelecciona el valor retenido no nulo mínimo o máximo.
"*"countCuenta cada candidato retenido en el bucket. El resultado coincide con bucket.count.
_scoresum, avg, min, maxAgrega los valores de similitud o distancia ANN de los candidatos retenidos.

SearchAggregation.order Acepta las siguientes claves:

Clave de ordenSignificado
Un alias de métricaOrdena según un valor calculado en metrics al mismo nivel de agregación, como avg_price.
_countOrdena según el número de candidatos retenidos en cada compartimento.
_keyOrdena por la clave del bucket en lugar de por un campo de la colección denominado « _key ».

Cada entrada de « order » asigna una clave a « "asc" » o « "desc" ». Milvus evalúa varias entradas de la primera a la última. Si se omite « order », Milvus mantiene el orden de descubrimiento de los buckets del conjunto de candidatos retenidos.

Para ordenar los buckets según la calidad de la coincidencia de vectores, calcula primero una métrica a nivel de bucket a partir de _score y, a continuación, utiliza el alias de la métrica en order. No puedes utilizar _score directamente como clave de ordenación de buckets, ya que cada bucket puede contener varias puntuaciones de entidades. Por ejemplo, con COSINE o IP:

aggregation = SearchAggregation(
    fields=["brand"],
    size=3,
    metrics={"max_score": {"max": "_score"}},
    order=[{"max_score": "desc"}],
)

Con L2, calcula el valor mínimo de _score y ordena el alias de la métrica en orden ascendente, de modo que los buckets con la distancia más baja aparezcan en primer lugar.

Crear claves de compartimento compuestas

Para crear una clave de bucket compuesta, introduzca varios nombres de campo en la misma lista:

aggregation = SearchAggregation(
    # Combine brand and color to form a composite bucket key.
    fields=["brand", "color"],
    size=6,
)

Esta configuración puede generar claves como (Brand A, black), (Brand A, blue) y (Brand B, white). Dos entidades comparten un compartimento solo cuando ambos valores coinciden. Milvus conserva el orden de la lista, por lo que brand es el primer componente de la clave y color es el segundo. Cuando se utiliza _key en order, Milvus compara los componentes de la clave compuesta en el mismo orden. Pasa varias cadenas en una lista plana; no se admiten listas anidadas.

size=6 es el número máximo de compartimentos compuestos devueltos en este nivel de agregación. Los datos de ejemplo contienen cinco combinaciones distintas de marca y color, por lo que se pueden devolver las cinco. En el límite de entradas devueltas, esta solicitud aporta 1 query vector × 6 buckets × 1 = 6 entradas de resultado configuradas.

Varios campos en una lista « SearchAggregation.fields » crean una clave de compartimento compuesto en ese nivel de agregación. Para crear una jerarquía de compartimentos padre-hijo, utiliza una agregación anidada.

Los ejemplos que siguen redefinen ` aggregation`. Pase el objeto actualizado al mismo parámetro ` search_aggregation ` y vuelva a ejecutar la llamada de búsqueda.

Mostrar resultados representativos de cada grupo

Incluya entidades representativas cuando la aplicación necesite mostrar productos reales de cada cubo. En este ejemplo, Milvus devuelve hasta dos productos de cada cubo de marca, ordenados por valoración y, a continuación, por puntuación vectorial.

Configura TopHits de la siguiente manera:

aggregation = SearchAggregation(
    fields=["brand"],
    size=3,
    # Return and sort representative entities for each selected bucket.
    top_hits=TopHits(
        # Return up to two entities per bucket.
        size=2,
        # Apply sort criteria in list order.
        sort=[
            {"rating": "desc"},
            {"_score": "desc"},
        ],
    ),
)

Ver un grupo con resultados representativos

El siguiente grupo de la marca A se ha extraído de la solicitud anterior y se ha serializado como JSON para facilitar su lectura.

{
  "key": [
    {
      "field_id": 103,
      "field_name": "brand",
      "value": "Brand A"
    }
  ],
  "count": 2,
  "metrics": {},
  "hits": [
    {
      "pk": 1,
      "score": 0.99976646900177,
      "fields": {
        "brand": "Brand A",
        "category": "running_shoes",
        "color": "black",
        "in_stock": true,
        "name": "Runner A1",
        "price": 129.99,
        "rating": 4.7
      }
    },
    {
      "pk": 2,
      "score": 0.9997048377990723,
      "fields": {
        "brand": "Brand A",
        "category": "running_shoes",
        "color": "blue",
        "in_stock": true,
        "name": "Trail A2",
        "price": 139.99,
        "rating": 4.6
      }
    }
  ],
  "sub_groups": []
}

ParámetroPropósito
top_hitsOpcional. Configura las entidades representativas para este nivel de agregación. Si se omite, « bucket.hits » queda vacío y el presupuesto candidato por clave se establece por defecto en uno.
TopHits.sizeDevuelve hasta dos entidades representativas de cada grupo seleccionado y establece el presupuesto candidato por clave en dos para todo el árbol de agregación.
TopHits.sortOrdena las entidades dentro de cada grupo según los criterios indicados.

Configura « top_hits » cuando la aplicación necesite entidades representativas o cuando los recuentos y las métricas requieran una ventana de candidatos por clave más amplia. Un valor mayor de « TopHits.size » aumenta tanto el presupuesto de candidatos como el cálculo del número máximo de entradas devueltas en «Limits».

SearchAggregation.order «sorts buckets», mientras que « TopHits.sort » ordena las entidades retenidas dentro de cada bucket. El orden de clasificación no cambia qué candidatos se han retenido para « count » y las métricas. « TopHits.sort » acepta nombres de campos escalares comparables compatibles y el campo integrado « _score », que representa la similitud o distancia ANN. Milvus evalúa las entradas de « sort » de la primera a la última. En este ejemplo, ordena los productos por rating de mayor a menor y utiliza _score solo cuando dos valoraciones son iguales. Dado que la configuración utiliza COSINE, el orden descendente _score coloca en primer lugar el producto más similar.

Los campos utilizados por metrics o TopHits.sort no tienen por qué aparecer en output_fields. Milvus recupera esos campos internamente, pero solo los campos enumerados explícitamente en output_fields se incluyen en la asignación fields de cada resultado devuelto. Las claves primarias y las puntuaciones vectoriales siguen estando disponibles a través de AggregationHit.pk y AggregationHit.score.

Cada resultado devuelto AggregationHit expone su clave primaria en pk, la puntuación vectorial en score y los campos de salida solicitados en fields.

Agrupar resultados en varios niveles

Utiliza la agregación anidada cuando necesites un nivel de buckets dentro de otro. En este ejemplo, Milvus crea primero los buckets de categoría y, a continuación, crea los buckets de marca dentro de cada categoría.

La agregación secundaria solo recibe las entidades asignadas a su grupo principal. fields controla la clave del grupo en cada nivel de agregación, mientras que sub_aggregation crea la jerarquía principal-secundaria.

La configuración siguiente crea un bucket de categoría con la clave (running_shoes). Dentro de ese bucket principal, la agregación secundaria crea buckets de marca independientes con claves como (Brand A), (Brand B) y (Brand C).

Parent bucket key:
(running_shoes)

Child bucket keys:
├── (Brand A)
├── (Brand B)
└── (Brand C)

Cada nivel puede utilizar varios campos de forma independiente. Por ejemplo, el uso de fields=["brand", "color"] en la agregación secundaria crearía claves secundarias compuestas como (Brand A, black).

La siguiente configuración implementa esta jerarquía:

aggregation = SearchAggregation(
    fields=["category"],
    size=2,
    metrics={
        "product_count": {"count": "*"},
        "avg_price": {"avg": "price"},
    },
    order=[{"product_count": "desc"}],
    # For each category bucket, group only its entities by brand.
    sub_aggregation=SearchAggregation(
        fields=["brand"],
        size=3,
        metrics={
            "brand_count": {"count": "*"},
            "avg_rating": {"avg": "rating"},
        },
        order=[{"avg_rating": "desc"}],
        top_hits=TopHits(
            size=2,
            sort=[{"rating": "desc"}],
        ),
    ),
)

Ver el resultado de un bucket anidado

El siguiente extracto serializado muestra el bucket principal running_shoes y su bucket secundario «Brand B». Los buckets secundarios «Brand A» y «Brand C» se omiten por brevedad.

{
  "key": [
    {
      "field_id": 104,
      "field_name": "category",
      "value": "running_shoes"
    }
  ],
  "count": 4,
  "metrics": {
    "avg_price": 137.49,
    "product_count": 4
  },
  "hits": [],
  "sub_groups": [
    {
      "key": [
        {
          "field_id": 103,
          "field_name": "brand",
          "value": "Brand B"
        }
      ],
      "count": 1,
      "metrics": {
        "avg_rating": 4.8,
        "brand_count": 1
      },
      "hits": [
        {
          "pk": 3,
          "score": 0.9994542598724365,
          "fields": {
            "brand": "Brand B",
            "category": "running_shoes",
            "color": "white",
            "in_stock": true,
            "name": "Runner B1",
            "price": 159.99,
            "rating": 4.8
          }
        }
      ],
      "sub_groups": []
    }
  ]
}

El resultado mostrado representa la ruta del bucket (running_shoes) → (Brand B), no una única clave compuesta de bucket (running_shoes, Brand B).

Milvus selecciona primero hasta dos buckets de categoría, ordenados por product_count. A continuación, ejecuta sub_aggregation de forma independiente dentro de cada categoría seleccionada y devuelve hasta tres buckets de marca, ordenados por avg_rating.

En la salida anterior:

  • El grupo raíz « running_shoes » contiene cuatro candidatos retenidos en sus claves compuestas secundarias. Sus « metrics » contienen los valores de nivel raíz « avg_price » y « product_count ».
  • La lista « sub_groups » del bucket raíz contiene los buckets de marca secundarios. El bucket «Brand B» que se muestra contiene un candidato retenido y sus propios valores « avg_rating » y « brand_count ».
  • La lista « hits » del bucket raíz está vacía porque la agregación raíz no configura « top_hits ». El bucket secundario de la marca B contiene un resultado representativo porque « top_hits » está configurado en « sub_aggregation ».

Preguntas frecuentes

¿Qué grado de precisión tienen los recuentos y las métricas de los buckets?

La agregación de búsqueda resume los candidatos de la red neuronal artificial (ANN) retenidos. No ejecuta una agregación de la colección completa.

La retención de candidatos tiene dos etapas de aproximación. La búsqueda ANN puede omitir entidades relevantes de la colección, y la etapa de agrupación retiene, como máximo, los candidatos más grandes TopHits.size para cada clave compuesta completa. Si ningún nivel configura top_hits, este límite por clave es uno.

Por ejemplo, supongamos que una colección contiene 5.000 productos de la marca A y que muchos de ellos son relevantes para la consulta vectorial. Si la agregación utiliza ` TopHits(size=4)`, el compartimento de la marca A puede retener como máximo cuatro candidatos para una clave compuesta completa. Su ` count ` y sus métricas describen esos candidatos retenidos, no todos los productos relevantes de la marca A ni las 5.000 entidades de la colección.

La aproximación cobra mayor importancia cuando « order » utiliza un alias de métrica. Los cambios en la recuperación de la búsqueda pueden modificar los valores de las métricas y, por lo tanto, alterar qué buckets se ajustan a « SearchAggregation.size ». La agregación anidada puede amplificar este efecto, ya que cada nivel secundario opera sobre las entidades disponibles en su bucket principal.

Si necesitas estadísticas exactas sobre cada entidad coincidente, utiliza un flujo de trabajo de agregación de consultas exactas en lugar de la agregación de búsqueda.

Elige en función del formato de resultados principal de la aplicación:

Necesidad principalPreferirRespuesta a consumir
Devuelve una lista de entidades clasificada de forma estándar con menos valores repetidos en un campo de agrupaciónBúsqueda por agrupaciónResultados de búsqueda planos para cada vector de consulta
Inspeccionar o comparar grupos como compartimentos, con claves, recuentos, métricas, ordenación, resultados representativos o compartimentos secundariosAgregación de búsquedaAggregationBucket objetos en result.agg_buckets

Incluso cuando la agregación de búsqueda configura « top_hits », su respuesta principal sigue siendo un árbol de compartimentos. La búsqueda por agrupación sigue siendo útil cuando la aplicación ya procesa resultados de búsqueda ordinarios y busca principalmente diversidad en los resultados.

Las API son mutuamente excluyentes. PyMilvus genera un error « ParamError » cuando se combina « search_aggregation » con « group_by_field » o « group_by_fields » en la misma solicitud.