Top

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>"}]}