MongoDB
Mit MongoDB wollen wir nur eine der vielen NoSQL-Datenbanken vorstellen
- Im
Unterschied zu relationalen Datenbanksystemen verzichten NoSQL-Datenbanken auf Tabellen und feste Strukturen
- Daten werden in Dokumenten gespeichert, deren Aufbau keinem strengen Schema unterliegt
- Das ist einerseits sehr praktisch, weil man sich komplizierte Datenbankschema-Anpassungen spart, kann aber auch zu Verwirrung führen, wenn jedes Dokument eine andere Struktur aufweist
Gerade im Umfeld von modernen Webapplikationen sind NoSQL-Datenbanken sehr beliebt
- Das als Austauschformat zwischen Frontend und Backend verbreitete JSON-
Format lässt sich sehr komfortabel, oft ohne es zu modifizieren, in NoSQL-Datenbanken speichern oder aktualisieren MongoDB hat außerdem den Ruf, bei sehr großen Datenmengen (Stichwort Big Data) gut zu skalieren, selbst da, wo relationale Datenbanken an ihre Grenzen sto- ßen
- Abstriche macht man dafür bei der Transaktionssicherheit und einer möglichen
Datenduplizierung
NoSQL ausprobieren Auch wenn Ihr Webprojekt nicht Millionen von Zugriffen verzeichnen wird, sollten Sie einen Blick auf MongoDB werfen
- Für viele Einsatzzwecke ist es eine sehr interessante
Alternative zu MySQL/MariaDB oder PostgreSQL
Einfaches docker-compose-Setup mit MongoDB Für das erste Beispiel werden wir, wie im vorangegangenen Abschnitt über PostgreSQL, wieder den Deutschland-Datensatz von Geonames importieren
- MongoDB
verfügt in der Standardinstallation bereits über einige sehr praktische Funktionen für räumliche Abfragen
- Da der Beispieldatensatz georeferenzierte Punkte enthält, können Sie mit MongoDB einfach Abfragen erstellen, wie Gib mir alle Hotels im Umkreis von einem Kilometer um den Kölner Dom
- Genau das wollen wir im folgenden Beispiel erreichen
Das offizielle Image auf dem Docker Hub baut auf Debian auf und bietet ebenso wie PostgreSQL und MariaDB die Möglichkeit, eine Datenbank während der Initialisierung einzurichten
- Wenig überraschend heißt der Ordner, in dem die Daten dabei vorliegen müssen, wieder docker-entrypoint-initdb.d
- MongoDB akzeptiert hier allerdings keine Datenbank-Dumps, sondern verlangt entweder Shell-Scripts oder JavaScript-Dateien, die direkt auf diejenige Datenbank angewendet werden, die in der Umgebungsvariablen MONGO_INITDB_DATABASE angegeben ist
Laden Sie sich die CSV-Datei von http://download.geonames.org/export/dump/DE.zip herunter, und entpacken Sie sie in einem Verzeichnis namens init
- Sie haben dort jetzt eine Datei DE.txt, die im CSV-Format (mit Tabulatoren getrennt) über 190.000
Punkte in Deutschland enthält
- Mit dem kleinen Shell-Script 01_seed.sh werden Sie diese Daten in die Datenbank »füttern«
- Datei : db / mongo / init /01 _seed .sh mongoimpo r t --db geonames --collecti on geoname \
--type tsv \ --fields = geonameid ,\ name ,\ asciiname ,\ alternatenames ,\ latitude ,\ longitude ,\ feature_class ,\ feature_code ,\ country_code ,\ cc2 ,\ admin1_code ,\ admin2_code ,\ admin3_code ,\ admin4_code ,\ population ,\ elevation ,\ dem ,\ timezone ,\ modificat i o n _ da t e \
/docker - entrypoint - initdb
- d / DE
- txt
Dem mongoimport -Kommando muss die Liste aller Felder in der CSV-Datei übergeben werden, denn der Datensatz enthält keine eigene Kopfzeile mit diesen Angaben (sonst könnten Sie diese mit der Option --headerline übernehmen)
- Der Name der
Datenbank ist geonames , und die Collection heißt geoname. (Eine Collection ist in etwa mit einem Ordner für Dokumente vergleichbar.) MongoDB ist hier sehr nachsichtig Wenn die Datenbank oder die Collection nicht existiert, wird sie erstellt
- Der Typ tsv teilt dem Importer mit, dass die Felder durch Tabulatoren und nicht durch andere
Trennzeichen (wie etwa Kommata oder Strichpunkte) getrennt sind Um räumliche Abfragen zu erstellen, benötigen Sie einen speziellen Index in der Collection
- Ein Index vom Typ 2d kann Distanzen auf einer ebenen Oberfläche berechnen; mit dem Typ 2dsphere werden Abstände auf einer erdähnlichen Kugel errechnet
Mit folgendem Script erstellen Sie die notwendige Struktur und den Index
// Datei : db / mongo /init /02 _createi n d ex
- js print (" Generate GeoJson points ...")
let i = 0
db
- geoname
- find ()
- forEach ( data = > {
i ++ db
- geoname
- update ( { _id : data . _id } , {
$set : { location : { type : "Point " ,
coordinat e s : [ parseFlo a t (data
- longitude ) , parseFloa t ( data
latitude ) ] } }
})
})
print ( i + " points done , generate 2 d -index ...") db
- geoname
- createInd e x ( { location : "2 dsphere " } )
Die forEach-Schleife läuft über alle Dokumente in der Collection (find() ohne Einschränkung liefert alle Dokumente) und erweitert jedes Dokument um einen location-Eintrag
- Hier werden Längenund Breitengrad-Angaben in dem standardisierten GeoJSON-Format gespeichert
- Die abschließende Zeile erzeugt den Index vom Typ
2dsphere auf dem location -Feld
Jetzt fehlt nur noch die compose.yaml-Datei, und das MongoDB-Setup ist bereit
- Datei : db / mongo / compose
- yaml services
db
image : mongo :5 volumes - mongo :/ data / db - ./ init :/ docker - entrypoint - initdb
- d
- ./ queries :/ queries environm e nt MONGO_INI T D B _D A T A B AS E : geonames volumes
mongo
Starten Sie nun das Setup mit docker compose up
- Die Ausgabe sollte so ähnlich aussehen wie hier
mongo - db -1 | / usr /local /bin / docker - entrypoin t
- sh
running / docker - entrypoint - initdb
- d/01 _seed
- sh mongo - db -1 | {" t ":{" $date ":"2023 -07 -04 T09 :59:31.379+00: 00 "} ,
[...] mongo - db -1 | 2023 -07 -04 T09 :59:31.3 8 0+ 0 0 0 0 connecte d to mongodb :// localhost /
mongo - db -1 | 2023 -07 -04 T09 :59:34.. . . [####### # ## # # # # #. . . . . .. . . ] geonames
- geoname 15.3 MB /23.6 MB (65.0%)
mongo - db -1 | 2023 -07 -04 T09 :59:36.. . . [####### # ## # # # # ## # # # # ## # # ] geonames
- geoname 23.6 MB /23.6 MB (100.0%)
mongo - db -1 | 2023 -07 -04 T09 :59:36.0 1 4+ 0 0 0 0 198305 document ( s) imported successfu l l y . 0 document ( s) failed to import [...] mongo - db -1 | / usr /local /bin / docker - entrypoin t
- sh : running
/ docker - entrypoint - initdb
- d /02 _createi n d ex
- js mongo - db -1 | Generate GeoJson points
mongo - db -1 | 198305 points done , generate 2 d - index
Das docker-entrypoint.sh-Script führt die Dateien im init-Verzeichnis in alphabetischer Reihenfolge aus, wodurch die Benennung der Dateien mit 01_seed.sh und 02_createindex.js sinnvoll ist
- Wie Sie sehen, wird zuerst das Shell-Script ausgeführt, das die 198.305 Datensätze importiert, und anschließend die JavaScript-Datei, die die
GeoJSON-Punkte erzeugt und den Index erstellt
Datenbankzugriff Normalerweise werden Sie auf die Datenbank über eine Programmiersprache zugreifen
- In diesem Beispiel wollen wir aber kein Programm schreiben, sondern Abfragen über das mitgelieferte Kommandozeilenprogramm mongo ausführen
- mongo kann mit einer JavaScript-Datei als Parameter aufgerufen werden, ähnlich wie Sie es schon bei dem Script 02_createindex gesehen haben
- Speichern Sie das folgende Script in einem
Ordner queries ; in der docker compose-Konfiguration haben wir den Ordner schon im Container eingebunden
// Datei : db / mongo /queries / koeln
- js let dom = db
- geoname
- findOne ({
name : / K ö ln . * Dom /
})
const query = [
{
$geoNear : {
near : dom
- location , spherical : true , distanceF i e l d : 'dis ' , query : { feature_ c od e : 'HTL ' }
}
}, {
$limit : 10
}
]
let res = db
- geoname
- aggregat e (query )
res
- forEach ( data = > {
print (Math
- round (data
- dis ) + "m : " + data .name )
})
In dem Script werden zwei Abfragen ausgeführt: Zuerst suchen wir genau ein Dokument (findOne), in dem das Feld name den regulären Ausdruck /Köln.*Dom/ enthält
- Das
Ergebnis bleibt in der Variablen dom gespeichert Die zweite Abfrage verwendet dieses Ergebnis und setzt es in der aggregate -Abfrage bei near ein
- Außerdem schränken wir dabei die gesuchten Dokumente über das feature_code -Feld ein
Abschließend läuft eine forEach-Schleife über alle gefundenen Ergebnisse und gibt die Entfernung und den Namen des Hotels an
- Das Script können Sie jetzt mit dem mongo-Kommando im Container ausführen
docker compose exec db mongo geonames / queries / koeln
- js
MongoDB shell version v5 .0.18 connecti n g to : mongodb ://127.0. 0 . 1 :2 7 0 1 7 / geonames ? compress or Implicit session : session { "id " : UUID ("49 eaa0d4 -9 e33 -4 d55 - MongoDB server version : 5.0.18 90 m : Dom Hotel Le Meridien 121 m : Dom Hotel - A Meridien Hotel 126 m : Althoff Dom Hotel Koln 147 m : Sofitel Cologne Mondial Am Dom 156 m : City Class Hotel Europa Am Dom 166 m : Sofitel am Dom 168 m : Eden Fr ü h Am Dom 185 m : Haus Enteresan 185 m : Gir Keller G ä stehaus 185 m : Hotel Kunibert Der Fiese
Alle, die jetzt auf den Geschmack von MongoDB gekommen sind und sich etwas intensiver mit dieser Datenbank auseinandersetzen wollen, möchten wir auf das sehr nützliche Werkzeug Compass hinweisen (siehe Abbildung 10.3) https://www.mongodb.com/products/compass In der freien grafischen Benutzeroberfläche können Sie Ihre Mongo-Datenbanken übersichtlich verwalten, und es hilft vor allem bei der Erstellung und Analyse von Aggregation-Pipelines, eine sehr mächtige Abfragemöglichkeit, die wir im vorliegenden Beispiel verwendet haben ($geoNear)
Abbildung 10.3 Das Programm MongoDB-Compass bei der Erstellung einer Aggregate- Abfrage
MongoDB mit Authentifizierung In der Standardeinstellung benötigt der Zugriff auf MongoDB keine Authentifizierung
- Das klingt im ersten Moment etwas unheimlich, für ein abgeschottetes Setup oder eine reine Entwicklungsumgebung kann es aber ausreichend sein
- Bei einem
Produktivsystem kann es nicht schaden, auch für MongoDB einen Benutzernamen und ein Passwort zu verlangen Um die Authentifizierung zu aktivieren, reicht es, die beiden Umgebungsvariablen MONGO_INITDB_ROOT_USERNAME und MONGO_INITDB_ROOT_PASSWORD zu setzen
- Beim Start des Containers wird dadurch automatisch der Parameter --auth an den Datenbankserver übergeben; der Dienst ist dann nur noch mit der entsprechenden Authentifizierung zu erreichen
Das Start-Script kann auch mit der moderneren Variante der Docker-Secrets umgehen (siehe den Abschnitt »Passwörter und andere Geheimnisse« in Abschnitt 5.3, »Die Datei compose.yaml«)
- Verwenden Sie dazu einfach statt MONGO_
INITDB_ROOT_PASSWORD die Variable MONGO_INITDB_ROOT_PASSWORD_FILE , ergänzen Sie
die compose.yaml-Datei um den notwendigen secrets-Abschnitt, und speichern Sie die Datei mongo_root.txt mit dem Passwort im aktuellen Verzeichnis
- Datei : db / mongo - auth / compose .yaml services
db
image : mongo :5 volumes - mongo :/ data / db - ./ init :/ docker - entrypoint - initdb
- d
environm e nt MONGO_INI T D B _D A T A B AS E : geonames MONGO_INI T D B _R O O T _ US E R N A ME : dockerbu ch MONGO_INI T D B _R O O T _ PA S S W O RD _ F I L E : / run / secrets / mongo_ro ot secrets - mongo_ro o t volumes
mongo secrets mongo_ro o t
file : ./ mongo_roo t
- txt
Der so erzeugte Superuser wird in der Regel verwendet, um die Rechte für die Benutzer der Datenbanken zu verwalten, aber nicht, um selbst Datenbanken anzulegen und zu befüllen
- Wenn Sie die Authentifizierung für die Datenbank aktivieren, ist es also außerdem sinnvoll, beim Start des Containers einen Benutzer mit Schreibrechten für eine Datenbank hinzuzufügen
- Wir werden einen Benutzer geonames einrichten, der Schreibzugriff auf die Datenbank geonames erhält
- Bei der Ausführung der JavaScript-Dateien im docker-entrypoint-initdb.d-Verzeichnis wird noch keine
Authentifizierung verlangt, daher reicht das folgende kurze Script zum Erstellen des Benutzers
// Datei : db / mongo -auth / init / adduser .js db
- createUs e r ({ user : 'geonames ' , pwd : 'geheim ' , roles : [{ role : 'readWrite ' , db : 'geonames '}]})
Wenn Sie sich in diesem Setup ohne Angabe von Benutzername und Passwort mit der Datenbank verbinden, können Sie keine Abfragen mehr ausführen
$ docker compose exec db mongosh geonames
Current Mongosh Log ID : 64 a3facbf00 3 2 c 7f e d b b c9 8 e Connecti n g to : mongodb ://127.0 . 0 . 1: 2 7 0 1 7/ geonames ? dir Using MongoDB : 5.0.18 Using Mongosh : 1.10.0
For mongosh info see : https :// docs .mongodb
- com /mongodb - shell /
geonames > db
- getColle c ti o n N a me s ()
MongoSer v e r Er r o r : command listColl ec t i o n s requires authenti c a t io n
Starten Sie das Kommando mit der Angabe des Benutzernamens, so können Sie nach Eingabe des Passworts die mongosh-Shell wie gewohnt verwenden
$ docker compose exec db mongosh -u geonames geonames Enter password : ****** Current Mongosh Log ID : 64 a3fba5add 2 2 9 0b 6 d 7 5 4d 6 7 [...] geonames > db
- getCollec t i on N a m e s ()
[ 'geonames ' ]