29 Arbeiten mit der MongoDB Shell: mongosh als mächtiges Werkzeug

Die MongoDB Shell – mongosh – ist mehr als ein simples Command-Line-Interface. Sie ist eine vollwertige JavaScript-Umgebung, ein Debugging-Tool, ein Prototyping-Environment und oft der erste Kontaktpunkt zwischen Entwicklern und MongoDB. Wer mongosh beherrscht, kann Daten explorieren, Queries testen, Maintenance-Tasks durchführen und komplexe Aggregations-Pipelines entwickeln – alles ohne eine Zeile Anwendungs-Code zu schreiben.

mongosh ersetzte 2020 die alte mongo-Shell und brachte fundamentale Verbesserungen: Syntax-Highlighting, Autocomplete, moderne JavaScript-Support (ES2020+), bessere Error-Messages und eine extendible Plugin-Architektur. Für Entwickler, die aus modernen JavaScript-Umgebungen kommen, fühlt sich mongosh vertraut an. Für Admins ist sie ein mächtiges Tool für ad-hoc-Queries und schnelles Troubleshooting. Die Lernkurve ist flach, aber die Tiefe ist erheblich.

29.1 Der Start: Verbindung und erste Schritte

Das Starten von mongosh ohne Parameter verbindet zu localhost:27017 – dem MongoDB-Default:

mongosh

Die Shell zeigt eine Willkommens-Message mit Version-Info, Verbindungs-Details und einem Prompt:

Current Mongosh Log ID: 65abc123def456789012345
Connecting to:      mongodb://127.0.0.1:27017/?directConnection=true
Using MongoDB:      7.0.5
Using Mongosh:      2.1.1

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

test>

Der Prompt zeigt die aktuelle Datenbank – “test” ist der Default. Man ist sofort interaktiv und kann Befehle eingeben. Das test>-Prefix ändert sich, wenn man Datenbanken wechselt, was Orientierung gibt.

Für Verbindungen zu Remote-Hosts oder mit Authentifizierung nutzt man Connection Strings:

# Remote Host mit Port
mongosh "mongodb://server.example.com:27017"

# Mit Authentifizierung
mongosh "mongodb://admin:password@server.example.com:27017/admin"

# Replica Set
mongosh "mongodb://server1,server2,server3/mydb?replicaSet=myrs"

# Mit TLS/SSL
mongosh "mongodb://server.example.com:27017/?tls=true&tlsCAFile=/path/to/ca.pem"

Connection Strings folgen dem MongoDB URI-Format. Alle Optionen – Authentifizierung, Replica Set, Read Preference, TLS – können im String kodiert werden. Für komplexe Setups ist dies übersichtlicher als multiple CLI-Flags.

Nach erfolgreicher Verbindung hat man Zugriff auf das db-Objekt – das zentrale Interface zu MongoDB. db referenziert die aktuelle Datenbank. Methoden auf db führen Datenbankoperationen aus, Methoden auf db.<collection> operieren auf Collections.

Wer mit einem neuen MongoDB-System arbeitet, startet typischerweise mit Exploration. Welche Datenbanken existieren? Welche Collections? Wie sehen die Dokumente aus?

// Alle Datenbanken listen
show dbs
// Oder expliziter
db.adminCommand({ listDatabases: 1 })

// Zu einer Datenbank wechseln
use myapp

// Collections in aktueller Datenbank
show collections
// Oder
db.getCollectionNames()

Das use-Command wechselt die aktuelle Datenbank. Existiert die Datenbank nicht, erstellt MongoDB sie beim ersten Write implizit. Dies ist schemaless-Convenience – keine expliziten CREATE DATABASE-Statements nötig.

Um zu sehen, wie Dokumente strukturiert sind, fetcht man ein paar Samples:

// Ein Dokument
db.users.findOne()

// Fünf Dokumente
db.users.find().limit(5)

// Mit Pretty-Print für bessere Lesbarkeit
db.users.find().limit(5).pretty()

Die .pretty()-Methode formatiert JSON mit Indentation. Moderne mongosh-Versionen pretty-printen automatisch, aber explizites .pretty() funktioniert noch für Kompatibilität.

Um zu verstehen, wie Felder verteilt sind oder welche Typen existieren, hilft Aggregation:

// Eindeutige Werte eines Feldes
db.users.distinct("status")

// Zählung nach Typ eines Feldes
db.users.aggregate([
  { $group: { 
      _id: { $type: "$age" },
      count: { $sum: 1 }
  }}
])

Diese explorierenden Queries sind essentiell beim Debugging oder wenn man mit unfamiliären Daten arbeitet. Ein paar Minuten Exploration spart Stunden frustrierender “warum funktioniert meine Query nicht?”-Debugging.

29.3 Interaktives JavaScript: Mehr als nur Queries

mongosh ist keine Domain-Specific Language, sondern eine vollwertige JavaScript-Umgebung. Man kann Variablen definieren, Funktionen schreiben, Control-Flow nutzen – beliebigen JavaScript-Code:

// Variablen und Konstanten
const dbName = "production"
let userCount = 0

// Funktionen
function countActiveUsers() {
  return db.users.countDocuments({ status: "active" })
}

// Loops
for (let i = 0; i < 10; i++) {
  db.testdata.insertOne({ 
    num: i, 
    squared: i * i,
    timestamp: new Date()
  })
}

// Conditionals
const count = db.users.countDocuments()
if (count > 1000000) {
  print("Database is large, consider archiving")
} else {
  print("Database size is manageable")
}

Diese Programmierbarkeit ist mächtig für komplexe Tasks. Ein Beispiel: Bulk-Update mit komplexer Logik:

// Iteriere über alle inactive Users und archiviere sie
db.users.find({ status: "inactive", lastLogin: { $lt: new Date("2023-01-01") } })
  .forEach(user => {
    // Archiviere in separate Collection
    db.archivedUsers.insertOne({
      ...user,
      archivedAt: new Date(),
      archivedReason: "Inactive for over 1 year"
    })
    
    // Lösche Original
    db.users.deleteOne({ _id: user._id })
    
    // Log
    print(`Archived user ${user.username}`)
  })

Dieser Code macht etwas, das in einer single-Query schwierig wäre: Multi-step-Operation mit Logging. Die .forEach()-Methode auf Cursors ist essentiell für solche Tasks.

29.4 Modern JavaScript: async/await und Promises

mongosh unterstützt moderne JavaScript-Features wie async/await. Viele MongoDB-Operationen sind asynchron, und async/await macht den Code lesbarer als Callback-Hell:

// Async Funktion
async function getUserWithOrders(userId) {
  const user = await db.users.findOne({ _id: userId })
  
  if (!user) {
    throw new Error("User not found")
  }
  
  const orders = await db.orders.find({ customerId: userId }).toArray()
  
  return {
    ...user,
    orderCount: orders.length,
    totalSpent: orders.reduce((sum, o) => sum + o.total, 0)
  }
}

// Ausführen
getUserWithOrders(ObjectId("..."))
  .then(result => printjson(result))
  .catch(err => print("Error:", err))

// Oder mit await im Top-Level (ES2022+)
const result = await getUserWithOrders(ObjectId("..."))
printjson(result)

Top-Level await ist in mongosh verfügbar – keine Wrapper-Function nötig. Dies ist praktisch für schnelle Scripts.

29.5 Autocomplete und Introspection: Die Shell kennt MongoDB

Eine der größten Verbesserungen von mongosh über die alte mongo-Shell ist Autocomplete. Drückt man Tab, zeigt mongosh verfügbare Methoden:

db.users.find<TAB>
// Zeigt: find, findAndModify, findOne, findOneAndDelete, findOneAndReplace, findOneAndUpdate

db.users.find().<TAB>
// Zeigt Cursor-Methoden: limit, skip, sort, count, explain, forEach, map, ...

Dies ist nicht nur Convenience, sondern Discovery-Tool. Man muss nicht die Dokumentation öffnen, um zu sehen, welche Methoden existieren. Das Autocomplete ist context-aware – es weiß, ob man auf einem Collection-Objekt, einem Cursor oder der Datenbank ist.

Die .help()-Methoden geben detailliertere Info:

// Hilfe für Datenbank-Operationen
db.help()

// Hilfe für Collection-Operationen
db.users.help()

// Hilfe für spezifische Methode
db.users.find.help()

Die Help-Messages sind konzise aber informativ, oft mit Beispiel-Syntax.

29.6 Der .editor: Multi-Line-Code komfortabel schreiben

Für längeren Code ist die interaktive Zeile-für-Zeile-Eingabe umständlich. Der .editor-Command öffnet einen Editor im Terminal:

.editor
// Editor öffnet sich

Im Editor kann man mehrzeiligen Code schreiben, mit Syntax-Highlighting und ohne dass jede Zeile sofort executed wird. Nach Beenden (typischerweise Ctrl+D oder Ctrl+Z je nach System) wird der gesamte Code als Block ausgeführt.

Dies ist ideal für komplexe Aggregations-Pipelines oder Scripts mit mehreren Funktionen. Die Alternative wäre, Code in eine .js-Datei zu schreiben und mit load() zu importieren:

// code.js enthält Funktionen und Scripts
load("./myScripts.js")

// Funktionen aus der Datei sind nun verfügbar
myCustomFunction()

Die load()-Funktion ist nützlich für wiederverwendbare Scripts oder wenn man Code versionskontrollieren will.

29.7 Snippets: Wiederverwendbarer Code

mongosh 2.0+ hat ein Snippet-System – die Möglichkeit, häufig genutzte Code-Blöcke zu speichern und mit Namen abzurufen:

// Snippet speichern
snippet.save("activeUsers", `
  db.users.find({ 
    status: "active",
    lastLogin: { $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
  }).count()
`, "Count active users from last 30 days")

// Snippet ausführen
snippet.run("activeUsers")

// Alle Snippets listen
snippet.list()

// Snippet löschen
snippet.delete("activeUsers")

Snippets werden persistent gespeichert (in ~/.mongodb/mongosh/snippets) und sind über Sessions hinweg verfügbar. Für oft wiederholte Queries oder Maintenance-Tasks ist dies ein Time-Saver.

29.8 Explain: Query-Performance verstehen

Eine der mächtigsten Debugging-Tools in mongosh ist .explain(). Es zeigt, wie MongoDB eine Query ausführt – welche Indizes genutzt werden, wie viele Dokumente gescannt wurden, wie lange es dauerte:

// Explain für find
db.users.find({ age: { $gt: 25 } }).explain("executionStats")

// Explain für Aggregation
db.orders.aggregate([
  { $match: { status: "shipped" } },
  { $group: { _id: "$customerId", total: { $sum: "$amount" } } }
]).explain("executionStats")

Das "executionStats"-Level gibt detaillierte Performance-Daten. Die Output ist umfangreich, aber die wichtigsten Felder:

Eine Query mit COLLSCAN (Collection Scan) und totalDocsExamined gleich Millionen ist ein Red Flag – sie scannt die gesamte Collection. Ein fehlender Index ist wahrscheinlich. Die ideale Query hat totalKeysExamined ähnlich oder kleiner als returned Documents und nutzt IXSCAN.

29.9 Administration und Maintenance: mongosh als Admin-Tool

mongosh ist nicht nur für Queries, sondern auch für Administration. Viele admin-Tasks, die sonst GUIs oder separate Tools erfordern, sind in der Shell möglich.

Benutzer erstellen:

use admin
db.createUser({
  user: "appUser",
  pwd: "securePassword123",
  roles: [
    { role: "readWrite", db: "myapp" },
    { role: "read", db: "analytics" }
  ]
})

Indizes verwalten:

// Index erstellen
db.users.createIndex({ email: 1 }, { unique: true })

// Alle Indizes einer Collection
db.users.getIndexes()

// Index löschen
db.users.dropIndex("email_1")

Replica Set Status:

rs.status()
rs.conf()

Sharded Cluster Info:

sh.status()

Database Stats:

db.stats()
db.users.stats()

Diese Commands geben JSON-Output, den man programmatisch weiterverarbeiten kann. Für automatisierte Monitoring-Scripts oder Custom-Admin-Tools ist mongosh oft der einfachste Weg.

29.10 Profiling und Monitoring: Slow Queries finden

MongoDB hat einen Profiler, der langsame Queries logged. Man kann ihn in mongosh aktivieren und analysieren:

// Profiler aktivieren (Level 2: log alle Operationen)
db.setProfilingLevel(2)

// Oder nur Slow Queries (>100ms)
db.setProfilingLevel(1, { slowms: 100 })

// Profiler-Daten analysieren
db.system.profile.find().sort({ ts: -1 }).limit(10)

// Slowest Queries finden
db.system.profile.find({ millis: { $gt: 1000 } }).sort({ millis: -1 })

Die system.profile-Collection speichert alle geloggten Operations. Jedes Dokument enthält Query-Details, Execution-Time, genutzten Index, etc. Für Performance-Debugging ist dies Gold.

29.11 Scripting und Automation: mongosh in CI/CD

mongosh kann Scripts von Dateien ausführen, was Automation ermöglicht:

# Script ausführen
mongosh --file script.js

# Mit Connection String
mongosh "mongodb://server:27017/mydb" --file migration.js

# Quiet Mode (nur Output, kein Prompt/Banner)
mongosh --quiet --file cleanup.js

Ein typisches Migrations-Script könnte so aussehen:

// migration.js
print("Starting migration...")

const result = db.users.updateMany(
  { country: { $exists: false } },
  { $set: { country: "UNKNOWN" } }
)

print(`Updated ${result.modifiedCount} documents`)

// Validation
const missingCountry = db.users.countDocuments({ country: { $exists: false } })
if (missingCountry > 0) {
  print(`ERROR: ${missingCountry} documents still missing country field`)
  quit(1)  // Exit mit Error-Code
}

print("Migration completed successfully")

Dieses Script updated Dokumente, validiert das Resultat und exitiert mit Error-Code bei Problemen. In CI/CD-Pipelines kann man den Exit-Code prüfen, um Success/Failure zu detektieren.

29.12 Tips und Tricks für produktive Nutzung

Command History: mongosh speichert History in ~/.mongodb/mongosh/.mongosh_repl_history. Man kann mit Pfeiltasten durch vorherige Commands navigieren. Ctrl+R startet Reverse-Search durch History.

Output-Formatting: Für große Outputs ist .pretty() hilfreich, aber für maschinelle Verarbeitung ist kompaktes JSON besser. Die printjson()-Funktion gibt formatiertes JSON aus:

const result = db.users.findOne()
printjson(result)

Exit: Um mongosh zu beenden: exit, quit() oder Ctrl+D.

Clear Screen: cls oder Ctrl+L löscht den Bildschirm.

Telemetrie: mongosh sammelt anonyme Usage-Statistiken (welche Commands genutzt werden, Crash-Reports). Dies ist opt-out:

disableTelemetry()

Config File: mongosh kann eine Config-Datei nutzen für persistente Settings. Die Datei ist ~/.mongoshrc.js und wird beim Start automatisch executed:

// ~/.mongoshrc.js
// Automatisch executed bei jedem mongosh-Start

// Helper-Funktionen definieren
function countAll(collectionName) {
  return db[collectionName].countDocuments()
}

// Default-Einstellungen
DBQuery.shellBatchSize = 10  // Nur 10 Dokumente per Batch statt 20

print("Custom mongoshrc loaded! Use countAll('collectionName') for quick counts")

Die .mongoshrc.js ist wie .bashrc für Bash – ein Platz für persönliche Customizations und Helper-Functions.

Die folgende Tabelle fasst wichtige mongosh-Features zusammen:

Feature Zweck Typischer Use-Case
Autocomplete (Tab) Discovery Methoden explorieren
.explain() Performance-Analyse Slow Queries debuggen
.editor Multi-Line-Code Komplexe Scripts schreiben
snippet Code-Reuse Häufige Queries speichern
load() Script-Import Wiederverwendbare Functions
.help() Dokumentation Quick Reference
db.stats() Monitoring Speicher-Nutzung prüfen
rs.status() Replica Set Admin Replication checken
.mongoshrc.js Customization Persönliche Helpers

mongosh ist mehr als ein Terminal-Interface zu MongoDB. Es ist ein vollwertiges Development-Environment, ein Admin-Tool und oft der schnellste Weg, um Daten zu explorieren, Queries zu testen oder Maintenance-Tasks durchzuführen. Die moderne JavaScript-Integration macht es vertraut für Web-Entwickler. Die mächtigen Features wie Explain, Profiling und Snippets machen es essentiell für Performance-Tuning und Troubleshooting. Wer mongosh meistert, hat ein Werkzeug, das in nahezu jedem MongoDB-Workflow nützlich ist – von ersten Prototypen bis zu komplexen Produktions-Deployments.