Februar 2016

Band 31, Nummer 2

Dieser Artikel wurde maschinell übersetzt.

Die Arbeit Programmierer - mit dem Mittelwert: Was kann MongoDB?

Durch Ted Neward | Februar 2016

Ted NewardWillkommen Sie bei "MEANies." (Ich habe mich entschieden, dass das besser als "Nodeists" und außerdem hört sich diese Information ab, wir nach nur Knoten verschieben.)

Der Code hat einen wichtige Übergangspunkt erreicht, es sind einige Tests zu überprüfen, ob die Funktionalität bleibt gleich, es ist sicherer, einige ernsthafte Umgestaltung beginnen. Insbesondere die aktuellen Daten im Arbeitsspeicher Speichersystem möglicherweise gut für schnelle Demos, jedoch wird hier nicht besonders gut mit der Zeit zu skalieren. (Ganz zu schweigen davon, alles ist eine VM-Neustart/Restart, und Sie verlieren alle Daten ist, die nicht in den Startcode hartcodiert.) Es ist das "M" untersuchen in ermöglichen: MongoDB.

Hu-MongoDB-Organisationseinheiten

Zuallererst, es wichtig zu erkennen, dass gemäß beliebte überlieferten MongoDB tatsächlich seinen Namen aus dem Wort "humongous" Ruft Ob dieses Bit Internet spätere Aktualisierungsvorgänge erfüllt ist oder nicht, dazu dient, Unterstrich, MongoDB wird nicht erstellt, die genau denselben Funktionsumfang wie die durchschnittliche relationale Datenbank bereitzustellen. MongoDB Skalierbarkeit hoch bewertet, und zu diesem Zweck MongoDB bereit ist, etwas Konsistenz zu opfern Handel deaktiviert ACID-Transaktionsfunktionen über den Cluster zugunsten "eventual Consistency".

Dieses System erreichen die Skalierung der Bazillions Datensätze möglicherweise nicht einmal; in der Tat würde ich ziemlich Erschrockener, wenn sie jemals in Shouting Abstand Sie stammt. MongoDB hat jedoch ein weiteres Feature, das auf der Skala "this is entdecken" Alarmglocken gleichermaßen hoch bewertet und das Datenmodell ist: Es ist eine dokumentorientierte Datenbank. Dies bedeutet, dass anstelle der herkömmlichen relationalen Tabellen und Spalten Schema erzwungenen Modell MongoDB "schemalos" Dokumente in Sammlungen erfasst ein Datenmodell verwendet. Diese Dokumente werden im JSON-Format dargestellt, und wie solche jedes Dokument besteht aus Name-Wert-Paare, die Werte herkömmliche möglich, Datentypen (Zeichenfolgen, Ganzzahlen, Gleitkommazahlen, boolesche Werte usw.) sowie weitere "composite" Daten Typen (Arrays von den Datentypen nur oder untergeordneten Objekte, die ihrerseits die Name-Wert-Paare enthalten können). Dies bedeutet, offhand, die Datenmodelle werden etwas anders, als Sie erwarten würden, wenn das einzige mit einer relationalen Datenbank ist; Diese Anwendung ist klein genug, nun, diese Unterschiede nicht sehr bei einer offenkundig werden, aber es etwas ist zu bedenken, bei der Arbeit mit komplexeren Speicherplatz benötigt wird.

Hinweis: Für eine genauere Betrachtung MongoDB aus .NET der Perspektive eines Entwicklers, sehen Sie sich diese Spalte 201 dreiteiligen Reihe auf MongoDB (bit.ly/1J7DjOB).

Entwerfen von Daten

Vom Entwurfsstandpunkt, sehen die Zuordnung des Datenmodells "Personen" für MongoDB ist einfach: Es ist eine Auflistung von "Personen" und jedes Dokument innerhalb der werden ein JSON-basierte Bündel von Name-Wert-Paaren und so weiter.

Und das war so ziemlich alles. Im Ernst, Dies ist Teil der Grund dafür, dass dokumentorientierte Datenbanken solche bevorzugen, in der Entwicklercommunity genießen werden – die Start-Kurve zum Abrufen von Daten in diese unglaublich niedrig ist, ist im Vergleich zu Schema-basierte relationale Gegenstücke. Dies hat ihre Nachteile, natürlich – ein Tippfehler und plötzlich alle Abfragen, die auf "FirstName" plötzlich stammen wieder leer ist, da kein Dokument ein Feld "FirstName" aufweist, jedoch werde ich einige Methoden zum Verringern des später.

Jetzt sehen wir uns einige Daten in und aus MongoDB abrufen.

Datenzugriff

Der erste Schritt ist zum Aktivieren der Anwendung die Kommunikation mit MongoDB. beinhaltet, überrascht nicht, installieren ein neue Npm-Paket, das mit dem Namen "Mongodb". Daher sollte jetzt in dieser Übung fast automatische erscheinen:

npm install --save mongodb

Das Npm-Tool wird durch die üblichen Gyrations Abwanderung und bei der Node.js MongoDB-Treiber in das Verzeichnis Node_modules installiert ist. Wenn Sie eine Warnung von Npm erhalten zu einem Kerberos-Paket nicht installiert ("erfordert einen Peer kerberos@~0.0 mongodb-core@1.2.28"), dies ist ein bekanntes Problem scheint behebbaren einfach Kerberos direkt über Npm ("Npm Install Kerberos") installieren. Es darf keine Probleme darüber hinaus, aber dies ist natürlich alle unterliegt der nächsten Version von dieser Pakete – Spaß an der Entwicklung für topaktuellen ist.

Als Nächstes wird der Code zum Öffnen einer Verbindung mit der MongoDB-Instanz muss. Wenn die Instanz befindet sich verdient eine kleine Diskussion.

Datenspeicherort

Wie bereits im ersten Artikel dieser Reihe erwähnt, es gibt zwei einfache Optionen für MongoDB: eine ist das lokal, was sich hervorragend für die Entwicklung aber nicht so gut für die Produktion Erfahrung; ist und der andere für die Ausführung in der Cloud, die hervorragend für die Produktion Erfahrung, aber nicht für die Entwicklung. (Wenn ich den Code ausführen können, wenn ich im Flugzeug auf einer Konferenz bin, ist es keine hervorragende Entwicklungsprozess, meiner Meinung nach.) Dies ist nicht ungewöhnlich Informationsquelle und der Lösung ist sehr ähnlich wie für jede Anwendung: es lokal ausgeführt, während der Entwicklung und aus der Cloud in Produktions- oder testen.

Wie die meisten Datenbanken benötigen Herstellen einer Verbindung mit MongoDB einen DNS-Servernamen oder IP-Adresse, einen Datenbanknamen und (optional) einen Anschluss. Bei der Entwicklung "Localhost", den Namen und "27017" (die MongoDB-Standard), werden normalerweise die Einstellungen für eine MongoDB-Instanz in der Cloud werden natürlich unterscheidet. Beispielsweise werden Server und Port für meine Mongolab MongoDB-Instanz namens "Msdn-Mittelwert" "ds054308.mongolab.com" und "54308," bzw..

Die einfachste Möglichkeit zum Erfassen dieser Abweichung im Knoten World eigenständige JS-Datei (in der Regel als "config.js" bezeichnet) zu erstellen und in den Code app.js erforderlich wie folgt:

// Load modules
var express = require('express'),
  bodyParser = require('body-parser'),
  debug = require('debug')('app'),
  _ = require('lodash');
// Go get your configuration settings
var config = require('./config.js');
debug("Mongo is available at",config.mongoServer,":",config.mongoPort);
// Create express instance
var app = express();
app.use(bodyParser.json());
// ... The rest as before

Was bleibt, dann ist für die Config-Datei, die Umgebung zu ermitteln, in der diese Anwendung ausgeführt wird. die übliche Weise dazu in der Node.js-Umgebung ist, untersuchen Sie eine Umgebungsvariable "ENV" eines "Prod", "Dev", festgelegt oder "test" (Wenn eine dritte, QA-orientierte Umgebung vorhanden ist). Daher muss der Config-Code Untersuchen der ENV-Umgebungsvariable und die richtigen Werte in das exportierte Objekt:

// config.js: Configuration determination
//
var debug = require('debug')('config');
debug("Configuring environment...");
// Use these as the default
module.exports = {
  mongoServer : "localhost",
  mongoPort : "27017"
};
if (process.env["ENV"] === "prod") {
  module.exports.mongoServer = "ds054308.mongolab.com";
  module.exports.mongoPort = "54308";
}

Beachten Sie die Verwendung des Objekts "Prozess" – Dies ist ein standard Node.js-Objekt, die in jeder ausgeführte Node.js-Anwendung immer implizit vorhanden und die Eigenschaft "Env" wird verwendet, um die Umgebungsvariable "ENV" zu suchen. (Spitze Leser werden Beachten Sie, dass der ExpressJS Code die gleiche Bedeutung, führt Wenn entscheiden, welcher Port verwendet, wahrscheinlich Sie diesen Ausschnitt gestalten könnten, um die config.js-Einstellungen auch verwenden, aber ich, die als Übung, wenn Sie den Reader lasse.)

So weit, so gut. Eigentlich besser; Dies hat eine schöne Trennung des Codes von der main-Codebasis auch implizit erstellt.

Beginnen wir, hinzufügen und Entfernen von Daten.

MongoDB + Node.js

Wie die meisten Datenbanken zum Öffnen einer Verbindung mit MongoDB, warten Sie auf das Objekt, und verwenden Sie, die für die nachfolgenden Aktionen in der Datenbank. Folglich es scheint ein offensichtlicher erster Schritt wäre, dieses Objekt zu erstellen, wie die Anwendung gestartet wird, und speichern es global, siehe Abbildung 1.

Abbildung 1: Erstellen eines Objekts in MongoDB

// Go get your configuration settings
var config = require('./config.js');
debug("Mongo is available at ",config.mongoServer,":",config.mongoPort);
// Connect to MongoDB
var mongo = null;
var persons = null;
var mongoURL = "mongodb://" + config.mongoServer +
  ":" + config.mongoPort + "/msdn-mean";
debug("Attempting connection to mongo @",mongoURL);
MongoClient.connect(mongoURL, function(err, db) {
  if (err) {
    debug("ERROR:", err);
  }
  else {
    debug("Connected correctly to server");
    mongo = db;
    mongo.collections(function(err, collections) {
      if (err) {
        debug("ERROR:", err);
      }
      else {
        for (var c in collections) {
          debug("Found collection",collections[c]);
        }
        persons = mongo.collection("persons");
      }
    });
  }
});
// Create express instance
var app = express();
app.use(bodyParser.json());
// ...

Beachten Sie, dass die Connect-Aufruf eine URL und einen Rückruf werden – dieser Rückruf ein Error-Objekt und das Verbindungsobjekt für die Datenbank wie die Node.js-Konvention wird als Parameter akzeptiert. Wenn der erste alles undefined oder null ist, ist ein Fehler, andernfalls swimmingly Lief. Die URL ist ein MongoDB-spezifische URL mithilfe des Schemas "Mongodb", aber andernfalls eine herkömmliche HTTP-URL sieht ähnlich.

Es ist jedoch eine Besonderheit auf diesen Code, der nicht offensichtlich ist zunächst: Der Rückruf wird zu einem bestimmten Zeitpunkt aufgerufen, nachdem die restlichen den Startcode abgeschlossen, die offensichtlicher beim Betrachten der Ausgabe Debug gedruckt wird ist, wie in dargestellt, Abbildung 2.

Debug-Ausgabe
Abbildung 2 Debuggen Druckausgabe

Sehen Sie, wie die Nachricht "Beispiel für app-Überwachung" vor der Meldung "Verbindung ordnungsgemäß zu Server" aus dem Rückruf angezeigt wird? Angesichts der Tatsache, dass dies beim Start der Anwendung der Fall ist, dieses Problem Parallelität nicht wichtig, aber wird sich daran, und werden Sie ohne Frage ein schwierigsten Teil der Arbeit mit Node.js. Es stimmt, dass Ihre Node.js Code nie gleichzeitig auf zwei Threads gleichzeitig, aber, die ausgeführt wird, müssen Sie einige interessante Probleme mit der Parallelität hier passiert nicht bedeutet nicht; Sie sehen nur unterscheiden, was Sie als .NET-Entwickler kennen.

Außerdem einfach, denken Sie daran, wenn dieser Code für eine ganz neue MongoDB-Datenbank, die Schleife Sammlungen zuerst ausgeführt wird Adressenfeld – MongoDB erstellt nicht die Sammlungen (oder sogar der Datenbank) bis dies unbedingt erforderlich ist, die in der Regel tritt auf, wenn jemand in diese schreibt. Nachdem eine Einfügung erfolgt ist, wird der MongoDB die erforderlichen Elemente und Datenstrukturen zum Speichern der Daten erstellt.

Unabhängig davon, haben wir für den Moment Datenbankverbindungen. Uhrzeit, zu die CRUD-Methoden sofort zu aktualisieren.

Einfügen

Die InsertPerson wird die Insert-Methode auf das Auflistungsobjekt MongoDB verwendet, und wieder, benötigen Sie einen Rückruf, der mit den Ergebnissen des Vorgangs Datenbank aufrufen:

var insertPerson = function(req, res) {
  var person = req.body;
  debug("Received", person);
  // person.id = personData.length + 1;
  // personData.push(person);
  persons.insert(person, function(err, result) {
    if (err)
      res.status(500).jsonp(err);
    else
      res.status(200).jsonp(person);
  });
};

Beachten Sie den Code auskommentiert (aus der Datenbank im Arbeitsspeicher-Version, die Migration von); Ich nach links, insbesondere, um einen Punkt zu beweisen. MongoDB wird einem Bezeichnerfeld "_id", die den primären Schlüssel für das Dokument in der Datenbank, sodass Code-Generator unglaublich verbesserungswürdig zweitägige "Id" nicht nur nicht mehr erforderlich ist aber vollständig unerwünschte erstellt.

Beachten Sie, dass die letzte Anweisung in der Funktion der Insert-Methode, mit dem Rückruf verknüpft ist. Obwohl es nicht erforderlich ist, dass dies die letzte Anweisung im Funktionsblocks sein, ist es wichtig zu verstehen, dass die Funktion InsertPerson beendet wird, bevor die Datenbank einfügen. Die Rückruf basierende Art von Node.js ist, sodass Sie nichts an den Aufrufer zurück, bis Sie den Erfolg oder Misserfolg des Vorgangs Datenbank wissen möchten – daher die Aufrufe von "Res" nicht allzu an einer beliebigen Stelle außerhalb des Rückrufs. (Skeptics sollte sich dieser verlegen Sie einen Debug-Aufruf nach dem persons.insert-Aufruf und eine andere in der Rückruffunktion selbst überzeugen und finden Sie unter der ersten ausgelöst, bevor der Rückruf wird.)

Alle abrufen

Fügt die Validierung erforderlich, damit während ich hier bin, werde ich GetAllPersons, Umgestalten der jedoch benötigt eine schnelle Abfragen auf die Sammlung aus, um alle Dokumente in der Auflistung gesucht werden soll.:

var getAllPersons = function(req, res) {
  persons.find({}).toArray(function(err, results) {
    if (err) {
      debug("getAllPersons--ERROR:",err);
      res.status(500).jsonp(err);
    }
    else {
      debug("getAllPersons:", results);
      res.status(200).jsonp(results);
    }
  });
};

Bevor Sie fortfahren, gibt es ein paar Sachen zu beachten: Zunächst wird der Aufruf suchen ein Prädikat Dokument beschreiben Sie die Kriterien, nach denen Sie die Auflistung abzufragen, wodurch die in diesem Fall ich als leer lassen möchten. würde dies eine Abfrage nach dem Vornamen, müssten das Prädikat Dokument aussehen:

"{ 'firstName':'Ted' }"

Zweitens feststellen, dass das zurückgegebene Objekt aus suchen eine tatsächliche Resultset, daher ToArray, um es ähnlich der Verwendung konvertieren aufgerufen werden muss. Die ToArray wird einen Rückruf und wieder jede Verzweigung des Rückrufs sicherstellen muss, dass etwas zurück an den Aufrufer über res.status () .jsonp kommunizieren.

Middleware

Bevor ich fortfahren können, wie verfasster, dass die GetPerson, UpdatePerson und DeletePerson Funktionen alle hängen von der PersonId Middleware-Funktion, um eine Person durch Bezeichner zu suchen. Dies bedeutet, dass diese Middleware muss aktualisiert werden, um die Sammlung von seiner _id-Feld (das ein MongoDB ObjectID, keine Zeichenfolge ist!), anstatt in das Array im Arbeitsspeicher, siehe Abbildung 3.

Abbildung 3 Middleware zum Abfragen der Sammlungssatzes aktualisieren

app.param('personId', function (req, res, next, personId) {
  debug("personId found:",personId);
  if (mongodb.ObjectId.isValid(personId)) {
    persons.find({"_id":new mongodb.ObjectId(personId)})
      .toArray(function(err, docs){
        if (err) {
          debug("ERROR: personId:",err);
          res.status(500).jsonp(err);
        }
        else if (docs.length < 1) {
          res.status(404).jsonp(
            { message: 'ID ' + personId + ' not found'});
        }
        else {
          debug("person:", docs[0]);
          req.person = docs[0];
          next();
        }
      });
  }
  else {
    res.status(404).jsonp({ message: 'ID ' + personId + ' not found'});
  }
});

Die MongoDB-Node.js-Treiber eine FindOne-Methode besser geeignet zu sein scheint, Dokumente, aber die Treiberdokumentation weist darauf hin, als veraltete Methode.

Beachten Sie, dass die Middleware, wenn sie eine ungültige Objekt-ID wird als Nächstes rufen Sie nicht. Dies ist eine schnelle Möglichkeit, speichern einige Codezeilen in die verschiedenen Methoden, die zum Suchen von Personen aus der Datenbank abhängig sind, da ist er nicht zulässige ID, sie dort wieder so Hand sein kann einen 404-Fehler. Dies gilt auch, wenn die Ergebnisse sind keine Dokumente (d. h., die ID in der Datenbank war nicht).

Rufen Sie ab, löschen und aktualisieren

Daher ist die Middleware GetPerson trivial, da sie alle möglichen Fehler oder Zustände Dokument nicht gefunden behandelt:

var getPerson = function(req, res) {
  res.status(200).jsonp(req.person);
};

Und DeletePerson ist fast genauso trivial:

var deletePerson = function(req, res) {
  debug(“Removing”, req.person.firstName, req.person.lastName);
  persons.deleteOne({“_id”:req.person._id}, function(err, result) {
    if (err) {
      debug(“deletePerson: ERROR:”, err);
      res.status(500).jsonp(err);
    }
    else {
      res.person._id = undefined;
      res.status(200).jsonp(req.person);
    }
  });
};

Beide stellen UpdatePerson ziemlich vorhersagbare:

var updatePerson = function(req, res) {
  debug(“Updating”,req.person,”with”,req.body);
  _.merge(req.person, req.body);
    persons.updateOne({“_id”:req.person._id}, req.person, function(err, result) {
    if (err)
      res.status(500).jsonp(err);
    else {
      res.status(200).jsonp(result);
    }
  });
};

Der Merge-Aufruf ist, die gleiche Lodash-Funktion vor verwendet, um die Eigenschaften vom Hauptteil Anforderung über das Person-Objekt zu kopieren, die aus der Datenbank geladen wurde.

Nachbereitung

Unglaublich. Dies war ein wenig mehr als einige der anderen in der Reihe, aber ich habe an diesem Punkt Code, der vollständig jetzt für eine MongoDB-Datenbank anstelle eines Array im Arbeitsspeicher ausgeführt wird, während die Objektmocks mit hatte. Es ist jedoch nicht die ideale, nicht weitem. Zunächst einmal werden keine Tippfehler im Code, um diese abfrageprädikate unerwartete Laufzeitfehler erstellt. Noch wichtiger ist, als .NET Entwickler, wir sind daran gewöhnt, mit einer "Domänenobjekt" zu besonders wenn irgendeine der Überprüfung auf die verschiedenen Eigenschaften des Objekts, das ausgeführt werden müssen – es ist nicht empfehlenswert, diese Überprüfungscode in der Express-Teile des Codes Basis zu verteilen. Für das nächste Mal befindet sich auf der Docket. Aber nun... viel Spaß beim Programmieren!


Ted Newardist CTO von iTrellis, einem Beratungsunternehmen Seattle-basierten Polytechnology. Er hat mehr als 100 Artikel geschrieben ist ein F#-MVP, INETA-Sprecher und erstellt oder ein Dutzend Bücher verfasst. Sie können ihn erreichen unter ted@tedneward.com wenn sie möchten, dass er in Ihrem Team arbeitet, oder sein Blog lesen unter blogs.tedneward.com.

Dank den folgenden technischen Experten für die Überprüfung dieses Artikels: Shawn Wildermuth