MongoDB

Materialized Views mit $out und $merge

Die Vorbereitung von Daten für die Ansichten ist zentral für eine gute Performance der Anwendung, die auf MongoDB basiert. Hierbei kommen oft sogenannte Materialized Views zum Einsatz, also Views, deren Daten nicht erst bei der Ausführung einer Query dynamisch bestimmt werden, sondern vorbereitet in eigenen Collections abgelegt werden. Normalerweise werden hierfür Ausgangsdaten, beispielsweise in einer Time Series Collection, über Aggregationen aggregiert und zusammengefasst. Anstatt die Ausgabe der Aggregation Pipeline zu laden und wieder auf in das MongoDB-Cluster zurückzuschreiben, ist es ratsam, eine der beiden Stages $out oder $merge zu nutzen, um das Ergebnis in einer Collection abzulegen. Damit verbleiben die Daten im Cluster und wertvolle Bandbreite wird gespart.

Die Frage ist natürlich, welche Stage besser für den konkreten Fall geeignet ist und welche Rahmenbedingungen bestehen.

$out ersetzt, $merge integriert

Der wichtigste Unterschied zwischen den beiden Stages ist, dass bei der Verwendung von $out eine eventuell bereits vorhandene Collection vollständig ersetzt wird. Hierbei werden Indexe wiederhergestellt. Wenn die Operation scheitert, werden keine Veränderungen an der vorhandenen Collection vorgenommen, da bis zum letzten Ersetzen der vorhandenen Collection auf einer temporären Collection im Hintergrund gearbeitet wird.

Die $merge Stage erlaubt dagegen das Integrieren der Ausgabe in eine vorhandene Collection. Damit eignet sich die Stage auch für Szenarien, in denen eine Materialized View Stück für Stück gefüllt wird - beispielsweise täglich mit den Daten des Vortags. Das verringert die verwendete Datenmenge enorm und entlastet das Cluster bei großen Datenmengen deutlich. Das vollständige Ersetzen des Collection-Inhalts durch die Ergebnismenge der Aggregation ist jedoch nur dann möglich, wenn für jedes Dokument in der vorhandenen Collection auch ein Dokument im Ergebnis vorhanden ist.

Keine der beiden Stages kann in einer Multi-Document-Transaction verwendet werden.

Ausgabe in Ziel-Collections

In vielen Fällen werden die Ergebnisse der Aggregation Pipeline in eine Collection in der gleichen Datenbank ausgegeben; sowohl $out als auch $merge erlauben jedoch die Ausgabe von Daten in eine Collection, die sich in einer anderen Datenbank im Cluster befindet.

Time Series Collections eigenen sich zwar als Ausgangspunkt für $out oder $merge, können jedoch aktuell (MongoDB 7.0) noch nicht durch $out oder $merge gefüllt werden.

Ein wichtiger Unterschied ist, dass bei $merge die Ausgabe in eine Sharded-Collection möglich ist, bei $out jedoch nicht.

Definition einer $out Stage

Eine $out Stage ist immer die letzte Stage in einer Pipeline. Die Definition ist aufgrund des geradlinigen Ersetzens einer Ziel-Collection denkbar einfach:

[
  // ...
  { $out: 'output_collection_name' }
]

Falls in eine Collection in einer anderen Datenbank geschrieben werden soll, kann dies folgendermaßen erreicht werden:

[
  // ...
  { $out: { db: "output_db_name", coll: 'output_collection_name' } }
]

Definition einer $merge Stage

Auch die $merge Stage schließt eine Aggregation Pipeline ab. Die Menge der Parameter für eine $merge Stage ist etwas größer:

[
  // ...
    {
    $merge: {
      into: 'output_collection_name',
      on: '_id',
      whenMatched: 'merge',
      whenNotMatched: 'insert'
    }
  }
]

Das obige Beispiel zeigt die einfache Ausgabe in eine Collection in der gleichen Datenbank. Soll in eine andere Datenbank geschrieben werden, so kann diese ebenso angegeben werden wie oben bei der $out Stage gezeigt.
Im Beispiel wird auf Basis der _ id festgestellt, ob ein Dokument bereits in der Collection vorhanden ist. Hier kann auch ein Array mit Eigenschaften angegeben werden, jedoch muss dann ein eindeutiger Index vorhanden sein, der diese Eigenschaften umfasst.

Über whenMatched wird gesteuert, wie bei Dokumenten verfahren wird, die in der Collection bereits vorhanden sind:

  • “merge” (Standard): die beiden Dokumente werden zusammengeführt. Eigenschaften, die im vorhandenen Dokument vorhanden sind, werden überschrieben; nicht vorhandene Eigenschaften werden hinzugefügt.
  • “replace”: ein vorhandenes Dokument wird vollständig durch das Dokument im Ergebnis der Pipeline ersetzt.
  • “keepExisting”: das vorhandene Dokument wird nicht verändert - das Dokument im Ergebnis der Pipeline wird verworfen.
  • “fail”: Die Aggregation scheitert, allerdings werden bereits geschriebene Änderungen nicht zurück genommen.
  • Aggregation Pipeline: hiermit kann ein Update über eine Aggregation Pipeline definiert werden, so dass feingranular gesteuert werden kann, wie die Dokumente zusammengeführt werden.

Der whenNotMatched Parameter gibt an, was mit neuen Dokumenten im Ergebnis der Aggregation geschehen soll:

  • “insert” (Standard): neue Dokumente werden hinzugefügt.
  • “discard”: neue Dokumente werden verworfen.
  • “fail”: Die Aggregation scheitert, allerdings werden bereits geschriebene Änderungen nicht zurück genommen.

Vollständiges Ersetzen mit $merge

Wie oben bereits angemerkt, ist es mit $merge nicht möglich, den gesamten Inhalt einer Materialized View zu ersetzen. Um dies zu umgehen, kann man folgendes Vorgehen wählen:

Bei Aggregieren der Daten wird der Zeitstempel der letzten Aktualisierung hinzugefügt:

[
  // ...
  {
    $set: {
      upd: "$$NOW"
    }
  },
  {
    $merge: {
      // ...
    }
  }
]

Entweder schließt man an die Ausführung des $merge ein deleteMany an, durch das Dokumente gelöscht werden, die nicht aktualisiert wurden, oder man erstellt einen TTL-Index auf das Feld, so dass MongoDB die Dokumente automatisch löscht, wenn sie nicht mehr relevant sind.

Fazit

Der Vergleich der beiden Aggregation Stages zeigt, dass $out für einfache Fälle mit überschaubarer Datenmenge geeignet ist. Die Definition der Stage ist denkbar einfach, so dass sie gut bei der schnellen Datenaufbereitung im Rahmen der Entwicklung genutzt wird. Deutlich größere Möglichkeiten bietet hingegen die $merge Stage, die Teilbereiche einer Materialized View aktualisieren kann.


MongoDB Analyse Time Series Collections NoSql
Markus Wildgruber
Markus Wildgruber

Geschäftsführer

  • CloudArchitect
  • DeveloperCoach
  • DotNet
  • MongoDB
  • Angular