PHP Magazin

PHP, JavaScript, Open Web Technologies
X

Unser neues Quickvote: Welche PHP-Version nutzt ihr?

Ein Blick auf das CouchDB-Backend für FLOW

NoSQL mit FLOW3

Karsten Dambekalns
An diesem Beispiel kann man sehen, dass die in der Klasse festgelegten Properties (in diesem Fall nur $identifier) sowie einige Metadaten gegenüber dem FLOW3-internen Format fast unverändert gespeichert werden. Lediglich _id und _rev sind CouchDB-spezifisch. Da CouchDB, wie fast alle NoSQL-DatenBanken, keine Join-Operationen unterstützt, werden Value-Objekte vom CouchDB-Backend inline gespeichert, d. h. mit der sie referenzierenden Entity zusammen. Das kann man im folgenden Beispiel an der Eigenschaft personalData erkennen, wie in Listing 2 dargestellt.

Listing 2

{
   "_id": "5117884658",
   "_rev": "1-0237b3c8111f37a1fa5f650ac238c5ec",
   "classname": "F3\\MyPackage\\Domain\\Model\\Customer",
   "properties": {
       "customerNumber": {
           "type": "string",
           "multivalue": false,
           "value": "5117884658"
       },
       "personalData": {
           "type": "F3\\MyPackage\\Domain\\Model\\PersonalData",
           "multivalue": false,
           "value": {
               "identifier": "2fec5643c6312dc4d1d1d9a2502f6403d689aaf0",
               "classname": "F3\\Package\\Domain\\Model\\PersonalData",
               "properties": {
                   "salutation": {
                       "type": "string",
                       "multivalue": false,
                       "value": "ms"
                   },
                   "firstname": {
                       "type": "string",
                       "multivalue": false,
                       "value": "Lucious"
                   },
                   "lastname": {
                       "type": "string",
                       "multivalue": false,
                       "value": "Farrell"
                   },
                   "dateOfBirth": {
                       "type": "string",
                       "multivalue": false,
                       "value": "1995-06-22"
                   }
               }
           }
       },
   },
   "parentIdentifier": null
}

Obwohl PersonalData ein Objekt ist und auch einen Identifier hat, wird es direkt in personalData abgelegt und kann so beim Laden der Daten für das Customer-Objekt direkt mit abgefragt werden. Eine ähnliche Optimierung ist für Entities möglich. Hier werden beim Rekonstituieren der persistierten Entities Datensätze von referenzierten Objekten nicht direkt geladen, sondern es werden zunächst nur die Identifier gesammelt. Im Anschluss werden alle nötigen Daten in einer Anfrage auf die _all_docs-View von CouchDB abgeholt.

Ein Drop-in Replacement?

Wenn man die Begeisterung für NoSQL nur oberflächlich verfolgt, ist es nicht schwer, die folgende Erwartungshaltung aufzubauen: Wir schreiben ein Backend für NoSQL-Datenbank X, in dem wir unsere Daten als JSON ablegen und die FLOW3-Queries in das passende spezifische Format umwandeln. Dieses Backend nutzen wir dann an Stelle des bisherigen, und schon geht alles viel schneller als mit RDBMS Y. Soweit die Theorie, der Teufel steckt aber wie üblich im Detail. Das Speichern und Laden von Daten war in der Tat recht schnell erledigt. Da FLOW3 intern bereits alle Objekte in ein "objektfreies" Zwischenformat umwandelt, ist es tatsächlich kein Problem, es als JSON an CouchDB zu verfüttern. Auch das Wiederherstellen von Objekten ist vergleichbar einfach zu bewerkstelligen. Allerdings hat FLOW3 ein zwar recht einfaches, aber dennoch flexibles Query Object Model (QOM) um nach bestimmten Objekten zu suchen. Es ermöglicht beispielsweise beliebige Konjunktionen und Disjunktionen der verfügbaren Operatoren sowie das Sortieren nach beliebigen Properties.

Listing 3

$query = $this->createQuery();
$query->setOrderings(array('index' => \F3\FLOW3\Persistence\QueryInterface::ORDER_ASCENDING));
$query->matching(
  $query->logicalAnd(
    $query->equals('workspace', $workspace),
    $query->equals('depth', $depth),
    $query->like('path', $path)
  )
);

Eine vergleichbare Funktion bietet CouchDB nicht. Genau genommen gibt es in CouchDB gar keine Abfragesprache - sämtliche Suchanfragen müssen über Views und entsprechende Start- und End-Keys erledigt werden. Auch das Sortieren der Ergebnisse ist nicht wie gewohnt möglich, es kann wieder nur der Key auf- oder absteigend sortiert werden. In der Praxis wird dieses Problem durch zwei Dinge entschärft. Erstens ist es selten, dass Queries vollständig dynamisch aufgebaut werden - nur wenn der Nutzer einer Software selbst Anfragen zusammenstellen kann, ist das nötig. Sind jedoch nur Parameter in einer in ihrer Struktur festgelegten Abfrage vom Nutzer zu verändern, kann man die Abfrage bereits als View für CouchDB vorgeben. Aber selbst das ist nur selten notwendig, denn zweitens lassen sich die häufigsten Abfragen automatisch aus dem QOM von FLOW3 in entsprechende CouchDB-Views umwandeln. Ein Beispiel ist diese Query aus dem AccountRepository von FLOW3:

$result = $query->matching(
  $query->logicalAnd(
    $query->equals('accountIdentifier', $accountIdentifier),
    $query->equals('authenticationProviderName', $authenticationProviderName)
  )
);

Diese Anfrage wird für CouchDB in die folgende Map-Funktion umgesetzt:

function(doc) {
  if(doc.classname == "F3\FLOW3\Security\Account") {
    emit([doc.properties["accountIdentifier"].value, doc.properties["authenticationProviderName"].value], null);
  }
}

Aufgerufen mit den passenden Tupeln für Start- und End-Key kann man die passenden Datensätze abrufen, das wird von FLOW3 im Hintergrund erledigt. Alles in allem ist der Umstieg auf CouchDB für den Entwickler, der das FLOW3-Framework nutzt, nicht mit großen Umstellungen verbunden. Es wird allerdings Fälle geben, in denen man die geforderten Queries nur schwer wird umsetzen können.

Ist NoSQL besser als SQL?

Der aktuelle NoSQL-Ansatz dient vor allem der Lösung von Skalierungsproblemen. Dass man hier nur weiterkommt, wenn man Kompromisse eingeht, muss jedem klar sein, der den Einsatz einer Datenbank wie CouchDB in Betracht zieht. Abfragen, die intensiv mit Verknüpfungen arbeiten und mit einem JOIN in SQL recht einfach zu realisieren wären, sind in NoSQL-Systemen unmöglich. Man kann das durch Denormalisierung umgehen oder die entsprechende Logik in seiner Anwendung implementieren, verlagert damit das Problem aber nur. Ähnlich liegt die Sache bei Aggregatsfunktionen, auch wenn hier mit Map/Reduce-Funktionen Lösungen möglich sind. Eine schöne Zusammenfassung dieser Problematik liefert Christian Köhntopp in einem Blogartikel [6].

Arbeitet man jedoch mit einem Datenmodell, in dem die Verbindungen zwischen Objekten weniger im Vordergrund stehen und gleichzeitig die Skalierbarkeit wichtig ist, kann man mit Systemen wie CouchDB richtig liegen. Es kommt also bei der Entscheidung für oder wider NoSQL vor allem auf die konkreten Rahmenbedingungen an, eine definitive Antwort gibt es nicht. Und auch wenn FLOW3 die Unterschiede zwischen RDBMS und NoSQL-Systemen weitestgehend abstrahiert, darf man nicht erwarten, ein komplexes Projekt transparent von einem Persistenz-Backend auf das andere umstellen zu können. Zu unterschiedlich sind an dieser Stelle die Konzepte.

alt="Karsten Dambekalns" width="50" />

Karsten Dambekalns, Jahrgang 1977, hat die Grundlagen des Webs noch auf die harte Tour gelert, indem er die Quelltexte fremder Websites las. 2002 entdeckte er TYPO3 und war von den enormen Möglichkeiten fasziniert. Heute ist er Teil des "TYPO3 5.0" und FLOW3-Entwicklerteams und Mitglied des Steering Committee der TYPO3 Association. Karsten lebt mit seiner Frau Liga, seinen drei Kindern und einer Espressomaschine in Lübeck.

 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).