Agrégat

Description

L'étape aggregate calcule les résultats agrégés (par exemple, le nombre ou 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.

Syntaxe

Pour les agrégations sans groupement, 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, il faut 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();

ce 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

En plus du regroupement par valeurs de champ, l'étape aggregate permet de regrouper les résultats d'expressions complexes. Toute expression valide dans une étape select 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 "État" est nul ou non, et 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 des agrégateurs

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

Comportements clés des groupes

Lorsque vous regroupez des documents, Firestore utilise la 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, sont toutes regroupées, quel que soit leur type d'origine (entier 32 bits, entier 64 bits, nombres à virgule flottante, decimal128, etc.).

Par exemple, dans une collection numerics avec différents documents contenant des valeurs foo de type entier 32 bits 1, entier 64 bits 1L et nombre à virgule flottante 1.0, elles seront toutes cumulées dans le même groupe. L'exécution d'un regroupement de décomptes par foo renvoie les éléments suivants :

{foo: 1.0, count: 3}

Dans ce cas, 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 façon dont l'agrégation sera exécutée dépend des index disponibles. Lorsque l'optimiseur de requête ne choisit pas d'index approprié, l'agrégat doit mettre en mémoire tampon tous les groupes.

Si vous avez un très grand nombre de groupes ou si chaque groupe est très volumineux (par exemple, si vous regroupez des valeurs énormes), 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 ou moins nombreux, ou créer des index comme recommandé pour éviter une utilisation importante de la mémoire. Query Explain 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.