Aggregate

Beschreibung

Die Phase aggregate(...) berechnet aggregierte Ergebnisse (z.B. Anzahl, Summe) aus den Dokumenten, die von der vorherigen Phase zurückgegeben wurden.

Optional werden Dokumente anhand eines bereitgestellten Gruppierungsausdrucks gruppiert und dann Akkumulatorfunktionen auf jede Gruppe angewendet.

Beispiele

Für Aggregationen ohne „group-by“ verwendet die Phase aggregate(...) einen oder mehrere Aggregatorausdrücke mit Alias:

Node.js

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

Für Aggregationen mit Gruppierung werden neben den Aggregatoren zusätzliche Gruppen verwendet:

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();

Verhalten

Aggregationen ohne Gruppierung

Erstellen Sie eine cities Sammlung mit den folgenden Dokumenten:

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});

So ermitteln Sie die Gesamtzahl der Städte und die durchschnittliche Einwohnerzahl:

Node.js

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

Ergebnis:

{avg_population: 5100000, total: 5}

Aggregationen für Gruppen ausführen

Wenn Sie ein groups-Argument angeben, können Sie Aggregationen für jede einzelne Gruppe ausführen.

Beispiel: So ermitteln Sie die Stadt mit der größten Bevölkerung in jedem Land und jedem Bundesstaat:

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();

Ergebnis:

{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}

Komplexe Ausdrücke für die Gruppierung

Neben der Gruppierung nach Feldwerten unterstützt die Phase aggregate(...) auch die Gruppierung nach Ergebnissen komplexer Ausdrücke. Jeder Ausdruck, der in einer select(...) Phase gültig ist, kann als Gruppierungsschlüssel verwendet werden. So können Sie flexibel nach berechneten Werten oder Bedingungen gruppieren.

Beispiel: So gruppieren Sie nach dem Wert des Felds „state“ (Bundesstaat) und ermitteln die Gesamtbevölkerung in jeder Gruppe:

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();

Ergebnis:

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

Aggregatorverhalten

Das Aggregationsverhalten jeder unterstützten Funktion (z.B. count, sum, avg) finden Sie auf der entsprechenden Seite zu Aggregatfunktionen.

Verhalten von Gruppenschlüsseln

Beim Gruppieren von Dokumenten verwendet Firestore die Gleichheitssemantik, um zu ermitteln, ob Werte zur selben Gruppe gehören.

Das bedeutet, dass äquivalente Werte, z. B. mathematisch äquivalente numerische Werte, unabhängig vom ursprünglichen Typ (32-Bit-Ganzzahl, 64-Bit-Ganzzahl, Gleitkommazahlen, decimal128 usw.) alle zusammen gruppiert werden.

Beispiel: In einer Sammlung numerics mit verschiedenen Dokumenten, die foo Werte vom Typ 32-Bit-Ganzzahl 1, 64-Bit-Ganzzahl 1L und Gleitkommazahl 1.0 enthalten, werden alle in derselben Gruppe zusammengefasst. Wenn Sie eine Zählung nach foo gruppieren, wird Folgendes zurückgegeben:

{foo: 1.0, count: 3}

In solchen Fällen, in denen verschiedene äquivalente Werte im Dataset vorhanden sind, kann der Ausgabewert der Gruppe jeder dieser äquivalenten Werte sein. In diesem Beispiel kann foo den Wert 1, 1L oder 1.0 haben.

Auch wenn es deterministisch erscheint, sollten Sie sich nicht darauf verlassen, dass ein bestimmter Wert ausgewählt wird.

Arbeitsspeichernutzung

Wie die Aggregation ausgeführt wird, hängt von den verfügbaren Indexen ab. Wenn der Abfrageoptimierer keinen geeigneten Index auswählt, muss die Aggregation alle Gruppen im Arbeitsspeicher puffern.

Wenn es sehr viele Gruppen gibt oder jede Gruppe sehr groß ist (z.B. Gruppierung nach sehr großen Werten), kann der Arbeitsspeicher für diese Phase nicht ausreichen.

In solchen Fällen sollten Sie Filter anwenden, um das Dataset für die Aggregation zu beschränken, nach kleineren/weniger Feldern zu gruppieren oder Indexe wie empfohlen erstellen, um eine hohe Arbeitsspeichernutzung zu vermeiden. Mit „Query Explain“ erhalten Sie Informationen zum tatsächlichen Abfrageausführungsplan und Profiling-Daten, die Ihnen bei der Fehlerbehebung helfen.