Aggregate

Beschreibung

In der Phase aggregate werden aggregierte Ergebnisse (z.B. Anzahl, Summe) aus den von der vorherigen Phase zurückgegebenen Dokumenten berechnet.

Wenn ein Gruppierungsausdruck angegeben wird, werden Dokumente optional anhand der angegebenen Ausdrücke gruppiert und dann Akkumulatorfunktionen auf jede Gruppe angewendet.

Syntax

Für Aggregationen ohne GROUP BY werden in der aggregate-Phase ein oder mehrere Alias-Aggregatorausdrücke verwendet:

Node.js

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

Für Aggregationen mit Gruppierung sind neben den Aggregatoren zusätzliche Gruppen erforderlich:

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

Dadurch wird Folgendes ausgegeben:

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

Wenn Sie beispielsweise die Stadt mit der größten Bevölkerung in jedem Land und jedem Bundesstaat ermitteln möchten, gehen Sie so vor:

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

Das ergibt:

{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

In der Phase aggregate kann nicht nur nach Feldwerten, sondern auch nach Ergebnissen komplexer Ausdrücke gruppiert werden. 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.

Wenn Sie beispielsweise nach dem Feld „state“ gruppieren möchten, um herauszufinden, ob es null ist, und die Gesamtbevölkerung in jeder Gruppe ermitteln möchten, gehen Sie so vor:

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

wird Folgendes zurückgegeben:

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

Verhalten von Aggregatoren

Das Aggregationsverhalten der einzelnen unterstützten Funktionen (z.B. count, sum, avg) finden Sie auf der Seite Aggregatfunktionen.

Wichtige Verhaltensweisen gruppieren

Beim Gruppieren von Dokumenten verwendet Firestore die Gleichheitssemantik, um zu bestimmen, 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, Dezimal128 usw.), alle zusammen gruppiert werden.

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 ausführen, wird Folgendes zurückgegeben:

{foo: 1.0, count: 3}

Wenn im Dataset unterschiedliche äquivalente Werte vorhanden sind, kann der Ausgabewert der Gruppe jeder dieser äquivalenten Werte sein. In diesem Beispiel könnte foo 1, 1L oder 1.0 sein.

Auch wenn es deterministisch erscheint, sollten Sie nicht versuchen, sich auf das Verhalten zu 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 Query-Optimierer keinen geeigneten Index auswählt, müssen alle Gruppen im Arbeitsspeicher gepuffert werden.

Wenn Sie eine sehr große Anzahl von Gruppen haben oder jede Gruppe sehr groß ist (z.B. bei der Gruppierung nach großen Werten), kann es sein, dass in dieser Phase der Arbeitsspeicher nicht ausreicht.

In solchen Fällen sollten Sie Filter anwenden, um das Dataset einzugrenzen, für das die Aggregation erfolgen soll, die Gruppierung auf weniger Felder beschränken oder wie empfohlen Indexe erstellen, um eine hohe Speicherauslastung zu vermeiden. Query Explain liefert Informationen zum tatsächlichen Abfrageausführungsplan und Profiling-Daten, die beim Debuggen helfen.