Agrégation des résultats de rechercheCompatible with Milvus 3.0.x
Lorsqu'un acheteur recherche « des chaussures de course noires pour l'entraînement quotidien », la recherche par voisin le plus proche (ANN) classe les produits en fonction de la similarité de leurs vecteurs et renvoie une liste plate des Top-K. Les résultats peuvent être pertinents mais répétitifs : dans l'exemple ci-dessous, quatre des six premiers résultats sont des produits de la marque A, tandis que les marques B et C n'apparaissent qu'une seule fois chacune.
Une liste plate ne permet pas de fournir directement un résumé organisé par catégories. Une application peut avoir besoin de comparer les marques en fonction du nombre de candidats retenus ou du prix moyen, d’examiner un petit nombre de produits représentatifs de chaque marque, ou d’organiser les résultats en plusieurs niveaux de catégories.
L’agrégation de recherche organise les candidats ANN retenus en segments en fonction de champs scalaires sélectionnés. Dans cet exemple, chaque marque devient un segment distinct. Milvus peut calculer des statistiques pour chaque segment, classer les segments et y associer des produits représentatifs. L’application exploite cette réponse axée sur les segments via result.agg_buckets.
Un résultat de recherche plat sur les chaussures de course se transforme en un ensemble de catégories de marques comparables
L’agrégation de recherche n’effectue pas d’agrégation exacte sur l’ensemble de la collection. L’existence des compartiments, leur nombre, leurs métriques, leur classement et les résultats représentatifs dépendent des candidats retenus par les étapes du réseau neuronal artificiel (ANN) et de regroupement.
Fonctionnement
Candidats ANN regroupés par clés de compartiment et renvoyés avec leurs nombres d’occurrences, leurs métriques et leurs résultats représentatifs
Récupération des candidats. Milvus exécute une recherche ANN pour trouver les entités les plus proches du vecteur de requête. L’étape de regroupement retient ensuite un nombre limité de candidats pour chaque clé composite complète. Ce quota de candidats par clé correspond à la plus grande valeur de `
TopHits.size` n’importe où dans l’arborescence d’agrégation, ou à `1` lorsqu’aucun niveau ne configure `top_hits`.Création des compartiments.
SearchAggregation.fieldsdéfinit la clé du compartiment. Chaque combinaison unique de valeurs de champs crée une clé distincte. Dans la figure,fields=["brand"]génère les clés de compartiment(Brand A),(Brand B)et(Brand C). Les candidats retenus ayant la même clé appartiennent au même compartiment et contribuent à soncount.SearchAggregation.sizelimite le nombre de compartiments renvoyés par Milvus.Calculer et renvoyer les résultats. Chaque compartiment renvoyé contient sa clé et le nombre de candidats conservés. Milvus peut également calculer les métriques configurées, trier les compartiments, renvoyer des entités représentatives et créer des compartiments enfants. Chaque
AggregationBucketdansresult.agg_bucketsexposekey,count,metrics,hitsetsub_groups. Lorsque l’agrégation de recherche est activée, la liste habituelle des résultats de recherche est vide.
Dans le schéma, TopHits.size=4 fournit un budget de candidats de quatre par clé ; ainsi, les quatre candidats retenus pour la marque A produisent count: 4. La fiche complète de la marque A n’affiche que deux des quatre résultats représentatifs renvoyés afin de conserver une présentation concise.
Avec « sub_aggregation », Milvus répète les étapes 2 et 3 à l’intérieur de chaque compartiment parent. Les variations du rappel du réseau neuronal artificiel (ANN) ou du budget de candidats par clé peuvent modifier le nombre de compartiments, les métriques, l’ordre, les résultats et les résultats imbriqués.
Limites
Avant d’utiliser l’agrégation de recherche, veuillez noter les limites suivantes :
Agrégations imbriquées : une requête peut contenir une agrégation racine «
SearchAggregation» et jusqu’à trois niveaux imbriqués «sub_aggregation», pour un maximum de quatre niveaux au total. Sur l’ensemble des niveaux, 10 champs au maximum peuvent être utilisés pour créer des clés de compartiment.Champs utilisés pour créer des clés de compartiment : «
SearchAggregation.fields» prend en charge les champs booléens, entiers, «VARCHAR» et «TIMESTAMPTZ». Il ne prend pas en charge les champs «FLOAT», «DOUBLE», «ARRAY», «JSON», «GEOMETRY», «TEXT», vectoriels ou dynamiques.Champs métriques :
countaccepte les champs de type «"*"» ou tout champ non «JSON» et non dynamique, et ignore les valeurs de type «NULL» lorsqu’un champ est spécifié.sumetavgacceptent les champs de type entier et à virgule flottante.minetmaxacceptent en outre les champs de type chaîne de caractères et «TIMESTAMPTZ».Champs de tri des meilleurs résultats :
TopHits.sortaccepte les champs comparables de type booléen, entier, à virgule flottante, chaîne de caractères etTIMESTAMPTZ, ainsi que_score. Il ne prend pas en charge les champs de typeARRAY,JSON,GEOMETRY, vectoriel ou dynamique.Budget de candidats : la plus grande valeur de `
TopHits.size` dans l’arborescence d’agrégation correspond également au nombre de candidats conservés par clé composite complète. Si aucun niveau ne configure `top_hits`, Milvus conserve un candidat par clé. Le `count` des compartiments et les métriques sont calculés à partir de ces candidats conservés ; par conséquent, la modification de `TopHits.size` peut les modifier.Champs de compartiment pouvant prendre la valeur « null » : une valeur «
NULL» forme sa propre clé de compartiment. Pour exclure le compartiment « null », ajoutez un filtre tel que «brand is not null» à la requête de recherche.Champs répétés : un même champ ne peut pas apparaître dans plusieurs listes d’
SearchAggregation.fields. Par exemple, si l’agrégation racine utilisefields=["category"], une agrégation imbriquéesub_aggregationne peut pas également utiliserfields=["category"].Combinaisons non prises en charge : l’agrégation de recherche ne peut pas être combinée avec une agrégation de recherche de niveau supérieur (
offset) différente de zéro, des itérateurs de recherche (Search Iterators), la recherche hybride (Hybrid Search), un surligneur (Highlighter) ou la recherche par regroupement (Grouping Search). Une valeur de niveau supérieuroffsetégale à0équivaut à omettre le paramètre. Dans les requêtes de recherche REST v2,searchAggregationetidsne peuvent pas être spécifiés ensemble.Entrées renvoyées : par défaut, Milvus rejette une requête d’agrégation de recherche lorsque le nombre maximal d’entrées de résultats calculé pour la requête dépasse 10 000. Ce seuil est contrôlé par
proxy.maxSearchAggregationResultEntries. Définissez la valeur de configuration sur0ou sur un nombre négatif pour désactiver cette vérification.Milvus calcule ce nombre maximal comme suit :
number of query vectors × product of the effective search_size at every aggregation level × largest TopHits.size at any levelPour ce calcul côté serveur, la valeur effective de `
search_size` à un niveau donné est la valeur explicitement configurée `search_size`, ou la valeur `size` de ce niveau lorsque `search_size` est omis. L’API PyMilvus utilisée dans ce guide n’expose pas actuellement `search_size` ; les requêtes PyMilvus utilisent donc la valeur `size` de chaque niveau pour ce calcul. Utilisez1pour le dernier facteur lorsqu’aucun niveau ne configureTopHits. Par exemple, un vecteur de requête, 10 compartiments racines, cinq compartiments enfants par compartiment racine et deux résultats par compartiment enfant produisent un maximum calculé de :1 × 10 × 5 × 2 = 100
Utiliser l’agrégation de recherche
Choisissez un exemple en fonction de ce que vous souhaitez réaliser :
| Accédez à | Description | Paramètres clés |
|---|---|---|
| Comparer et trier les compartiments | Calculez les statistiques par compartiment pour comparer les compartiments, puis triez les compartiments renvoyés par métriques, nombres ou clés. | fields, size, metrics, order |
| Afficher des résultats représentatifs de chaque compartiment | Renvoyer un nombre limité d’entités issues de chaque compartiment et trier ces entités indépendamment selon des champs scalaires ou un score vectoriel. | top_hits, TopHits.size, TopHits.sort |
| Regrouper les résultats à plusieurs niveaux | Organisez les résultats en niveaux de segments parents et enfants pour analyser plusieurs dimensions à la suite. | sub_aggregation |
Les exemples ci-dessous utilisent une collection de produits comportant des champs de marque, de catégorie, de couleur, de prix et de note. Tous les noms de marque, noms de produit, prix, notes et résultats de recherche sont des données d’exemple synthétiques. Développez la section suivante pour créer la collection et définir les variables de recherche partagées.
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 configuration ci-dessus définit COSINE à la fois pour l’index vectoriel et pour les paramètres de recherche. Par conséquent, les exemples suivants utilisent {"_score": "desc"} pour placer en premier les similarités cosinus les plus élevées. Pour une métrique de distance telle que L2, utilisez {"_score": "asc"}.
Comparer et trier les compartiments
Utilisez ce modèle lorsque vous devez comparer des groupes d’entités récupérées à l’aide de statistiques calculées et contrôler l’ordre dans lequel les groupes sont renvoyés. Dans cet exemple, Milvus regroupe les produits récupérés par brand, calcule des métriques de prix pour chaque groupe de marques, puis trie les groupes par prix moyen.
Si votre objectif est uniquement d’améliorer la diversité des résultats en renvoyant une ou plusieurs entités par valeur de champ, utilisez plutôt la recherche par regroupement.
La configuration suivante crée jusqu’à trois segments de marque, calcule des métriques pour chaque segment et trie les segments par prix moyen :
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"},
],
)
Transmettez l’objet au paramètre « 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,
)
Lorsque le paramètre « search_aggregation » est défini, PyMilvus ne renvoie aucun résultat d’entité ordinaire dans « result[0] ». Consultez plutôt la réponse du compartiment à l’adresse result.agg_buckets[0]. Le paramètre « output_fields » contrôle quels champs scalaires apparaissent dans chaque mappage « AggregationHit.fields » renvoyé ; Milvus peut toujours utiliser les champs « metric-source » et « sort » qui ne figurent pas dans « output_fields ».
La sortie suivante a été capturée à partir de la requête ci-dessus et sérialisée au format JSON pour plus de lisibilité. PyMilvus renvoie des objets ` AggregationBucket ` plutôt que du JSON. La valeur ` key ` est toujours une liste ordonnée de composants de clé, même lorsque ` fields ` ne contient qu’un seul champ. Cela permet de préserver l’ordre des champs pour les clés composites.
[
{
"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": []
}
]
Pour le vecteur de requête unique présenté dans ce guide, lisez les compartiments de niveau supérieur renvoyés dans ` result.agg_buckets[0]`. Chaque compartiment expose ses composants de clé ordonnés, les candidats conservés dans ` count`, les valeurs calculées dans ` metrics`, les valeurs représentatives dans ` hits` et les compartiments imbriqués dans ` sub_groups`.
Lisez la configuration comme suit :
| Paramètre | Ce qu’il contrôle | Dans cet exemple |
|---|---|---|
fields | Comment Milvus crée les clés de compartiment | Crée un compartiment pour chaque valeur distincte de « brand ». |
size | Nombre maximal de compartiments renvoyés | Renvoie jusqu’à trois buckets de marque. |
metrics | Les statistiques calculées pour chaque compartiment | Calcule le nombre de produits, le prix moyen et le prix minimum. |
order | Comment Milvus trie les segments renvoyés | Trie par prix moyen, puis utilise la clé du segment pour départager les ex aequo. |
Milvus ignore l’ limit lorsque l’option « search_aggregation » est activée. Utilisez la valeur « SearchAggregation.size » de la racine pour contrôler le nombre de segments de niveau supérieur.
Avec ces paramètres, Milvus renvoie les compartiments « Marque B », « Marque A » et « Marque C » par ordre décroissant d’ avg_price. Le critère « _key » ne s’applique que lorsque les compartiments ont le même prix moyen. Comme cette configuration ne définit pas de valeur « top_hits », la liste « hits » de chaque compartiment est vide et le budget candidat par clé est « 1 ». Les nombres et les métriques affichés décrivent donc un candidat retenu par marque. Configurez « top_hits » avec une valeur « TopHits.size » plus élevée lorsque l’agrégation nécessite une fenêtre de métrique par clé plus large.
Chaque entrée de la table « SearchAggregation.metrics » associe un alias défini par l’utilisateur à une valeur de la table « {operation: source} » :
| Source | Opérations prises en charge | Comportement |
|---|---|---|
Tout champ non «JSON » et non dynamique | count | Compte les candidats retenus dont le champ source n’est pas de type « NULL ». |
| Champ de type entier ou à virgule flottante | sum, « avg », « min », max | Effectue le calcul sur les valeurs retenues non nulles. |
Champ de type chaîne de caractères ou « TIMESTAMPTZ » | min, max | Sélectionne la valeur conservée non nulle minimale ou maximale. |
"*" | count | Compte chaque candidat conservé dans le compartiment. Le résultat correspond à bucket.count. |
_score | sum, avg, min, max | Agrège les valeurs de similarité ou de distance ANN pour les candidats conservés. |
SearchAggregation.order Accepte les clés suivantes :
| Clé d’ordre | Signification |
|---|---|
| Alias d’une métrique | Trie selon une valeur calculée dans l'metrics au même niveau d'agrégation, par exemple avg_price. |
_count | Trie en fonction du nombre de candidats retenus dans chaque compartiment. |
_key | Trie selon la clé du compartiment plutôt que selon un champ de collection nommé « _key ». |
Chaque entrée « order » associe une clé à « "asc" » ou « "desc" ». Milvus évalue les différentes entrées de la première à la dernière. Si vous omettez « order », Milvus conserve l’ordre de découverte des compartiments issu de l’ensemble des candidats retenus.
Pour trier les compartiments en fonction de la qualité de la correspondance vectorielle, calculez d’abord une métrique au niveau du compartiment à partir de _score, puis utilisez l’alias de cette métrique dans order. Vous ne pouvez pas utiliser directement _score comme clé de tri des compartiments, car chaque compartiment peut contenir plusieurs scores d’entités. Par exemple, avec COSINE ou IP:
aggregation = SearchAggregation(
fields=["brand"],
size=3,
metrics={"max_score": {"max": "_score"}},
order=[{"max_score": "desc"}],
)
Avec L2, calculez la valeur minimale de _score et triez l’alias de métrique par ordre croissant afin que les compartiments présentant la distance la plus faible apparaissent en premier.
Pour créer une clé de compartiment composite, passez plusieurs noms de champs dans la même liste :
aggregation = SearchAggregation(
# Combine brand and color to form a composite bucket key.
fields=["brand", "color"],
size=6,
)
Cette configuration peut produire des clés telles que (Brand A, black), (Brand A, blue) et (Brand B, white). Deux entités ne partagent un compartiment que lorsque les deux valeurs correspondent. Milvus conserve l’ordre de la liste ; ainsi, brand est le premier composant de la clé et color le second. Lorsque _key est utilisé dans order, Milvus compare les composants de la clé composite dans le même ordre. Passez plusieurs chaînes de caractères dans une liste plate ; les listes imbriquées ne sont pas prises en charge.
size=6 correspond au nombre maximal de compartiments composés renvoyés à ce niveau d’agrégation. Les données d’exemple contiennent cinq combinaisons distinctes de marque et de couleur ; les cinq peuvent donc être renvoyées. Dans la limite d’entrées renvoyées, cette requête contribue à 1 query vector × 6 buckets × 1 = 6 entrées de résultat configurées.
Plusieurs champs dans une liste « SearchAggregation.fields » créent une clé de compartiment composite à ce niveau d’agrégation. Pour créer une hiérarchie de compartiments parent-enfant, utilisez une agrégation imbriquée.
Les exemples suivants redéfinissent ` aggregation`. Transmettez l’objet mis à jour au même paramètre ` search_aggregation ` et relancez l’appel de recherche.
Afficher des résultats représentatifs de chaque compartiment
Incluez des entités représentatives lorsque l’application doit afficher des produits réels issus de chaque compartiment. Dans cet exemple, Milvus renvoie jusqu’à deux produits par compartiment de marque, classés par note puis par score vectoriel.
Configurez TopHits comme suit :
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"},
],
),
)
Le compartiment « Marque A » suivant a été extrait de la requête ci-dessus et sérialisé au format JSON pour plus de lisibilité.
{
"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": []
}
| Paramètre | Objectif |
|---|---|
top_hits | Facultatif. Configure les entités représentatives pour ce niveau d’agrégation. S’il est omis, « bucket.hits » est vide et le budget candidat par clé est défini par défaut sur un. |
TopHits.size | Renvoie jusqu’à deux entités représentatives de chaque segment sélectionné et définit le budget candidat par clé sur deux pour l’ensemble de l’arborescence d’agrégation. |
TopHits.sort | Trie les entités au sein de chaque segment selon les critères indiqués. |
Configurez ` top_hits ` lorsque l’application a besoin d’entités représentatives ou lorsque les comptages et les métriques nécessitent une fenêtre de candidats par clé plus large. Une valeur plus élevée de ` TopHits.size ` augmente à la fois le budget de candidats et le calcul du nombre maximal d’entrées renvoyées dans `Limits`.
SearchAggregation.order « sorts buckets » trie les compartiments, tandis que « TopHits.sort » trie les entités conservées au sein de chaque compartiment. L’ordre de tri ne modifie pas les candidats retenus pour l’ count et les métriques. « TopHits.sort » accepte les noms de champs scalaires comparables pris en charge ainsi que le champ intégré « _score », qui représente la similarité ou la distance ANN. Milvus évalue les entrées « sort » de la première à la dernière. Dans cet exemple, il classe les produits par rating du plus élevé au plus bas et n’utilise _score que lorsque deux notes sont égales. Comme la configuration utilise COSINE, un classement décroissant _score place le produit le plus similaire en premier.
Les champs utilisés par metrics ou TopHits.sort ne doivent pas nécessairement apparaître dans output_fields. Milvus récupère ces champs en interne, mais seuls les champs explicitement répertoriés dans output_fields sont inclus dans le mappage fields de chaque résultat renvoyé. Les clés primaires et les scores vectoriels restent disponibles via AggregationHit.pk et AggregationHit.score.
Chaque « AggregationHit » renvoyé expose sa clé primaire dans pk, son score vectoriel dans score et les champs de sortie demandés dans fields.
Regroupement des résultats à plusieurs niveaux
Utilisez l’agrégation imbriquée lorsque vous avez besoin d’un niveau de compartiments à l’intérieur d’un autre. Dans cet exemple, Milvus crée d’abord des compartiments de catégorie, puis des compartiments de marque au sein de chaque catégorie.
L’agrégation enfant ne reçoit que les entités attribuées à son compartiment parent. fields contrôle la clé du compartiment à chaque niveau d’agrégation, tandis que sub_aggregation crée la hiérarchie parent-enfant.
La configuration ci-dessous crée un compartiment de catégorie avec la clé (running_shoes). Au sein de ce compartiment parent, l’agrégation enfant crée des compartiments de marque distincts avec des clés telles que (Brand A), (Brand B) et (Brand C).
Parent bucket key:
(running_shoes)
Child bucket keys:
├── (Brand A)
├── (Brand B)
└── (Brand C)
Chaque niveau peut utiliser plusieurs champs de manière indépendante. Par exemple, l’utilisation de fields=["brand", "color"] dans l’agrégation enfant créerait des clés enfants composites telles que (Brand A, black).
La configuration suivante met en œuvre cette hiérarchie :
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"}],
),
),
)
L'extrait sérialisé suivant montre le compartiment parent running_shoes et son compartiment enfant « Brand B ». Les compartiments enfants « Brand A » et « Brand C » sont omis par souci de concision.
{
"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": []
}
]
}
Le résultat affiché représente le chemin d’accès au compartiment (running_shoes) → (Brand B), et non une clé de compartiment composite unique telle que (running_shoes, Brand B).
Milvus sélectionne d’abord jusqu’à deux compartiments de catégorie, classés par product_count. Il exécute ensuite sub_aggregation indépendamment au sein de chaque catégorie sélectionnée et renvoie jusqu’à trois compartiments de marque, classés par avg_rating.
Dans le résultat ci-dessus :
- Le groupe racine «
running_shoes» contient quatre candidats retenus répartis entre ses clés composites enfants. Ses «metrics» contiennent les valeurs de niveau racine «avg_price» et «product_count». - La liste «
sub_groups» du compartiment racine contient les compartiments enfants de marque. Le compartiment « Brand B » affiché contient un candidat retenu ainsi que ses propres valeurs «avg_rating» et «brand_count». - La liste «
hits» du compartiment racine est vide, car l’agrégation racine ne configure pas «top_hits». Le compartiment enfant « Marque B » contient un résultat représentatif, car «top_hits» est configuré dans «sub_aggregation».
FAQ
Quelle est la précision des comptages et des métriques des compartiments ?
L’agrégation de recherche résume les candidats ANN conservés. Elle n’effectue pas d’agrégation sur l’ensemble de la collection.
La conservation des candidats comporte deux étapes d’approximation. La recherche ANN peut omettre des entités pertinentes de la collection, et l’étape de regroupement ne conserve au maximum que les candidats les plus grands TopHits.size pour chaque clé composite complète. Si aucun niveau ne configure top_hits, cette limite par clé est égale à un.
Par exemple, supposons qu’une collection contienne 5 000 produits de la marque A et que beaucoup d’entre eux soient pertinents pour la requête vectorielle. Si l’agrégation utilise l’option « TopHits(size=4) », le compartiment de la marque A peut retenir au maximum quatre candidats pour une clé composite complète. Ses paramètres « count » et ses métriques décrivent ces candidats retenus, et non l’ensemble des produits pertinents de la marque A ni l’ensemble des 5 000 entités de la collection.
L’approximation revêt une importance particulière lorsque l’ order utilise un alias de métrique. Les variations du taux de rappel de la recherche peuvent modifier les valeurs des métriques et, par conséquent, déterminer quels compartiments correspondent à l’ SearchAggregation.size. L’agrégation imbriquée peut amplifier cet effet, car chaque niveau enfant opère sur les entités disponibles dans son compartiment parent.
Si vous avez besoin de statistiques exactes sur chaque entité correspondante, utilisez un workflow d’agrégation de requêtes exactes plutôt que l’agrégation de recherche.
En quoi l’agrégation de recherche diffère-t-elle de la recherche par regroupement ?
Faites votre choix en fonction de la forme principale des résultats de l’application :
| Besoin principal | Préférer | Réponse à exploiter |
|---|---|---|
| Renvoie une liste d’entités classées standard avec moins de valeurs répétées dans un champ de regroupement | Recherche par regroupement | Résultats de recherche plats pour chaque vecteur de requête |
| Inspecter ou comparer les groupes sous forme de compartiments, avec des clés, des comptes, des métriques, un classement, des résultats représentatifs ou des compartiments enfants | Agrégation de recherche | AggregationBucket objets dans result.agg_buckets |
Même lorsque l’agrégation de recherche configure top_hits, sa réponse principale reste une arborescence de compartiments. La recherche par regroupement reste utile lorsque l’application traite déjà des résultats de recherche classiques et recherche avant tout la diversité des résultats.
Ces API s’excluent mutuellement. PyMilvus lève une exception « ParamError » lorsque « search_aggregation » est combiné avec « group_by_field » ou « group_by_fields » dans la même requête.