Agrégat

Description

L'étape aggregate(...) calcule les résultats agrégés (par exemple, le nombre, la somme) à partir des documents renvoyés par l'étape précédente.

Éventuellement, lorsqu'une expression de regroupement est fournie, elle regroupe les documents en fonction des expressions fournies, puis applique des fonctions d'accumulateur à chaque groupe.

Exemples

Pour les agrégations sans regroupement, l'étape aggregate(...) prend une ou plusieurs expressions d'agrégateur avec alias :

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

Pour les agrégations avec regroupement, elle prend des groupes supplémentaires en plus des agrégateurs :

Node.js

const result = await db.pipeline()
  .collectionGroup('citites')
  .aggregate({
    accumulators: [
      countAll().as('cities'),
      field('population').sum().as('total_popoluation')
    ],
    groups: [field('location.state').as('state')]
  })
  .execute();

Comportement

Agrégations sans regroupement

Créez une collection cities avec les documents suivants :

Node.js

await db.collection('cities').doc('SF').set({name: 'San Francisco', state: 'CA', country: 'USA', population: 870000});
await db.collection('cities').doc('LA').set({name: 'Los Angeles', state: 'CA', country: 'USA', population: 3970000});
await db.collection('cities').doc('NY').set({name: 'New York', state: 'NY', country: 'USA', population: 8530000});
await db.collection('cities').doc('TOR').set({name: 'Toronto', state: null, country: 'Canada', population: 2930000});
await db.collection('cities').doc('MEX').set({name: 'Mexico City', state: null, country: 'Mexico', population: 9200000});

Pour connaître le nombre total de villes et leur population moyenne :

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

qui produit :

{avg_population: 5100000, total: 5}

Effectuer des agrégations sur des groupes

En fournissant un argument groups, vous pouvez effectuer des agrégations sur chaque groupe distinct.

Par exemple, pour trouver la ville la plus peuplée de chaque pays et de chaque État :

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
          countAll().as("number_of_cities"),
          maximum("population").as("max_population")
      ],
      groups: ["country", "state"]
  })
  .execute();

qui donne :

{country: "USA", state: "CA", max_population: 3970000, number_of_cities: 2},
{country: "USA", state: "NY", max_population: 8530000, number_of_cities: 1},
{country: "Canada", state: null, max_population: 2930000, number_of_cities: 1},
{country: "Mexico", state: null,  max_population: 9200000, number_of_cities: 1}

Expressions complexes sur le regroupement

Au-delà du regroupement par valeurs de champ uniquement, l'étape aggregate(...) permet de regrouper les résultats d'expressions complexes. Toute expression valide dans une select(...) étape peut être utilisée comme clé de regroupement. Cela permet un regroupement flexible basé sur des valeurs ou des conditions calculées.

Par exemple, pour regrouper les données selon que le champ d'état est nul ou non, et pour connaître la population totale de chaque groupe :

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
         sum("population").as("total_population")
      ],
      groups: [equal(field("state"), null).as("state_is_null")]
  })
  .execute();

renvoie le résultat :

{state_is_null: true, total_population: 12130000}
{state_is_null: false, total_population: 13370000}

Comportements de l'agrégateur

Le comportement d'agrégation de chaque fonction compatible (par exemple, count, sum, avg) est disponible sur la page dédiée aux fonctions d'agrégation.

Comportements de la clé de groupe

Lors du regroupement de documents, Firestore utilise une sémantique d'égalité pour déterminer si les valeurs appartiennent au même groupe.

Cela signifie que les valeurs équivalentes, par exemple les valeurs numériques mathématiquement équivalentes, quel que soit le type d'origine (entier 32 bits, entier 64 bits, nombres à virgule flottante, decimal128, etc.), sont toutes regroupées.

Par exemple, dans une collection numerics avec différents documents contenant des foo valeurs de type entier 32 bits 1, entier 64 bits 1L et nombre à virgule flottante 1.0 respectivement, elles seront toutes accumulées dans le même groupe. L'exécution d'un regroupement de comptage par foo renverra le résultat suivant :

{foo: 1.0, count: 3}

Dans de tels cas où différentes valeurs équivalentes sont présentes dans l'ensemble de données, la valeur de sortie du groupe peut être n'importe laquelle de ces valeurs équivalentes. Dans cet exemple, foo peut être 1, 1L, ou 1.0.

Même si cela semble déterministe, vous ne devez pas essayer de vous fier au comportement d'une valeur spécifique sélectionnée.

Utilisation de la mémoire

La manière dont l'agrégation sera exécutée dépend des index disponibles. Lorsqu'aucun index approprié n'est choisi par l'optimiseur de requête, l'agrégat doit mettre en mémoire tampon tous les groupes.

Si le nombre de groupes est très élevé ou si chaque groupe est très volumineux (par exemple, en cas de regroupement par valeurs volumineuses), cette étape peut manquer de mémoire.

Dans ce cas, vous devez appliquer des filtres pour limiter l'ensemble de données à agréger, regrouper les données sur des champs plus petits/moins nombreux ou créer des index comme recommandé pour éviter une utilisation importante de la mémoire. La fonctionnalité "Expliquer la requête" fournit des informations sur le plan d'exécution réel de la requête et des données de profilage pour faciliter le débogage.