MongoDB

Gesamtwerte und prozentuale Anteile ermitteln

Interessiert man sich für den prozentualen Anteil eines Werts am Gesamtwert, leistet $setWindowFields gute Dienste. Mit dieser Aggregation Stage können Berechnungen innerhalb eines Bereichs von Dokumenten durchgeführt werden und das Ergebnis als Feld hinzugefügt werden. Also genau das, was wir brauchen, um den Anteil eines Werts am Gesamtwert ermitteln zu können.

Als Beispiel gehen wir von folgender Dokumentenstruktur aus, die jeweils einen Zeitstempel und einen Wert umfasst:

[{
  "ts": ISODate("2023-09-20T00:00:00.000Z"),
  "value": 100
},
{
  "ts": ISODate("2023-09-21T00:00:00.000Z"),
  "value": 150
},
{
  "ts": ISODate("2023-09-24T00:00:00.000Z"),
  "value": 50
},
{
  "ts": ISODate("2023-09-26T00:00:00.000Z"),
  "value": 10
}]

Folgende Aggregation-Pipeline berechnet den Gesamtwerts des value-Felds:

[
  {
    $setWindowFields: {
      partitionBy: null,
      output: {
        total: {
          $sum: "$value",
          window: {
            documents: ["unbounded", "unbounded"],
          },
        },
      },
    },
  },
]

Durch die Angabe von null als Kriterium zur Bildung der Partitionen und unbounded als Grenzen des Fensters, werden alle Dokumente einbezogen, so dass die Gesamtsumme gebildet wird.
Als Ergebnis werden folgende Dokumente geliefert:

[{
  "ts": ISODate("2023-09-20T00:00:00.000Z"),
  "value": 100,
  "total": 310
},
{
  "ts": ISODate("2023-09-21T00:00:00.000Z"),
  "value": 150,
  "total": 310
},
{
  "ts": ISODate("2023-09-24T00:00:00.000Z"),
  "value": 50,
  "total": 310
},
{
  "ts": ISODate("2023-09-26T00:00:00.000Z"),
  "value": 10,
  "total": 310
}]

Eine einfache $set-Stage wird anschließend zum Berechnen des Anteils genutzt:

[
  {
    $setWindowFields: {
      partitionBy: null,
      output: {
        total: {
          $sum: "$value",
          window: {
            documents: ["unbounded", "unbounded"],
          },
        },
      },
    },
  },
  {
    $set: {
      pct: {
        $divide: ["$value", "$total"],
      },
    },
  },
]

Sollte es möglich sein, dass die Gesamtsumme den Wert Null ergibt, muss man den Ausdruck in der $set-Stage erweitern, um zuvor zu prüfen, ob die Gesamtsumme gleich 0 ist und gegebenenfalls auf die Division verzichten. Damit ist man gegen eine Division durch 0 gewappnet.
Für unser Beispiel führt die Aggregation zu folgender Ausgabe:

[{
  "ts": ISODate("2023-09-20T00:00:00.000Z"),
  "value": 100,
  "total": 310,
  "pct": 0.3225806451612903
},
{
  "ts": ISODate("2023-09-21T00:00:00.000Z"),
  "value": 150,
  "total": 310,
  "pct": 0.4838709677419355
},
{
  "ts": ISODate("2023-09-24T00:00:00.000Z"),
  "value": 50,
  "total": 310,
  "pct": 0.16129032258064516
},
{
  "ts": ISODate("2023-09-26T00:00:00.000Z"),
  "value": 10,
  "total": 310,
  "pct": 0.03225806451612903
}]

Weitere Möglichkeiten

Durch die Verwendung des partitionBy-Felds kann der Bereich eingeschränkt werden, für den die Gesamtsumme gebildet wird; wenn also beispielsweise verschiedene Sensoren berücksichtigt werden sollen oder die Tagessumme relevant ist, kann über eine geeignete Partitionierung die entsprechende Summe gebildet werden.

Fazit

Die Berechnung von prozentualen Anteilen ist nur ein Beispiel für die Möglichkeiten, die $setWindowFields bietet. Weitere Artikel in dieser Serie zeigen zusätzliche Beispiele wie diese bei der Ermittlung von Kennzahlen eingesetzt werden können.


MongoDB Analyse Time Series Collections NoSql
Markus Wildgruber
Markus Wildgruber

Geschäftsführer

  • CloudArchitect
  • DeveloperCoach
  • DotNet
  • MongoDB
  • Angular