MongoDB
Es handelt sich bei MongoDB um eine dokumentenorientierte Datenbank.
Sie wird von MongoDB Inc. aus New York seit 2007 entwickelt.
Bei MongoDB enthalten die Datenbanken, jeweils n
Collections mit m
Dokumenten.
Aggregation Pipelines - Replica Sets
mongod
Datenbankserver Port 27017 und Webinterface auf Port 28017
mongod --help
Es wird die Konfigurationsdatei /etc/mongodb.conf
genutzt.
Repariert alle Datenbanken und gibt Festplatten Speicher frei.
mongod --repair
mongo
JavaScript-Konsole für dynamische Abfragen.
mongo <ip>:<port>/<db>
mongo <ip>:<port>/<db> script.js
mongoexport
Exportieren von Daten in JSON oder CSV Dateien.
mongoexport -d <database> -c <collection> --out dbexport.json
mongoexport -h <host> -d <database> -c <collection> -u <user> -p <password> -o <file>
mongoimport
Importieren von Daten aus JSON, CSV, oder TSV Dateien.
mongoimport -d <database> -c <collection> --file dbimport.json
mongoimport -d <database> -c <collection> < dbimport.json
mongoimport -h <host:port> -d <database> -c <collection> -u <user> -p <password> --file <file>
Bei CSV Dateien wird der Typ mit angegeben und ob die erste Zeile Header enthält.
mongoimport -d <database> -c <collection> --file dbimport.csv --headerline --type csv
Datenbank
Datenbanken werden automatisch angelegt, indem man die Datenbank mit use anspricht.
Der Daemon legt beim Initialisieren zwei neue Dateien im Datenverzeichnis an.
.0 ist zur Speicherung von Dokumenten im BSON-Format.
.ns enthält eine Liste der vorhandenen Collections.
> show dbs
> use <datenbankname>
Nachdem man eine Datenbank angelegt hat kann man diese auch wieder entfernen.
> use <datenbankname>
> db.dropDatabase();
Die Datenbank kann neugeschrieben werden was aber weitere Operationen blockiert.
> db.repairDatabase();
Die Größe wird mit stats()
ermitteln und in Bytes zurück gegeben.
> db.stats()
Collection
Collections gruppieren Dokumente und können mit Tabellen in einem RDBMS verglichen werden.
Sie können beliebig benannt werden (Buchstaben, Zahlen und Unterstriche) mit max. 128 Zeichen.
> db.createCollection("user");
{ "ok : 1 }
> show collections
system.indexes
user
> db.getCollectionNames()
[ "system.indexes", "users" ]
Eine Collection kann mit der Methode copyTo()
kopiert werden.
> db.user.copyTo(customer)
Um eine Collection zu löschen wird die drop()
Methode verwendet.
> db.user.drop();
Um die Daten einer Collection neu zuschreiben wird compact
genutzt.
> db.runCommand({compact: "user"});
Die Kombination von db.getCollectionNames
und db.runCommand
kann dies für alle Collections ausführen, was dann aber keinen Unterschied zu db.repairDatabase()
darstellt.
> db.getCollectionNames().forEach(function (collectionName) {
... print('Compacting: ' + collectionName);
... db.runCommand({ compact: collectionName });
...});
Capped Collection
Besitzen eine bestimmte Größe und nutzen FIFO (First in First out).
> db.createCollection(<name>, {capped:true, size: <bytes>, max: <docs>});
Sind aslBeispiel für Log-Einträge geeignet.
> db.createCollection("log", {capped:true, size: 100000});
System Collection
Hier werden Metainformationen zur Datenbank gespeichert, sie sind schreibgeschützt.
system.namespaces
Namespaces einer Datenbank.
system.indexes
Indizes für die einzelnen Collections.
system.profile
In dieser Collection werden Infomationen zum Profiling gespeichert.
> db.system.profile.find()
system.users
MongoDB nutzt diese Collection bei der Zugriffskontrolle.
Benutzer anzeigen.
> db.system.users.find()
Einen Benutzer hinzufügen.
> db.addUser("username", "password")
Einen Benutzer entfernen.
> db.removeUser("username")
> db.system.users.remove({user: "username"})
Index
Die Collection system.indexes
speichert die Index Deklarationen.
Die Indizes einer Collection kann man mit getIndexes()
abfragen.
> db.system.indexes.find()
> db.<collection>.getIndexes()
MongoDB erstellt einen Standard Index für das _id
Feld.
Einen Index erstellt man mit ensureIndex().
> db.<collection>.ensureIndex({<field>: 1})
> db.<collection>.ensureIndex({<field>: -1})
> db.<collection>.ensureIndex({<field1>: -1, <field2>: 1})
Löschen kann man einen Index mit dropIndex()
.
> db.<collection>.dropIndex({<field>: 1})
Die Größe eines Index kann man mit totalIndexSize()
ermitteln.
> db.<collection>.totalIndexSize()
Dokument
Die Dokumente sind auf eine Größe von 16 MB beschränkt.
Das Datenformat für Dokumente ist BSON (Binary JSON), eine JSON Erweiterung.
JSON-Standard
String, Integer, Boolean, Double, Array, Timestamp, Object, Null
BSON-Erweiterung
Date, Object ID, Binary Data, Regular Expressions
ObjektId
Diese wird immer unter _id gespeichert und automatisch beim speichern generiert.
{"_id": ObjectId("4aabc0023..."), ... }
DBRef
Zeigt auf ein Dokument in der gleichen oder einer anderen Collection.
{ $ref : <collname>, $id : <idvalue>[, $db : <datenbankname>] }
JavaScript shell
Auf der Konsole arbeitet man über die JavaScript Shell mit der Datenbank.
> use <db>
> db.<collection>.<action>(<parameters>)
Größe eines Dokuments feststellen
Mit findOne
kann man die Größe eines einzelnen Dokuments in Bytes ermitteln.
> Object.bsonsize(db.user.findOne())
Daten eintragen
Die Methode insert()
kann ein Dokument oder ein Array von Dokumenten speichern.
> db.<collection>.insert({...})
> db.<collection>.insert([{...},{...}])
> db.<collection>.insert({"name":"MongoDb"})
> db.<collection>.insert([{"name":"MongoDb"}, {"name":"Redis"}])
Bei der Methode save()
entscheidet MongoDB selbst, ob ein Insert oder Update ausgeführt wird.
Die Methode erwartet ein Dokument als Parameter.
> db.<collection>.save({...})
Daten abfragen
> db.<collection>.find(query, fields, num-docs, skip-docs)
> db.<collection>.find();
> db.<collection>.findOne(ObjectId("4c43717b310eda114fe1d35d"))
> db.<collection>.find({name:"mongodb"})
> db.<collection>.find({name:"mongodb", active:"True"})
> db.<collection>.find({name:"mongodb", active:"True"}).count()
> db.<collection>.find({name:"mongodb", active:"True"}).explain()
> db.<collection>.find().sort({name:-1}).limit(10)
> db.<collection>.count()
> db.<collection>.count({name:"mongodb"})
Es werden Vergleichsoperatoren geboten, um Werte kleiner gleich $lt
oder größer gleich $gt
abzufragen.
> db.<collection>.find({age:{$lt:30}})
> db.<collection>.find({age:{$gt:18, $lt:30}})
> db.<collection>.find({active: "True", age:{$lt: 30}})
> db.<collection>.find({$or: [{active: "True"}, {age:{$lt: 30}}]})
Mit $and
und $or
kann man logisches und/oder abfragen.
> db.<collection>.find({$or: [{age: 20},{age: 30}]})
> db.<collection>.find({$and: [{age: 20},{name: "Tester"}]})
Um zu schauen ob ein Feld existiert wird $exists
geboten.
> db.<collection>.find({active: {$exists: true}})
Daten ändern
> db.<collection>.update(query, new-document/update-document, upsert, multiple)
Um ein bestehendes Dokument zu überschreiben, wird diese Syntax genutzt.
> db.<collection>.update({name:"mongodb"}, {name:"MongoDB"})
Möchte man das Dokument nicht schreiben sondern nur Werte ersetzen, wird diese Syntax genutzt.
> db.<collection>.update({name:"mongodb"}, {$set: {name:"MongoDB"}})
Mit upsert
wird ein Update oder Insert ausgeführt, abhängig davon ob das Dokument vorhanden ist, dazu setzt man den dritten Parameter auf true.
> db.<collection>.update({name:"mongodb"}, {$set: {name:"MongoDB"}}, true)
Soll mehr als ein Dokument ersetzt werden setzt man multiple
als vierten Parameter.
> db.<collection>.update({name:"mongodb"}, {$set: {name:"MongoDB"}}, false, true)
Daten löschen
> db.<collection>.remove(query)
> db.<collection>.remove({name:"mongodb"})
> db.<collection>.remove(ObjectId("4c43717b310eda114fe1d35d"))
Soll nur ein Dokument gelöscht werden, setzt man den justOne
Parameter.
> db.<collection>.remove({name:"mongodb"}, true)
Ohne den Query Parameter werden alle Dokumente gelöscht, der Index bleibt erhalten.
> db.<collection>.remove({})
Aggregation Pipelines
MongoDB bietet ein Aggregations Framework an, welches eine Pipeline in Stages definiert.
Dokumente durchlaufen in einer Pipeline unterschiedliche Stages und werden weitergegeben.
Die Dokumente in diesem Beispiel haben diese Struktur und sind mit Faker
erstellt worden.
{
"_id" : ObjectId("5f31a4719378236ed740b830"),
"email" : "wbrock@medina-brooks.com",
"age" : 39,
"attrs" : {
"tags" : [
"C",
"B"
],
"active" : true,
"validated" : false
},
"changeDate" : ISODate("2020-08-10T21:48:01.005Z")
}
Eine Pipeline wird mit dem Kommando aggregate
ausgeführt.
> db.<collection>.aggregate([<stages>], { explain : true })>
Hier ein paar Beispiele wie Pipelines aussehen können.
Die Dokumente besitzen ein Array attrs.tags
, die Summe der jeweiligen Elemente kann man so abfragen.
[
{ $match: { 'attrs.tags': { $exists: true, $type: 'array' } } },
{ $unwind: '$attrs.tags' },
{ $sortByCount: '$attrs.tags' }
]
Die Ausgabe würde so aussehen.
{"_id": 'B', "count": 38}
{"_id": 'A', "count": 38}
{"_id": 'C', "count": 33}
$match verhält sich wie find
und ist oft der erste Schritt in einer Pipeline.
$unwind nimmt ein Array und gibt pro Element ein Dokument weiter wo das Array mit dem jeweiligen Element ersetzt wird.
$sortByCount gruppiert die Dokumente basierend auf Ausdruck und zählt diese.
Wenn man nur $sortByCount nutzt könnte man sehen wie die Collection mit attrs.validated
aufgeteilt ist.
[
{ $sortByCount: '$attrs.validated' }
]
Die Ausgabe würde so aussehen.
{"_id": false, "count": 276}
{"_id": true, "count": 724}
Dokumente in einer zeitlich Spanne abfragen und nur einen Teil des Dokuments zurückgeben.
[
{
$match: {
changeDate: {
$gte: ISODate("2020-08-01T00:00:00.000Z"),
$lt: ISODate("2020-08-06T00:00:00.000Z")
},
age: { $lt: 30 }
}
},
{
$project: {
_id: 0, email: 1, age: 1, 'attrs.tags': 1
}
}
]
Die Ausgabe von einem Dokument würde so aussehen.
{
"email" : "lee35@johnson-rowland.com",
"age" : 27,
"attrs" : {
"tags" : [
"C",
"B"
]
}
}
$match fragt Dokumente ab wo changeDate
in einer zeitlichen Spanne liegt und age
kleiner als 30 sein soll.
$project gibt an welche Felder zurückgegeben werden sollen, ohne diesen Schritt würde das gesamte Dokument zurückgegeben.
Möchte man berechnete Werte und eine Anzahl an Dokumenten bekommen kann man dies mit $group machen.
{
$group: {
_id: null,
count: { $sum: 1 },
averageAge: { $avg: "$age" },
maximumAge: { $max: "$age" },
minimumAge: { $min: "$age" }
}
}
Die Ausgabe würde so aussehen.
{
"_id" : null,
"count" : 1000.0,
"averageAge" : 41.804,
"maximumAge" : 65,
"minimumAge" : 20
}
Möchte man das Ergebnis einer Pipeline speichern kann man $out als letzten Schritt in der Pipeline nutzen.
{ $out: '<collection>' }
{ $out: { db: '<datenbankname>', coll: '<collection>' } }
Replica Sets
Ein Replica Set ist eine Gruppe von MongoDB Instanzen.
Diese Gruppe besteht aus einem Primary und mehreren Secondaries.
Der MongoDB Daemon muss entsprechend gestartet werden um mit Replica Sets zu arbeiten, der Parameter --rest
wird für das Webinterface benötigt.
mongod --replSet <set-name> --port <port> --dbpath <path> --rest
Anschliessend startet man mongo
und initialisiert die Replikation.
> rs.initiate()
Man kann weitere Server in die Gruppe hinzufügen und wieder entfernen.
> rs.add("<host:port>")
> rs.remove("<host:port>")
Mit der Funktion rs.conf()
kann man sich die aktuelle Konfiguration ausgeben lassen.
> rs.conf()
{
"_id": "<set-name>",
"version": 1,
"members": [
{
"_id": 0,
"host": "<host:port>"
},
{
"_id": 1,
"host": "<host:port>"
}
]
}
Den Status des Replica Sets kann man sich mit der status Funktion anzeigen lassen.
> rs.status()
Jede mongod Instanz hat eine eigene local Datenbank, die Daten zur Replikation enthält.
> use local
switched to db local
> show collections
oplog.rs
system.replset
Die aktuelle Konfiguration steht in der local Datenbank.
> use local
switched to db local
> db.system.replset.find()
{"_id": "<set-name>", "version": 1, "members": [{"_id": 0, "host": "<host:port>"},{"_id": 1, "host": "<host:port>"}]}