De la recherche aux résultats structurés : agrégation et ORDER BY dans Milvus 3.0
Prenons un flux de recherche de produits familier. Un acheteur téléverse la photo d’une robe, et la recherche vectorielle récupère un ensemble de candidats pertinents dans un catalogue contenant des dizaines de millions de produits.
La page a toutefois besoin de plus qu’une liste classée. Elle a besoin de facettes de marques. Elle a besoin d’un tri par prix. L’équipe merchandising veut savoir quelles marques dominent cet ensemble de résultats, la plage de prix au sein de chaque marque, et quelques produits représentatifs de chaque groupe.
Avant Milvus 3.0, les applications géraient couramment cette seconde étape elles-mêmes : récupérer les lignes depuis Milvus, les regrouper et les trier dans pandas ou dans une couche de service, puis assembler la réponse. Certaines équipes maintenaient un pipeline d’analytique séparé uniquement pour calculer les nombres et les distributions sur des données déjà présentes dans la base de données vectorielle.
La base de données vectorielle trouvait les candidats ; l’application devait les transformer en un résultat structuré.
Milvus 3.0 déplace une plus grande partie de ce travail dans le moteur de récupération. Il ajoute trois capacités liées mais distinctes :
- L’agrégation de requêtes calcule
count,sum,avg,minetmaxsur les lignes filtrées et visibles, avec des champsGROUP BYfacultatifs. - Search Aggregation organise les candidats de plus proches voisins approximatifs (ANN) conservés en compartiments, calcule des métriques par compartiment, construit des compartiments imbriqués et renvoie des résultats représentatifs.
- Le tri côté serveur
**ORDER BY**trie les résultats de requête ou les candidats ANN selon un ou plusieurs champs scalaires avant que l’application les reçoive.
La distinction entre requête et recherche est importante :
| Capacité | Données résumées ou ordonnées | Forme principale du résultat | Limite d’exactitude |
|---|---|---|---|
| Agrégation de requêtes | Toutes les lignes visibles qui correspondent au filtre | Une ligne par groupe, avec des valeurs agrégées | Exacte sur l’ensemble de lignes visibles de la requête |
| Search Aggregation | Candidats conservés par la recherche ANN et l’étape de regroupement | Compartiments, métriques, résultats représentatifs et compartiments enfants facultatifs | Approximative par conception |
Requête ORDER BY | Lignes visibles qui correspondent au filtre | Lignes triées | Exacte sur le résultat de requête filtré |
Recherche ORDER BY | Candidats ANN | Résultats de recherche ou groupes triés | N’élargit pas la limite de rappel de l’ANN |
Cet article explique pourquoi ces opérations ont leur place dans la base de données, comment fonctionne l’agrégation distribuée, en quoi Search Aggregation diffère de Grouping Search, et où s’arrêtent les nouvelles sémantiques.
Pourquoi le post-traitement côté application atteint ses limites
Déplacer l’agrégation et le tri vers l’application peut ressembler à un petit choix d’implémentation. À grande échelle, cela crée trois problèmes plus importants.
L’application déplace beaucoup plus de données que la réponse n’en contient
Supposons qu’un tableau de bord opérationnel ait besoin du nombre de produits et du prix moyen pour chaque catégorie parmi deux millions de lignes en stock. Même avec une charge utile approximative de seulement 100 octets par ligne pour la catégorie, le prix, la clé primaire et les frais de sérialisation, l’application doit recevoir environ 200 Mo de données avant de pouvoir calculer le résultat.
Si le catalogue compte 200 catégories, la réponse ne représente que quelques centaines de clés et de nombres — de l’ordre de quelques kilooctets. L’application déplace plusieurs ordres de grandeur de données en plus que ce qu’elle renvoie, paie le même coût à chaque actualisation, et a besoin d’une mémoire cliente suffisante pour conserver ou diffuser les lignes intermédiaires.
Une agrégation dans le moteur change l’unité de déplacement des données. Les lignes brutes restent là où elles sont. Ce qui traverse les nœuds et finit par quitter Milvus est l’ensemble beaucoup plus réduit des états de groupe partiels et finaux.
Le tri local à une page n’est pas un tri global
Trier après la pagination est un bogue de correction, pas seulement une implémentation inefficace.
Si une application récupère les lignes 11 à 20 et ne trie que ces lignes par prix, elle a produit l’ordre par prix à l’intérieur de cette page — et non les lignes 11 à 20 du résultat global trié par prix. Une page ultérieure peut contenir des produits moins chers que tous les produits de la première page.
La même limite compte dans la recherche vectorielle. Récupérer un petit ensemble Top-K et le trier dans l’application ne peut réordonner que ces candidats. Cela ne peut pas récupérer des candidats pertinents que l’étape ANN n’a pas renvoyés, et cela conduit souvent les applications à sur-récupérer simplement pour rendre le tri côté client utile.
Le tri côté serveur donne à Milvus le contrôle de la séquence d’ordonnancement et de pagination. Pour les charges de travail de requête, le moteur trie l’ensemble de lignes filtré avant d’appliquer la fenêtre de page. Pour les charges de travail de recherche, il trie à l’intérieur de la limite des candidats ANN et garde cette limitation explicite.
Le client ne peut pas reproduire la visibilité de la base de données
L’agrégation dépend également des lignes visibles à l’horodatage de la requête. Les suppressions, les entités expirées et les écritures concurrentes sont régies par le contrôle de concurrence multiversion (MVCC) et les sémantiques de cohérence de Milvus.
Une fois que les lignes brutes quittent la base de données, l’application suppose généralement que le lot reçu représente l’instantané correct. Reconstruire les mêmes règles de visibilité dans un client est impraticable, surtout pendant que la collection reçoit des écritures et des suppressions.
La solution de contournement courante — un second moteur analytique alimenté par exportation et ETL — ajoute une autre copie des données, une autre limite de cohérence et un autre pipeline à exploiter. Les nombres, les métriques et le tri devraient s’exécuter là où les données et leurs règles de visibilité existent déjà.
Voyons maintenant ce que propose Milvus 3.0.
Agrégation de requêtes : statistiques exactes sur les lignes visibles
L’agrégation de requêtes répond à des questions telles que :
- Combien de produits en stock y a-t-il dans chaque catégorie ?
- Quel est le prix moyen par marque ?
- Quels sont les horodatages d’événements minimum et maximum pour chaque hôte ?
- Combien d’enregistrements restent après l’application d’un filtre et de la visibilité TTL ?
L’API paraît familière à quiconque a utilisé SQL : passez un ou plusieurs champs dans group_by_fields, puis placez les expressions d’agrégation dans output_fields.
res = client.query(
collection_name="products",
filter='status == "on_sale"',
group_by_fields=["category"],
output_fields=["category", "count(*)", "avg(price)"],
)
# [
# {"category": "books", "count(*)": 18734, "avg(price)": 45.3},
# …
# ]
La syntaxe est la partie simple. Le modèle d’exécution est ce qui rend le résultat utile dans une base de données vectorielle distribuée.
Les états locaux aux segments remplacent le déplacement de lignes brutes
Une collection Milvus peut s’étendre sur des centaines ou des milliers de segments distribués sur plusieurs nœuds de requête, avec des données récemment écrites encore sur le chemin de streaming. Aucun nœud d’exécution ne commence avec toutes les lignes visibles.
Milvus pousse donc l’agrégation vers les segments :
- Chaque segment applique localement les règles de filtre et de visibilité MVCC.
- Le segment émet un état partiel par groupe au lieu de ses lignes correspondantes.
- Les états partiels sont fusionnés au sein d’un nœud de requête.
- Le proxy effectue la fusion finale entre nœuds et renvoie les groupes complétés.
La quantité de données intermédiaires évolue désormais avec le nombre de groupes et d’états d’agrégation, plutôt que directement avec le nombre de lignes correspondantes.
L’opération de fusion dépend de l’agrégat :
| Agrégat | État partiel | Règle de fusion |
|---|---|---|
count | Nombre partiel | Additionner les nombres |
sum | Somme partielle | Additionner les sommes |
min | Minimum partiel | Prendre le minimum |
max | Maximum partiel | Prendre le maximum |
avg | Somme et nombre partiels | Additionner les deux états, puis diviser une seule fois à l’étape finale |
avg est le cas instructif. Faire la moyenne de deux moyennes partielles est incorrect lorsque les partitions contiennent des nombres de lignes différents. Milvus transporte sum et count indépendamment et ne calcule la moyenne finale qu’après leur fusion globale.
C’est l’une des raisons pour lesquelles l’agrégation a sa place dans la base de données : l’opération ne consiste pas simplement à « exécuter la même fonction sur plusieurs lots ». Le moteur doit préserver l’algèbre de chaque agrégat à travers les limites des segments et des nœuds.
La visibilité est appliquée avant l’agrégation
Les lignes supprimées et expirées sont retirées des états partiels au niveau du segment selon la limite de visibilité de la requête. Elles ne remontent pas pour être ensuite corrigées dans l’application.
Le résultat décrit donc les lignes que Milvus considère comme visibles pour cette requête, et non une collection arbitraire de lots extraits à des moments légèrement différents.
limit compte désormais les groupes
Dans une requête normale, limit contrôle le nombre de lignes d’entités renvoyées. Dans une requête groupée, il contrôle le nombre de groupes renvoyés. Comme la cardinalité du résultat est déterminée par les groupes plutôt que par les lignes correspondantes, une agrégation de requêtes peut également omettre limit lorsqu’elle a besoin de tous les groupes.
Cela ressemble à un petit détail d’API, mais cela reflète un modèle de résultat différent : la sortie n’est plus une page d’entités. C’est une relation dont les lignes représentent des groupes.
Search Aggregation : une vue en compartiments des candidats ANN
L’agrégation de requêtes répond à la question : « À quoi ressemblent les lignes visibles correspondant à ce filtre ? » Search Aggregation pose une question différente : « À quoi ressemble l’ensemble de candidats récupéré pour ce vecteur ? »
Cette opération n’a pas d’équivalent SQL exact. La recherche ANN établit d’abord une limite de candidats guidée par la similarité. Milvus organise ensuite les candidats conservés selon des clés scalaires et renvoie une arborescence de compartiments au lieu d’une liste plate ordinaire de résultats.
Un compartiment peut contenir :
- une clé telle que
brandou une clé composite telle que(brand, color); - un nombre de candidats conservés ;
- des métriques incluant
count,sum,avg,minetmax; - des entités représentatives sélectionnées avec
top_hits; et - une
sub_aggregationimbriquée qui crée des compartiments enfants.
Pour la page de recherche de produits, une seule requête peut renvoyer des compartiments de marques, le prix moyen dans chaque compartiment, et trois produits représentatifs par marque :
from pymilvus import SearchAggregation, TopHits
aggregation = SearchAggregation(
fields=[“brand”],
size=10, # Return up to 10 brand buckets
metrics={“avg_price”: {“avg”: “price”}},
order=[{“_count”: “desc”}], # Order by retained-candidate count
top_hits=TopHits(
size=3,
sort=[{“_score”: “desc”}], # Use “asc” for L2 distance
),
)
res = client.search(
collection_name=“products”,
data=[query_vector],
anns_field=“embedding”,
search_aggregation=aggregation,
output_fields=[“title”, “brand”, “price”],
)
buckets = res.agg_buckets[0]
Lorsque search_aggregation est défini, la liste ordinaire des résultats est vide. L’application lit la réponse de compartiments depuis result.agg_buckets.
La spécification d’agrégation définit deux limites différentes
Search Aggregation n’exécute pas un GROUP BY sur chaque entité de la collection, et ne se contente pas de prendre une réponse Top-K ordinaire pour agréger cette liste plate.
Son exécution comporte trois étapes :
- Milvus exécute une recherche ANN pour récupérer des candidats proches du vecteur de requête.
- L’étape de regroupement conserve un nombre borné de candidats pour chaque clé complète de compartiment.
- Milvus construit les compartiments, calcule les métriques sur les candidats conservés, ordonne les compartiments et attache des résultats représentatifs ou des compartiments enfants.
Deux paramètres contrôlent différentes parties du résultat :
SearchAggregation.sizelimite le nombre de compartiments renvoyés à ce niveau d’agrégation.- Le plus grand
TopHits.sizen’importe où dans l’arborescence d’agrégation définit le budget de candidats conservés pour chaque clé composite complète. Si la requête ne contient pas detop_hits, le budget par clé vaut un par défaut.
Le limit de recherche de premier niveau ne contrôle pas ce mode et est ignoré lorsque search_aggregation est présent.
Cette distinction est essentielle lors de la lecture du count ou des métriques d’un compartiment. Avec TopHits(size=3), un compartiment de marque peut résumer au plus trois candidats conservés pour sa clé complète, même si la collection contient des milliers de produits pertinents de cette marque. Augmenter TopHits.size élargit la fenêtre de métriques par clé, mais cela ne transforme pas la recherche ANN en analyse exacte.
Si l’application a besoin de statistiques exactes sur chaque ligne visible correspondant à un filtre, elle doit utiliser l’agrégation de requêtes. Search Aggregation sert à décrire et comparer les candidats produits par la récupération par similarité.
Search Aggregation et Grouping Search résolvent des problèmes différents
Milvus prend en charge Grouping Search (group_by) depuis Milvus 2.4. Il est facile de voir le mot « grouping » dans les deux fonctionnalités et de supposer qu’il s’agit de deux interfaces pour la même opération. Leurs contrats de sortie sont différents.
Grouping Search modifie les entités qui apparaissent dans une liste de résultats classée. Un schéma RAG courant stocke les fragments comme des entités individuelles, les regroupe par doc_id, et renvoie un ou quelques fragments de chaque document. La sortie principale reste des résultats de recherche ordinaires, mais avec moins de valeurs répétées du champ de regroupement.
Search Aggregation renvoie une vue statistique. La sortie principale est une arborescence de compartiments contenant des clés, des nombres, des métriques, des résultats représentatifs et des compartiments enfants facultatifs.
| Besoin de l’application | À privilégier | À consommer |
|---|---|---|
| Une liste d’entités classée avec une plus grande diversité sur un champ | Grouping Search | Résultats de recherche ordinaires |
| Nombres de facettes, métriques par groupe, résultats représentatifs ou distributions imbriquées | Search Aggregation | Objets AggregationBucket dans result.agg_buckets |
Une règle pratique consiste à partir de la forme de réponse de l’interface utilisateur ou de l’API. Si l’application affiche une liste, Grouping Search est généralement la bonne primitive. Si elle affiche des facettes, des cartes de distribution ou une hiérarchie de groupes, utilisez Search Aggregation.
Les deux modes sont mutuellement exclusifs dans une même requête, car ils définissent des formes de résultat principales différentes.
ORDER BY : déplacer le tri avant la limite de l’application
Le tri est la fonctionnalité la moins exotique de cette version et l’une des plus faciles à implémenter incorrectement en dehors du moteur.
Milvus 3.0 expose le tri à la fois sur les requêtes et sur la recherche, mais les deux chemins utilisent des paramètres SDK différents et opèrent sur des ensembles d’entrée différents.
Le tri des requêtes ordonne l’ensemble de lignes filtré
La requête PyMilvus utilise order_by, exprimé comme une liste de chaînes "field:direction". Le moteur applique le filtre, ordonne les lignes visibles, puis applique limit et offset.
res = client.query(
collection_name="products",
filter='category == "books"',
output_fields=["title", "price"],
order_by=["price:desc", "title:asc"],
limit=10,
offset=10, # Rows 11-20 in the filtered, price-sorted result
)
Cela rend les requêtes utiles pour la navigation ordonnée métier : enregistrements ingérés les plus récents, produits les plus chers dans un filtre, inventaire le plus bas, ou valeurs extrêmes pour l’inspection des données. Sans ordonnancement côté serveur, les applications devaient d’abord récupérer les lignes et ne pouvaient pas définir un ordre métier fiable entre les pages.
Pour les champs de requête pouvant être nuls, l’ordre croissant place les valeurs nulles en dernier et l’ordre décroissant les place en premier. Un champ de tri n’a pas besoin d’apparaître dans output_fields ; incluez-le uniquement lorsque l’application a besoin de la valeur dans la réponse.
Le tri de recherche réordonne l’ensemble de candidats ANN
La recherche PyMilvus utilise order_by_fields, où chaque entrée nomme un champ scalaire et une direction :
res = client.search(
collection_name="products",
data=[query_vector],
anns_field="embedding",
limit=50,
output_fields=["title", "price"],
order_by_fields=[
{"field": "price", "order": "asc"},
],
)
L’ANN détermine toujours quelles entités deviennent des candidats. order_by_fields change la manière dont ces candidats sont renvoyés ; cela ne fait pas parcourir globalement la collection par la recherche pour trouver les produits les moins chers.
Cette limite donne aux deux API des rôles distincts :
- Utilisez une requête avec
order_bylorsque l’ordre scalaire définit lui-même le résultat, par exemple les dix produits en stock les moins chers. - Utilisez une recherche avec
order_by_fieldslorsque la pertinence sémantique ou vectorielle définit l’ensemble de candidats et qu’un champ scalaire détermine comment ces candidats doivent être présentés.
Le tri multi-champs applique les clés dans l’ordre de la liste. Lorsque des candidats de recherche ont les mêmes valeurs pour chaque clé scalaire spécifiée, Milvus conserve leur ordre original selon le score de similarité.
Le tri se compose également avec Grouping Search. Milvus ordonne les groupes selon la valeur scalaire configurée de la meilleure entité de chaque groupe tout en conservant la forme de résultat groupée. C’est utile lorsque l’application veut à la fois de la diversité sur un champ et un ordre de groupe pertinent pour le métier.
Ce que ces capacités rendent possible
Les API sont des primitives générales de base de données, mais plusieurs charges de travail de récupération en bénéficient immédiatement.
RAG et agents : inspecter la concentration de la récupération
Un système RAG ou agentique peut regrouper les fragments récupérés par document source, ligne de produits, locataire ou type de contenu. Un résultat concentré dans deux documents porte un signal de couverture différent d’un résultat réparti sur des dizaines de sources.
Cette distribution n’est pas une garantie de qualité de réponse. C’est toutefois un diagnostic de récupération utile qu’une application ou un agent peut combiner avec les scores, les citations et d’autres vérifications pour décider s’il faut élargir la requête, relancer la récupération ou demander une clarification.
Grouping Search reste le bon choix lorsque l’objectif est simplement de diversifier les fragments renvoyés. Search Aggregation est utile lorsque le système a besoin de la distribution elle-même.
E-commerce et recommandation de contenu : renvoyer les facettes avec la recherche
La page de recherche de produits initiale peut recevoir depuis Milvus des compartiments de marques, des métriques de prix, des articles représentatifs et une liste de candidats triée par scalaire. L’application contrôle toujours la présentation et la logique métier, mais elle n’a plus besoin de reconstruire les sémantiques de compartiments de base à partir de résultats exportés.
Journaux et sécurité : combiner similarité et distribution des incidents
La recherche par similarité peut trouver des événements liés à une ligne de journal suspecte. Search Aggregation peut ensuite montrer quels hôtes dominent ces candidats, l’horodatage minimum et maximum dans chaque compartiment d’hôte, ou comment les candidats se répartissent par gravité et par service.
Le résultat reste une vue des candidats récupérés plutôt qu’un nombre global exact d’incidents. Lorsque l’investigation nécessite des nombres exacts sur chaque événement correspondant à un filtre, l’agrégation de requêtes fournit cette seconde voie.
Opérations et exploration des données : calculer au lieu d’exporter
Les tableaux de bord et outils d’administration peuvent exécuter des nombres et moyennes exacts sur des lignes filtrées, puis parcourir les entités sous-jacentes dans un ordre scalaire défini. Cela supprime de nombreux utilitaires ponctuels « exporter, calculer et trier » sans prétendre que Milvus est devenu une base de données analytique complète.
Limites : ce que l’agrégation et ORDER BY ne remplacent pas
Ces fonctionnalités étendent le moteur de récupération ; elles ne transforment pas Milvus en système de traitement analytique en ligne (OLAP).
- L’agrégation de requêtes prend en charge le regroupement plus
count,sum,avg,minetmax. Elle n’ajoute pas de jointures, de fonctions de fenêtre ou de sous-requêtes complexes. Les grands travaux analytiques hors ligne restent du ressort de systèmes comme Spark, qui peuvent fonctionner avec les instantanés Milvus 3.0 et les chemins de stockage partagés. - Les clés de groupe de requête prennent en charge les champs entiers,
VARCHARetTIMESTAMPTZ. Les clés de compartiment de Search Aggregation prennent en outre en charge les champs booléens. Les valeurs à virgule flottante, vectorielles, JSON et de tableau ne sont pas des clés de compartiment. - Pour Search Aggregation,
countaccepte"*"ou une source non JSON et non dynamique ;sumetavgexigent des sources numériques ; etminetmaxprennent également en charge les sources chaîne etTIMESTAMPTZ. L’agrégation de requêtes suit les mêmes limites de types arithmétiques. Consultez le guide de l’API avant d’appliquer un agrégat à un type de champ complexe. - L’agrégation de requêtes peut ordonner la sortie groupée par clés de groupe, tandis que l’ordonnancement par un agrégat calculé tel que
count(*)reste une limite actuelle. Sans ordre explicite, l’ordre des groupes n’est pas garanti. - Search Aggregation ne peut pas actuellement être combiné avec Hybrid Search, Grouping Search, Search Iterators, un décalage non nul ou le surlignage dans la même requête.
- Les nombres et métriques de Search Aggregation décrivent les candidats ANN conservés, pas la collection complète ni chaque entité qui pourrait être sémantiquement pertinente.
- La recherche
ORDER BYmodifie la présentation des candidats. Elle ne répare pas les candidats ANN manqués et ne convertit pas la récupération par similarité en une requête Top-N scalaire exacte.
La manière la plus claire de choisir parmi les nouvelles primitives consiste à commencer par la question :
- Pour des statistiques exactes sur des lignes visibles filtrées, utilisez l’agrégation de requêtes.
- Pour une distribution sur des candidats de récupération par similarité, utilisez Search Aggregation.
- Pour une liste classée diversifiée, utilisez Grouping Search.
- Pour un ordre scalaire défini, utilisez une requête ou une recherche
ORDER BYselon le chemin qui a établi l’ensemble de résultats.
Des listes de candidats aux résultats structurés
Les bases de données vectorielles ont traditionnellement optimisé une question : quelles sont les K entités les plus proches de ce vecteur ?
Les systèmes de récupération en production posent immédiatement des questions de suivi. Quels groupes dominent le résultat ? Quels sont leurs nombres et leurs plages ? Quels exemples représentent chaque groupe ? Dans quel ordre métier l’application doit-elle présenter les lignes ou les candidats ?
Milvus 3.0 apporte ces opérations dans le même moteur qui possède les données, la limite de candidats ANN et les sémantiques de visibilité. L’agrégation de requêtes effectue une réduction distribuée exacte sur les lignes visibles. Search Aggregation construit une vue en compartiments sur les candidats ANN conservés. ORDER BY donne aux chemins de requête et de recherche un ordre scalaire côté serveur sans demander à l’application de le reconstruire page par page.
Le résultat n’est pas un moteur OLAP dissimulé dans une base de données vectorielle. C’est un moteur de récupération qui peut renvoyer une plus grande partie de la structure dont les applications ont réellement besoin.
Essayez l’agrégation et ORDER BY dans Milvus 3.0
Milvus 3.0 est disponible dès maintenant. Utilisez le guide Query pour l’agrégation exacte et le tri de requêtes, le guide Search Aggregation pour les sémantiques et les limites des compartiments, le guide Basic Vector Search pour le tri de recherche, et le guide Grouping Search lorsque votre objectif principal est la diversité des résultats.
Pour la version plus large, consultez le blog de lancement de Milvus 3.0, les notes de version de Milvus 3.0 et le dépôt milvus-io/milvus.
Si vous voulez évaluer les mêmes API sans exploiter le cluster vous-même, essayez-les sur Zilliz Cloud. La référence de requête Zilliz Cloud actuelle et la référence de recherche décrivent la disponibilité et les paramètres pour les types de clusters managés.
Pour discuter d’une charge de travail ou d’un cas limite avec l’équipe, rejoignez la communauté Milvus sur Discord ou réservez une session Milvus Office Hours.
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



