34 Praxisbeispiele zur Aggregation

Aggregation ermöglicht es, komplexe Analysen und Transformationen direkt innerhalb der Datenbank durchzuführen. Im Folgenden werden verschiedene Beispiele und Anwendungsfälle aufgezeigt, um die Möglichkeiten der Aggregation praxisnah zu veranschaulichen. Dabei kommen unterschiedliche Stages, Operatoren sowie das Einbinden externer Datenquellen mittels $lookup zum Einsatz.

34.1 Daten filtern und sortieren

Ein grundlegendes Szenario ist die Vorauswahl relevanter Dokumente aus einer Collection. Mit $match werden Dokumente gefiltert, während $sort für die Sortierung sorgt.

db.products.aggregate([
  { $match: { category: "electronics", price: { $lt: 500 } } },
  { $sort: { price: 1 } }
])

Diese Pipeline gibt alle Produkte der Kategorie “electronics” aus, deren Preis unter 500 liegt, sortiert nach aufsteigendem Preis.

34.2 Gruppieren und Summenbildung

Mit $group lassen sich Dokumente nach bestimmten Feldern gruppieren und aggregierte Kennzahlen berechnen. Hier wird die durchschnittliche Bewertung (rating) pro Kategorie bestimmt:

db.reviews.aggregate([
  { $group: { _id: "$category", avgRating: { $avg: "$rating" } } },
  { $sort: { avgRating: -1 } }
])

Das Ergebnis enthält pro Kategorie den durchschnittlichen Bewertungswert, absteigend sortiert.

34.3 Erzeugen neuer Felder

$project ermöglicht es, Felder auszuwählen, umzubenennen oder neue Felder auf Basis bestehender Informationen zu erzeugen. Hier werden aus einer Bestellung die wesentlichen Daten extrahiert, während interne Systemfelder ausgeblendet werden:

db.orders.aggregate([
  {
    $project: {
      _id: 0,
      orderId: "$_id",
      itemCount: { $size: "$items" },
      totalValue: { $sum: "$items.price" }
    }
  }
])

Es entsteht ein Ergebnis mit orderId, der Anzahl der enthaltenen Artikel (itemCount) und einem berechneten Gesamtwert (totalValue).

34.4 Mehrstufige Verarbeitung

Pipelines lassen sich aus beliebig vielen Stages zusammensetzen. So kann etwa zuerst gefiltert, dann gruppiert und anschließend ein Durchschnitt berechnet werden. Hier werden zunächst alle Transaktionen aus dem aktuellen Monat selektiert, dann die Summe pro Kunde gebildet und abschließend nach den höchsten Umsätzen sortiert:

db.transactions.aggregate([
  { $match: { month: 8 } },
  { $group: { _id: "$customerId", totalSpend: { $sum: "$amount" } } },
  { $sort: { totalSpend: -1 } }
])

34.5 Daten anreichern mit $lookup

Das $lookup-Stage ermöglicht ein Join-ähnliches Verhalten. Daten aus einer anderen Collection können eingebunden werden, um weitere Informationen zu ergänzen. Beispiel: Angenommen, wir haben eine orders-Collection mit Kunden-IDs, aber benötigen Kundeninformationen aus der customers-Collection:

db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customerId",
      foreignField: "_id",
      as: "customerInfo"
    }
  }
])

Das Ergebnis enthält für jede Bestellung ein Feld customerInfo, das die passenden Kundendaten aus der customers-Collection eingebunden hat. So lässt sich beispielsweise im nächsten Schritt problemlos auf den Namen oder die Adresse des Kunden zugreifen, ohne selbst einen weiteren Query ausführen zu müssen.

34.6 Mehrfache Nutzung von $lookup

Es ist auch möglich, $lookup mehrfach einzusetzen, um Informationen aus mehreren Collections einzubinden. Hier werden zu Bestellungen sowohl Kundendaten als auch Artikeldaten ergänzt:

db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customerId",
      foreignField: "_id",
      as: "customerInfo"
    }
  },
  {
    $lookup: {
      from: "products",
      localField: "items.productId",
      foreignField: "_id",
      as: "productDetails"
    }
  }
])

Damit stehen nun sowohl die Kundendetails als auch die Produktinformationen im selben Dokument für weitere Auswertungen zur Verfügung.

34.7 Transformation von Daten nach $lookup

Nach dem Einbinden externer Daten lassen sich diese natürlich weiter verarbeiten, filtern oder zusammenfassen. Hier ein Beispiel, bei dem nach dem $lookup nur bestimmte Felder aus den verbundenen Datensätzen projiziert werden:

db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customerId",
      foreignField: "_id",
      as: "customerInfo"
    }
  },
  {
    $project: {
      orderId: "$_id",
      customerName: { $arrayElemAt: ["$customerInfo.name", 0] },
      orderValue: { $sum: "$items.price" }
    }
  }
])

Aus dem eingebundenen customerInfo-Array wird der erste Eintrag extrahiert, um den Kundennamen direkt im Ergebnis anzuzeigen. Gleichzeitig wird der Gesamtbestellwert berechnet und das Feld _id in orderId umbenannt.

34.8 Nutzung komplexer Bedingungen

Mit Operatoren wie $cond, $ifNull oder logischen Operatoren lassen sich komplexe Bedingungen definieren. Hier ein Beispiel, bei dem für jeden Datensatz geprüft wird, ob er ein bestimmtes Feld enthält und andernfalls ein Standardwert zugewiesen wird:

db.inventory.aggregate([
  {
    $project: {
      _id: 0,
      item: 1,
      price: {
        $cond: {
          if: { $gte: ["$price", 0] },
          then: "$price",
          else: 0
        }
      }
    }
  }
])

Sollte kein Preis gesetzt sein, wird der Wert automatisch auf 0 gesetzt.