Meine Bibliothek-Dokumente

Multi-Root-Seitenleiste

flex-layout

  • Getting Started

  • Guides

  • Reference

typed-rx-http

  • Getting Started

  • Guides

  • Reference

global-rx-state

  • Getting Started

  • Guides

  • Reference

webflux-fe-dev-assistant

reactive-mongo-dsl

Übersicht

Ein kleiner Hilfsdienst für RDBMS-Entwickler, die MongoDB zum ersten Mal verwenden, der sich entsprechend den Anforderungen des Projekts entwickelt hat und die Geschichte und Designkriterien beschreibt, die zur Driver-first MongoDB Convenience-Schicht in 1.0.0 führten.


Ursprungsgeschichte

Ich habe etwa vier Jahre lang hauptsächlich mit RDBMS entwickelt und war auch in der Produktionsumgebung größtenteils mit AWS RDS vertraut. Als ich ein neues persönliches Projekt startete, wählte ich MongoDB zum ersten Mal und stellte fest, dass es nicht nur eine Datenbank mit flexibler Schema war, sondern ein Werkzeug, das meine Entwicklungsproduktivität erheblich verändern könnte.

Insbesondere MongoDB Atlas war für mich eine neue Welt. Ich konnte professionelle Suche und Vektorsuche, von denen ich dachte, dass ich separate Systeme anschließen müsste, innerhalb einer einzigen Datenplattform nutzen, und Betriebsaufgaben wie Backup, Monitoring und Skalierung waren für mich, der nur mit AWS RDS gearbeitet hatte, viel bequemer. Wenn ich eine professionellere Skalierung und Funktionalität benötige, könnte ich Suchmaschinen oder Vektordatenbanken als separate Dienste trennen, aber in der Phase, in der ich ein Projekt schnell erstellen und betreiben wollte, war der Umfang, den ich mit Atlas lösen konnte, sehr attraktiv.

Das Problem war, dass es für mich als Neuling in MongoDB zu langsam war, dynamische Abfragen zu schreiben. Zunächst war die findBy...-Form des Repositories ausreichend, aber als die Suchbedingungen komplexer wurden, musste ich Query, Criteria und Operatoren manuell zusammenstellen. In einem ungewohnten Zustand war es leicht, Tippfehler zu machen, und das ständige Schreiben ähnlicher Codes verlangsamte die Entwicklung erheblich.

Begann mit einer einfachen DSL für Abfragen

Deshalb war das erste, was ich machte, eine kleine Klasse, um nur das R von CRUD einfach zu erstellen. Dies war der Ausgangspunkt der Klasse, die heute als ReactiveMongoDsl bekannt ist. Zu dieser Zeit gab es keine Funktionen wie Aggregation, Pipeline oder Lookup, und es ging nur darum, die Query, Criteria und Paging von Spring Data ReactiveMongoTemplate etwas schneller zusammenzustellen. Ich dachte, dass die restlichen Aufgaben mit dem grundlegenden ReactiveMongoTemplate oder dem MongoDB Driver ausreichend erledigt werden könnten.

Aber die Meinung eines Entwicklers, mit dem ich zusammenarbeitete, war anders. Auch er war neu in MongoDB und fragte jedes Mal, wenn er Funktionen wie Upsert oder Bulk benötigte, ob diese in der DSL, die ich erstellt hatte, vorhanden waren. Ich hätte ihm sagen können, dass er die grundlegenden APIs direkt verwenden soll, aber ich dachte, dass er die gleichen Schwierigkeiten haben würde, die ich beim ersten Lernen von MongoDB hatte. Also begann ich, nach und nach die benötigten Funktionen in die ReactiveMongoDsl hinzuzufügen.

Anfangs hatte es etwa 600 Zeilen, und als es 1.000 Zeilen erreichte, dachte ich nicht, dass es notwendig sei, es in mehrere Klassen zu unterteilen. Als es etwa 2.000 Zeilen hatte, überlegte ich ein wenig, aber die Struktur zur Trennung fühlte sich eher größer an, und vor allem war ich ehrlich gesagt zu faul für diese Arbeit. Als es 5.000 Zeilen überschritt, konnte ich es nicht länger aufschieben und verschob einige trennbare Funktionen nach außen, aber es war bereits eine große Belastung, den Kernfluss detailliert neu zu unterteilen. Der Grund, warum die Kernklasse so groß ist, liegt nicht daran, dass ich von Anfang an eine riesige DSL entworfen habe, sondern vielmehr an der Geschichte, dass ich die Funktionen, die in echten Projekten benötigt wurden, immer wieder am gleichen Einstiegspunkt hinzugefügt habe.

Nachdem ich Funktionen wie Abfragen, Aggregationen, Lookups, atomare Updates, Bulk, Historie, Atlas Search und Vektor-Suche hinzugefügt hatte, stellte ich fest, dass die meisten Funktionen, die ich häufig benötigte, beim Arbeiten mit MongoDB in Java-Projekten bereits enthalten waren. Anstatt sie nur als interne Hilfsfunktionen in einem Projekt zu belassen, trennte ich sie, damit sie in mehreren Projekten auf die gleiche Weise verwendet werden konnten, und das ist der Entstehungsprozess von reactive-mongo-dsl.

Von Spring-Hilfen zu einer Driver-first-Bibliothek

Der Ausgangspunkt war ein Hilfsdienst, um ReactiveMongoTemplate einfacher zu verwenden, aber als die Funktionen zunahmen und ich begann, sie in mehreren Projekten wiederzuverwenden, kam ich zu dem Schluss, dass es nicht richtig ist, dass das Ausführungsmodell eines bestimmten Frameworks die Grenze der Bibliothek selbst bildet. Der Kern von 1.0.0 verwendet MongoExecutionContext als Ausführungsvertrag und nutzt direkt den MongoDB Reactive Streams Driver.

Diese Veränderung ist nicht dazu gedacht, Spring Data MongoDB auszuschließen. In Spring-Anwendungen können die Collection-Namensgebung von ReactiveMongoTemplate, MongoConverter, benutzerdefinierte Konvertierungen und reaktive Audits über den MongoExecutionContext-Adapter verbunden werden. Der Kern bleibt unabhängig vom Framework, und nur die benötigten Anwendungen verwenden die bestehenden Spring-Einstellungen.

Gleichzeitig wurde auch klarer, dass die MongoDB Driver-Funktionen, die bereits gut bereitgestellt werden, nicht innerhalb der DSL neu implementiert werden sollten. Allgemeine Aggregationen können direkt als Bson-Stufen empfangen werden, und für Search/Vector gibt es Escape-Hatches wie SearchOperator, VectorSearchQuery, driverOptions(...), stage(Bson). Dies ist eine Entscheidung, um sicherzustellen, dass neue Funktionen des Drivers sofort verwendet werden können, ohne auf die nächste Veröffentlichung der DSL zu warten.

Entwicklungsphilosophie

Ich verstecke MongoDB nicht.

Ich wollte kein ORM erstellen, das die Funktionen von MongoDB in andere Datenbankkonzepte umwandelt. Stattdessen verbinde ich die Abfragen, Aggregationen, Transaktionen, Atlas Search und Vektor-Suche von MongoDB in einem kürzeren und leichter auffindbaren Fluss.

Ich füge nur die Funktionen hinzu, die tatsächlich benötigt werden.

Es handelt sich nicht um eine Bibliothek, die zuerst eine Funktionsliste entworfen und dann implementiert wurde. Da in einem laufenden Projekt Abfragen erforderlich waren, wurden diese hinzugefügt, und da Upsert und Bulk benötigt wurden, wurden diese Funktionen sowie die Suchfunktionen hinzugefügt, was zur Integration von Atlas Search und Vector Search führte.

Produktivität hat Vorrang vor struktureller Perfektion.

Wir behaupten nicht, dass eine große Kernklasse die ideale Struktur ist. Zu Beginn war es wichtiger, dass das Team unbekannte MongoDB-Operationen schnell auf die gleiche Weise verarbeiten kann, und auch jetzt priorisieren wir den tatsächlichen Nutzungfluss über die Abstraktion selbst.

Wir erstellen nicht erneut, was der Driver bereits tut.

Wenn der MongoDB Java Driver einen typisierten Builder hat, verwenden wir diesen Typ so weit wie möglich. Wiederholbare Kombinationen, die durch die DSL wertvoll reduziert werden können, werden über eine Convenience-API bereitgestellt, aber wir duplizieren die Driver-API nicht einfach mit einem anderen Namen.

Es sollte möglich sein, auf die grundlegende API zurückzugreifen.

Wir denken nicht, dass die DSL alle Situationen abdecken kann. Es gibt Escape-Hatches wie Bson, Driver-Filter/-Sortierungen/-Operatoren/-Optionen und Publisher-Customizer, sodass der MongoDB Driver bei Bedarf direkt verwendet werden kann.

Die vier Flüsse, die in diesem Dokument behandelt werden.

Allgemeine Mongo-Abfrage.

Dies ist der Fluss, in dem diese Bibliothek ursprünglich gestartet wurde. Die Bedingungen und Ausführungen werden in der Reihenfolge execute* → fields(...) → end() → find/findAll/count/delete/exists/atomicUpdate konfiguriert.

Driver-native Aggregation.

Um ab der ersten Stage des Pipelines direkt zu steuern oder die neu bereitgestellten Aggregationsfunktionen des Drivers zu verwenden, verwenden Sie den Fluss aggregation().stage(Bson). Stages wie $score und $scoreFusion, die bereits vom Driver bereitgestellt werden, werden nicht erneut von der DSL implementiert.

Atlas-Suche

Dies ist der Fluss, der hinzugefügt wurde, während Atlas Search in das Projekt integriert wurde, ohne einen separaten Suchdienst zuerst einzuführen. Nach search(index) werden Text, Compound, Score, Highlight und Sequenz-Token konfiguriert.

Vektorsuche

Dies ist der Fluss, der hinzugefügt wurde, um die Suche innerhalb von MongoDB bis zur Einbettung zu verbinden. Es werden Abfragevektoren oder automatisierte Einbettungen, ANN/ENN, Vor-/Nachfilter und Optionen für verschachtelte/Array-Einbettungen konfiguriert.

Ausführungsmodell von 1.0.0.
text

Die Trennung zwischen allgemeinen Abfragen und Search/Vector erfolgt, um die tatsächlichen Pipeline-Beschränkungen von MongoDB nicht zu verbergen. $search und $vectorSearch haben Einschränkungen in der ersten Stage, und Funktionen, bei denen der Aufrufer die gesamte erste Stage steuern muss, können auf aggregation() zurückgreifen.

Anwendungsbereich in realen Projekten

Bedingungslose Paare werden als null übergeben, um dynamische Suchbedingungen für verschiedene Bildschirme zusammenzustellen.

Mit PageStream werden Daten und totalCount getrennt, um den Fluss der Massenverarbeitung im Zustand eines reaktiven Publishers aufrechtzuerhalten.

Mit atomicUpdate().upsertOne().document().setOnInsert(...) werden wiederholbare, sichere Operationen erstellt.

Gesammelte Daten und externe Integrationsdaten werden basierend auf ID oder Geschäftsschlüssel mit Bulk-Upsert gespeichert.

Mit executeLookupAndCount werden die Lookup-Ergebnisse und die Gesamtanzahl in einem Pipeline zurückgegeben.

Es werden die Sequenz-Token von Atlas Search und die automatisierte Einbettungsabfrage von Vector Search verwendet.

Neue Aggregationsstufen, die vom Driver bereitgestellt werden, werden direkt mit aggregation().stage(Bson) oder stage(Bson) von Search/Vector verbunden.

Wenn Atomizität zwischen DSL-Operationen erforderlich ist, wird der Transaktionsbereich von ClientSession mit getTxJob(...) angegeben.

© 2026 Byeolnaerim. Alle Rechte vorbehalten.EinführungDatenschutzrichtlinie