SQL- und NoSQL-Datenbanken

Einführung in Cypher

Sprachstruktur

Knoten Operationen

Kanten Operationen

Selektion

Projektionen

Aggregationsfunktionen

Index

Erweiterte Konstrukte

Tutorial Cypher

Cypher Query Language wurde von Neo Technology, Inc. Entwickelt, um Abfragen an die NoSQL Datenbank Neo4j zu formulieren. Durch den deklarativen Ansatz wird mit einer Abfrage das gewünschte Resultat beschrieben. Wie und wo die Daten geholt werden spielt dabei keine Rolle. Durch den Fokus auf ein effektives und einfaches Design, wurde eine Abfragesprache entwickelt, die sowohl für die Entwicklung als auch für den Betrieb geeignet ist. So sind simple Abfragen einfach und schnell zu formulieren und dennoch komplexe Konstrukte möglich.

Wie bereits in Abschnitt 3.4 gezeigt, repräsentieren Knoten Daten, welche durch Kanten in Beziehung zueinander gebracht werden können. Mittels Hinzufügen eines Labels kann ein Knoten einer Art zugewiesen und dadurch kategorisiert werden. Nebst Knoten können auch Kanten Daten enthalten, um Beziehungen zwischen Knoten zu beschreiben. Auf diese Weise ist es möglich, Kanten mit Hilfe von verschiedenen Typen weiter zu beschreiben. Die Definition und Struktur der Daten folgt in der Graph Datenbank Neo4j keinem strikten Schema, sondern ist durch die Existenz der Daten und der gültigen Beziehungen gegeben. So können alle Arten von Datentypen und Kombinationen eingefügt und miteinander verbunden werden. Im Gegensatz zu relationalen Datenbanken gibt es kaum Kontrollmechanismen auf der Datenbankebene, diese Aufgabe obliegt der Applikation bzw. der Entwicklung.

In diesem Tutorial werden schrittweise die verschiedenen Aspekte und Schlüsselwörter von Cypher behandelt und anhand von Beispielen veranschaulicht. Als Daten-Grundlage wird eine Film-Datenbank1 von Neo4j verwendet, welche im Laufe des Tutorials angepasst bzw. erweitert wird. Jeweils am Ende eines Abschnitts sind die Anpassungen als Abfrage aufgelistet, welche nötig sind für den weiteren Verlauf des Tutorials. Diese können eins zu eins so ausgeführt werden. Die Schlüsselwörter werden in diesem Tutorial immer grossgeschrieben z.B. MATCH. So kommt die Grammatik von Cypher besser zur Geltung und die Verständlichkeit wird erhöht. Platzhalter werden kursiv in Klammern geschrieben z. B. [Musterabgleich].

Anstelle den Schlüsselwörtern zu folgen, orientiert sich das Tutorial vom Einfachen zum Komplexen. So werden Schlüsselwörter mit jedem Abschnitt um wichtige Aspekte erweitert.

Inc., N. T. (7. Juli 2015). Cypher vs SQL. Von http://neo4j.com/docs/stable/cypherdoc- cypher-vs-sql.html abgerufen

Sprachstruktur

Syntax

Das Design von Cypher wurde durch andere Abfragesprachen wie SQL oder SPARQL beeinflusst. So ist eine Abfrage ähnlich strukturiert wie in SQL und nutzt zum Teil ähnliche Schlüsselworte, die ebenfalls grossgeschrieben sind. Die wichtigsten Schlüsselwörter sind

MATCH Musterabgleich
OPTIONAL MATCH Optional Musterabgleich
WHERE Selektion
RETURN Rückgabe als Knoten oder Ergebnistabelle
WITH Erstellen eines Alias für einen Knoten
CREATE Erstellen eines Knotens oder einer Kante
SET Eigenschaft eines Knotens oder einer Kante einen Wert zuweisen
DELETE Knoten entfernen
REMOVE Entfernen einer Eigenschaft, eines Labels oder eines Typs
ORDER BY Sortierung
SKIP Bestimmte Anzahl an Resultate am Anfang überspringen
LIMIT Resultat auf ein bestimmte Anzahl limitieren

Als Basis jeder Abfrage dient das MATCH – WHERE – RETURN Konstrukt. So werden gewünschte Knoten und Kanten durch den Musterabgleich ausgewählt (MATCH), je nach Wunsch gefiltert (WHERE) und die gewünschten Informationen zurückgegeben (RETURN). Dabei kann sowohl ein Knoten als auch eine Ergebnisstabelle mit einer Kombination von Konten und deren Eigenschaften zurückgegeben werden. Je nach Komplexität kann die WHERE Klausel auch in das MATCH integriert werden, was später noch detaillierter erläutert wird.

MATCH [Musterabgleich]
WHERE [Filter]
RETURN [Knoten oder Eigenschaften] 

Um einen neuen Knoten zu erstellen wird das Schlüsselwort CREATE verwendet, welches ergänzt wird mit den Eigenschaften, der Art und allfälliger Beziehungen zu anderen Knoten.

CREATE [Knoten- und Kantendefinition]
RETURN [Knoten oder Eigenschaften] 

Die zwei Arten können auch problemlos zu einer MATCH – CREATE – RETURN Abfrage verknüpft werden. So kann zum Beispiel ein Knoten abgefragt werden (MATCH), um diesen anschliessend mit einem neuen Knoten über eine neue Kante zu verbinden (CREATE) und zurückzugeben.

MATCH [Musterabgleich]
CREATE [Knoten- und Kantendefinition]
RETURN [Knoten oder Eigenschaften] 

Filterformulierung

Cypher bietet verschiedene Operatoren, um Filter (WHERE) zu formulieren. Diese unterscheiden sich nur wenig von anderen Sprachen wie Java oder SQL.

= Gleich
< Kleiner als
> Grösser als
<> Ungleich
<= Kleiner gleich
>= Grösser gleich
IS NULL Gleich NULL
IS NOT NULL Ungleich NULL

Um verschiedene Filter zu verknüpfen gibt es die vier booleschen Operatoren.

AND UND
OR ODER
XOR Exklusives ODER
NOT NICHT

Datentypen

Neo4j entscheidet durch die Definition der Daten, welcher Datentyp verwendet wird. Dabei werden die gängigen primitiven Datentypen verwendet (boolean, byte, short, int, long, float, double, char und String). So wird bei den folgenden Zuweisungen ein String oder int verwendet.

SET movie.title = «Ted»
SET movie.costOfProduction = 954000 

Knoten Operationen

Konten erstellen

Wie schon gezeigt werden mit Knoten und Kanten die entsprechenden Daten und Beziehungen abgebildet. Beide können durch das Schlüsselwort CREATE erstellt werden. Dank des Fehlens eines konkreten Schemas können Daten direkt hinzugefügt werden, ohne zuerst eine Struktur zu definierten.

Um neue Knoten zu erstellen, reicht im Minimum eine leere Variabel. So wird durch die folgende Abfrage ein leerer Knoten «n» ohne Label oder Eigenschaften erstellt (CREATE) und dieser zurückgegeben (RETURN).

CREATE (n)
RETURN (n) 

Damit Daten mithilfe eines Knoten abgebildet werden können, müssen diese als Eigenschaften eines Knotens in geschweiften Klammer definiert werden. Die Eigenschaften werden dabei, ähnlich wie bei JSON, als Key-Value Paare definiert. So kann ein neuer Knoten mit den Eigenschaften «title» und «date» wie folgt erstellt werden.

CREATE (n { title : 'Man of Tai Chi', released : 2013 }) 

Durch Hinzufügen eines Labels kann ein Knoten kategorisiert werden. Das entsprechende Label wird mit einem Doppelpunkt nach der Variabel angehängt. Ein spezifischer Movie-Knoten kann mit der folgenden Abfrage erstellt werden.

CREATE (n:Movie { title : 'Man of Tai Chi', released : 2013 }) 

Falls einem Knoten mehrere Labels zugeordnet werden soll, werden diese mit einem weiteren Doppelpunkt angefügt. Im folgenden Beispiel wird ein neuer Movie-Knoten erstellt, der zusätzlich noch als «Action» und «Drama» gekennzeichnet ist.

CREATE (n:Movie:Action:Drama { title : 'Man of Tai Chi', released : 2013 }) 

Zum Abschluss dieses Abschnittes sollen nun die folgenden Abfragen ausgeführt werden.

CREATE (n:Movie:Action:Drama { title : 'Man of Tai Chi', released : 2013})
CREATE (n:Movie:Romantic:Drama { title : 'The Lake House', released : 2006}) 

Musterabgleich und Selektion

Durch die Schlüsselwörter MATCH und WHERE können Musterabgleiche und Selektionen auf Knoten und deren Eigenschaften formuliert werden.

Um einen Überblick über alle Knoten einer Datenbank zu erhalten, kann die folgende Abfrage genutzt werden. Dies ist gleichzeitig auch der einfachste Musterabgleich.

MATCH (n) 
RETURN (n) 

Dank der Kategorisierung durch Labels sind Musterabgleiche auf Kategorie-Ebene möglich. Wie bei der Erstellung eines Knoten wird das entsprechende Label einfach mit einem Doppelpunkt der Variabel angehängt. So erhält man mit der folgenden Abfrage alle «Movie» Knoten.

MATCH (movie:Movie) 
RETURN (movie) 

Ebenfalls ist es möglich, anhand von mehreren Labels einen Musterabgleich durchzuführen.

MATCH (movie:Movie:Action) 
RETURN (movie) 

Um eine spezifische Selektion an Hand der Eigenschaften eines Knoten zu beschreiben, wird das Schlüsselwort WHERE verwendet. An anderer Stelle dieses Tutorials wird noch tiefer auf die Selektion und deren Möglichkeiten eingegangen. Mit der folgenden Abfrage können Knoten mit der Eigenschaft «title» und zugehörigem Wert «The Matrix» abgefragt werden.

MATCH (movie:Movie)
WHERE movie.title = 'The Matrix' 
RETURN (movie) 

Anstelle von WHERE kann die Selektion auch in einen Musterabgleich umgewandelt werden, welcher ein identisches Resultat zurückgibt.

MATCH (movie:Movie { title : 'The Matrix' }) 
RETURN (movie) 

Knoten bearbeiten

Um vorhandene Knoten zu bearbeiten, bietet Cypher mit SET ein Schlüsselwort. Damit können Labels hinzuzufügen oder Eigenschaften verändert werden. Mithilfe von REMOVE können Eigenschaften gelöscht oder Labels entfernt werden. Bevor jedoch die entsprechenden Änderungen definiert werden können, müssen zuerst mit einem Musterabgleich (MATCH) oder Filter (WHERE) die Knoten ausgewählt werden, welche für die Änderung berücksichtigt werden sollen.

Die folgende Abfrage fügt dem Movie- “Man of Tai Chi” das Label “Action” hinzugefügt.

MATCH (movie:Movie { title : 'Man of Tai Chi' }) 
SET movie:Action
RETURN (movie) 

Genau wie bei der Knotenerstellung oder dem Musterabgleich, können bei der Änderung mehrere Labels auf einmal einem Knoten hinzugefügt werden.

MATCH (movie:Movie { title : 'Man of Tai Chi' }) 
SET movie:Action:Drama
RETURN (movie) 

Durch die Verwendung des Schlüsselworts REMOVE wird ein Label entfernt.

MATCH (movie:Movie { title : 'Man of Tai Chi' }) 
REMOVE movie:Action
RETURN (movie) 

Auch damit können wieder mehrere Labels auf einmal entfernt werden.

MATCH (movie:Movie { title : 'Man of Tai Chi' })
REMOVE movie:Action:Drama 
RETURN (movie) 

Neben dem Hinzufügen von Labels können mit SET auch Eigenschaften geändert werden. Falls die angegebene Eigenschaft nicht vorhanden ist, wird sie dem Knoten automatisch hinzugefügt.

Um die Eigenschaft “tagline” zu beschreiben, dient die folgende Abfrage. Dabei spielt es keine Rolle, ob diese bereits existiert. Wenn ja wird sie aktualisiert und sonst als neue Eigenschaft dem Knoten hinzugefügt.

MATCH (movie:Movie { title : 'Man of Tai Chi' })
SET movie.tagline = 'No rules. No mercy. Pure fighting.' 
RETURN (movie) 

Um eine Eigenschaft zu löschen, gibt es zwei verschiedene Möglichkeiten. Zum einen setzt SET die entsprechende Eigenschaft auf NULL und wird damit gelöscht.

MATCH (movie:Movie { title : 'Man of Tai Chi' }) 
SET movie.tagline = NULL
RETURN (movie) 

Zum anderen wird mit REMOVE die Eigenschaft entfernt.

 MATCH (movie:Movie { title : 'Man of Tai Chi' }) 
REMOVE movie.tagline
RETURN (movie) 

Falls nicht schon geschehen, müssen nun noch die folgenden Abfragen ausgeführt werden.

CREATE (movie:Movie { title : 'Man of Tai Chi', released : 2013 })
MATCH (movie:Movie { title : 'Man of Tai Chi' })
SET movie.tagline = 'No rules. No mercy. Pure fighting.' 
RETURN (movie) 

Kanten Operationen

Nach der genaueren Betrachtung der Knoten, geht es. Wie bereits erläutert, werden diese in Neo4j genutzt, um eine Beziehung zweier Knoten abzubilden. Um diese genauer zu beschreiben, kann eine Kante (ähnlich wie bei Knoten) einem Typ zugewiesen werden. Ebenfalls können sie durch Hinzufügen von Eigenschaften detaillierter beschrieben werden. Auch dazu ist für die Erstellung des Schlüsselwortes CREATE nötig. Dazu muss zuerst der ausgehende Knoten angegeben werden, gefolgt von der Definition der Beziehung und abschliessend der eingehende Knoten. Eine Kante ist immer eine gerichtete Beziehung, daher ist die Reihenfolge der angegebenen Knoten von zentraler Bedeutung. So sind die folgenden zwei Zeilen nicht äquivalent zueinander.

CREATE (director) - [r:DIRECTED] -> (movie) 
CREATE (movie) - [r:IS_DIRECTED_BY] -> (director) 

Durch einen Richtungswechsel können jedoch zwei äquivalente Abfragen erstellt werden. In diesem Tutorial werden jedoch die Beziehungen immer von links nach rechts beschrieben.

CREATE (director) - [r:DIRECTED] -> (movie) 
CREATE (movie) 

Kante erstellen

Um eine Beziehung zweier Knoten zu erstellen, müssen diese zuerst mit einem Musterabgleich (MATCH) abgefragt werden. Durch das Filtern (WHERE) kann die Auswahl noch weiter eingeschränkt werden. Mittels der folgenden Abfrage werden zuerst die Person mit dem Namen “Keanu Reeves” und der Film mit dem Titel “Man of Tai Chi” abgefragt. Anschliessend wird von der Person zum Film eine Beziehung erstellt. Dabei soll die Person die Rolle des Filmregisseurs übernehmen. Daher wird wie bei der Definition von Labels der Beziehungs-Typ durch einen Doppelpunkt getrennt angegeben, in diesem Fall “DIRECTED”. Zum Schluss der Abfrage wird die Variabel «rel» zurückgegeben, welche die erstellte Beziehung beinhaltet.

MATCH (director:Person), (movie:Movie)
WHERE director.name = 'Keanu Reeves' AND movie.title = 'Man of Tai Chi'
CREATE (director) - [rel:DIRECTED] -> (movie)
RETURN (rel) 

Durch das Hinzufügen von Eigenschaften kann eine Beziehung erweitert werden. So kann bei diesem Beispiel der “DIRECTED” Beziehung eine Eigenschaft “salary” hinzugefügt werden, welche den Lohn des Regisseurs enthält. Diese Abfrage muss zwingend für den weiteren Verlauf das Tutorial ausgeführt werden.

MATCH (director:Person), (movie:Movie)
WHERE director.name = 'Keanu Reeves' AND movie.title = 'Man of Tai Chi'
CREATE (director) - [rel:DIRECTED { salary: 100000 }] -> (movie) 
RETURN (rel) 

Für weitere Beispiele in diesem Tutorial ist die folgende Abfrage nötig. Sie kann eins zu eins so übernommen werden und fügt allen Schauspielern, Regisseuren und Produzenten ein zufälliges Salär von bis zu 100’000.- hinzu. Die genau Funktionsweise wird dann zu einem späteren Zeitpunkt erläutert.

MATCH p = (person:Person) --> (movie:Movie) 
FOREACH (
    rel IN RELATIONSHIPS(p) | SET rel.salary = round( rand() * 100000 )
) 

Musterabgleich und Selektion

Bekannte Konstrukte von Knoten, können nun auch mit Kanten verwendet werden, um Musterabgleiche zu formulieren. Die folgende Abfrage ist dabei die Einfachste. Sie fragt alle Knoten ab, die eine Verbindung, egal welcher Typ, zu einem anderen Knoten hat. Alle Resultate werden dann den beiden Variabeln “n” und “r” zugewiesen und zurückgegeben. Achtung: bei der Rückgabe werden immer die Knoten mit all ihren Beziehungen zueinander zurückgeben.

MATCH (n) --> (r)
RETURN (r), (n) 

Durch eine Selektion oder eine Filterung können erweitertete Musterabgleiche auf Knotenebene beschrieben werden. Zum Beispiel gibt die folgenden Abfrage alle Knoten mit dem Label “Person”, die mit einer Kante zum Knoten mit dem Title “The Matrix” verbunden sind zurück.

MATCH (person:Person) --> (movie:Movie) 
WHERE movie.title = "The Matrix" 
RETURN (person), (movie) 

Wenn nun alle Schauspieler des Films «The Matrix» interessieren, können diese mit dem Kanten-Typ «ACTED_IN» im Musterabgleich (MATCH) abgefragt werden.

MATCH (person:Person) - [rel:ACTED_IN] -> (movie:Movie) 
WHERE movie.title = "The Matrix"
RETURN (person), (movie) 

Gegen Ende des letzten Abschnittes wurde allen Beteiligten eines Filmes ein Salär zugeordnet. Natürlich können auch Abfragen mithilfe von Kanten-Eigenschaften implementiert werden. So gibt die folgende Abfrage alle «Person» Knoten zurück, welche als Regisseur einem Film zugeordnet sind und dabei mehr als 50’000.- verdienen.

MATCH (person:Person) - [rel:DIRECTED] -> (movie:Movie) 
WHERE rel.salary > 50000
RETURN (person) 

Kante ändern

Nebst dem Erstellen einer Kanten-Eigenschaft, kann eine solche zu einem späteren Zeitpunkt geändert oder eine Neue erstellt werden. Wie bei Knoten kommt hier das Schlüsselwort SET zum Einsatz. Dabei spielt es auch hier keine Rolle, ob die Eigenschaft existiert oder nicht. Sie wird überschrieben oder hinzugefügt. So kann mit der folgenden Abfrage den Regisseuren von «The Matix» jeweils ein Salär von 1 Mio. zugeordnet werden.

MATCH (person:Person) - [rel:DIRECTED] -> (movie:Movie)
WHERE movie.title = "The Matrix" 
SET rel.salary = 1000000 
RETURN (rel) 

Natürlich kann eine Eigenschaft auch entfernt werden. So wird mit der folgenden Abfrage den Regisseuren von «The Matrix» das Salär gestrichen. Ob nun «REMOVE rel.salary» oder «SET rel.salary = NULL» verwendet wird, spielt keine Rolle.

MATCH (person:Person) - [rel:DIRECTED] -> (movie:Movie)
WHERE movie.title = "The Matrix"
REMOVE rel.salary
RETURN (person), (movie)

oder

MATCH (person:Person) - [rel:DIRECTED] -> (movie:Movie)
WHERE movie.title = "The Matrix"
SET rel.salary = NULL
RETURN (person), (movie) 

Den Typ einer Kante direkt zu ändern, ist in Cypher nicht möglich. Dazu muss eine neue Kante erstellt, die Eigenschaften übernommen und die alte Kante gelöscht werden.

MATCH (person:Person {name: "Joel Silver" }) - [rel:PRODUCED] -> (movie:Movie {title: "The Matrix" })
CREATE (person) - [rel2:DIRECTED] -> (movie) 
SET rel2 = rel
WITH rel 
DELETE rel 

Kante löschen

Kanten können genau gleich wie Knoten gelöscht werden durch DELETE entfernt werden. Um «Keanu Reeves» als Regisseur von «Man of Tai Chi» zu entfernen ist folgende Abfrage nötig. Für den weiteren Verlauf des Tutorials ist es wichtig, diese Beziehung nach der Entfernung wieder zu erstellen.

MATCH (director:Person) - [rel: DIRECTED] -> (movie:Movie)
WHERE director.name = 'Keanu Reeves' AND movie.title = 'Man of Tai Chi' 
DELETE rel
RETURN (director), (movie) 

Selektion

Wie schon einige Male erwähnt, werden in Cypher Selektionen durch das Schlüsselworts WHERE formuliert. Einige Male wurden dafür Standard-Operatoren wie «=» oder «<» verwendet. Es gibt jedoch andere erweiterte Möglichkeiten, um Selektionen anhand des Inhalts oder der Struktur zu beschreiben.

Sehr umfangreiche Möglichkeiten bieten dabei Selektionen mit Hilfe von regex (Regular Expression). Dadurch können Selektionen auf den Inhalt von String Eigenschaften ausgeführt werden. Dazu wird dem «=» Symbol eine Tilde («~») hinzugefügt («=~»). Darauf folgt die Beschreibung des regex. Dieses Konstrukt ist vergleichbar mit dem Schlüsselwort LIKE in SQL.

So werden mit der folgenden Abfrage alle «Movie» Knoten, die in der Eigenschaft «title» eine «o» enthalten selektiert. Die Zeichenkette «.*» entspricht dabei dem «*» Symbol. Übersetzt bedeutet diese Abfrage, alle Movie-Knoten selektieren, die ein «o» enthalten, welches auf irgendein Zeichen folgt und von irgendeinem Zeichen gefolgt wird.

MATCH (movie:Movie) 
WHERE movie.title =~ ".*o.*" 
RETURN (movie) 

Ein weiteres Beispiel für eine regex Selektion ist die Ignorierung von Gross- und Kleinschreibung, durch die Verwendung von «(?i)» So erhält man durch die folgende Abfrage alle «Movie» Knoten, die mit «the» beginnen, unabhängig der Gross-/Kleinschreibung von «the».

MATCH (movie:Movie)
WHERE movie.title =~ "(?i)THE.*" 
RETURN (movie) 

Durch die «length()» Funktion ist möglich, die Länge von Eigenschaft zu vergleichen. Diese kann unter anderem mit einer regex kombiniert werden. Als Beispiel dazu eine Abfrage, welche alle Knoten mit maximal 10 Zeichen und dem Anfangsbuchstaben «T» zurückgibt.

MATCH (movie:Movie)
WHERE length(movie.title) <= 10 AND movie.title =~ "T.*" 
RETURN (movie) 

Nebst der Selektion anhand des Inhalts, ist auch eine Selektion durch einen Strukturabgleich möglich. So können mit der Funktion «HAS()», Knoten auf die Existenz einer bestimmten Eigenschaft überprüft werden. Als Beispiel können mit folgender Abfrage alle Movie- Knoten mit der Eigenschaft «title» abgefragt werden.

MATCH (movie:Movie) 
WHERE HAS (movie.title) 
RETURN (movie) 

Das gleiche Resultat erhält man mit der Verwendung von «IS NOT NULL»

MATCH (movie:Movie)
WHERE movie.title IS NOT NULL 
RETURN (movie) 

Wenn der Typ einer Beziehung interessiert, kann mit der Funktion «type(rel)», der Typ einer Kante verglichen werden. So gibt die nächste Abfrage alle Beziehungen des Typs «ACTED_IN» zurück. Dies entspricht allen Personen, die als Schauspieler in einem Film mitgewirkt haben, zusammen mit dem entsprechenden Film.

MATCH () -[rel]-> ()
WHERE type(rel) = "ACTED_IN" 
RETURN (rel) 

Falls inverse Abfragen beschrieben werden sollen, können diese durch das Schlüsselwort NOT formuliert werden. So werden mit nachfolgender Abfrage alle Movie-Knoten zurückgegeben, die keine Verbindung zu «Keanu Reeves» aufweisen.

MATCH (person:Person { name: 'Keanu Reeves' }),(movie:Movie) 
WHERE NOT (person) --> (movie)
RETURN (movie) 

Eine weitere Möglichkeit ist der Vergleich von Eigenschaften mit einer Auswahl z. B. einem Array durch das Schlüsselwort «IN». So erhält man mit der folgenden Abfrage alle Movie- Knoten bei denen die Eigenschaft «title» entweder «The Matrix», «The Matrix Reloaded» oder «The Matrix Revolution» ist.

MATCH (movie:Movie)
WHERE movie.title IN ['The Matrix','The Matrix Reloaded','The Matrix Revolution'] 
RETURN (movie) 

Projektionen

Die Struktur eines Resultats kann durch Projektionen den Bedürfnissen angepasst dargestellt werden. Dies geschieht dabei nach dem Schlüsselwort RETURN. Das Resultat ist dabei zwingend eine Menge an Knoten und evtl. Kanten oder eine Tabelle.

Ein Beispiel für eine sehr simple Projektion ist die folgende Abfrage. Dabei werden alle Beziehungen des Typs «ACTED_IN» zwischen einer Person und einem Film zurückgegeben.

MATCH (person:Person) - [rel:ACTED_IN] -> (movie:Movie) 
RETURN (rel) 

Es fällt auf, dass auch eine Kante des Typs «PRODUCED» in der Resultat-Menge existiert. Dies weil ebenfalls eine «ACTED_IN» Kante existiert zwischen den beiden Knoten.

Die nächste Abfrage zeigt eine Projektion in eine Tabelle. Dabei werden alle Filme zusammen mit ihren Schauspielern und deren Salär in eine Tabelle ausgegeben. Die ganze Tabelle wird anschliessend nach dem jeweiligen Salär sortiert. Dies geschieht unter der Verwendung des Schlüsselwortes ORDER BY.

MATCH (person:Person) - [rel:ACTED_IN] -> (movie:Movie) 
RETURN movie.title, person.name, rel.salary
ORDER BY rel.salary 
movie.title person.name rel.salary
The Devil’s Advocate Keanu Reeves 121
The Matrix Laurence Fishburne 14972
The Matrix Keanu Reeves 17256
The Devil’s Advocate Charlize Theron 20077
The Matrix Hugo Weaving 32882
The Devil’s Advocate Al Pacino 70494
Monster Charlize Theron 72418
The Matrix Carrie-Anne Moss 97770

Aggregationsfunktionen

Ein wichtiges Mittel für die Darstellung von Resultaten sind Aggregationsfunktionen. Diese wurden schon einige Male benutzt in diesem Tutorial und werden nun vertieft behandelt. Besonders für statistische Abfragen sind sie sehr nützlich.

Als Beispiel können alle existierenden Typen von Kanten und deren Anzahl mit den Funktionen «type()» und «count()» abgefragt werden.

MATCH () - [rel] -> ()
RETURN type(rel), count(rel) 

Dazu kommen weitere statistische Funktionen. Wie die Maximalfunktion «max()», mit der zum Beispiel das grösste Salär abfragen kann.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN max(rel.salary) 

Oder die Minimalfunktion «min()», welche das kleinste Salär zurückgibt.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN min(rel.salary) 

Mit der Summenfunktion «sum()» kann die Höhe aller Saläre zusammen gerechnet abgefragt werden.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN sum(rel.salary) 

Wenn das durchschnittliche Salär gesucht wird, hilft die Durchschnittsfunktion «avg()» weiter.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN avg(rel.salary) 

Um ein genaueres Bild über die Verteilung der Werte zu erhalten, kann die Standardabweichung mittels «stdev()» berechnet werden.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN stdev(rel.salary) 

Eine Sammlung aller Werten kann durch die Funktion «collect()» abgefragt werden. Als Resultat wird anschliessend ein Array zurückgegeben.

MATCH (person:Person) - [rel] -> (movie:Movie) 
RETURN collect(rel.salary) 

Möchte man die Anzahl der verschiedenen Schaupspieler abfragen, wird das Resultat verfälscht, weil einige Schaupieler in mehreren Filmen mitgewirkt haben. Durch das Schlüsselwort DISTINCT werden Duplikate gelöscht und ein korrektes Resultat zurückgeben.

MATCH (person:Person) --> (movie:Movie)
RETURN count(DISTINCT person.name) 

Index

Wie in SQL bietet Cypher mit dem Index-Konstrukt eine Möglichkeit, um die Ausführung von Abfragen zu unterstützen und zu beschleunigen. Dabei wird ein Index immer aufgrund der Eigenschaft eines Labels erstellt. Genutzt wird dabei ein CREATE – INDEX ON Konstrukt. Nach der Ausführung wird im Hintergrund der Index erstellt. Mit dem Befehl «:schema» kann die Erstellung überprüft werden.

Um einen Index auf die Eigenschaft «name» des Labels «Person» zu erstellen, ist folgende Abfrage nötig.

CREATE INDEX ON :Person(name) 

Für die Nutzung des Index ist keine Anpassung nötig. Neo4j nutzt einen Index, falls vorhanden, und sonst wird die Abfrage normal ausgeführt. Die folgende Abfrage ist ein Beispiel dafür.

MATCH (person:Person {name:"Al Pacino"}) 
RETURN (person) 

Damit der Unterschied ersichtlich wird, muss die Ausführungsabfolge detaillierter beachtet werden. Dazu bietet sich das Schlüsselwort «EXPLAIN» an. Welches die Abfolge und Ausführungsdauer visualisiert.

EXPLAIN
MATCH (person:Person {name:"Al Pacino"}) 
RETURN (person) 

Es fällt auf, dass mit Index die Abfrage schneller und effizienter durchgeführt worden ist. Der Knoten wird dabei direkt über den Index gefunden.

Im Gegensatz dazu, die Abfolge der gleichen Abfrage, jedoch vor der Erstellung des Index. Man sieht, dass zuerst alle Knoten des «Person» Labels abgefragt werden und erst dann anhand der Eigenschaft gefiltert wird, um den gewünschten Knoten zu erhalten. Dieser Vorgang ist aufwändiger und dauert länger als einem Index.

Durch die Visualisierung einer Abfrage sieht man sehr gut, welchen Vorteil die Kategorisierung von Knoten durch Labels mit sich bringt. Falls keine Labels angegeben werden, berücksichtigt Neo4j bei der Ausführung jeden Knoten in der Datenbank. Durch Labels beschränkt sich Neo4j auf die Knoten dieses Labels.

Constraint

Constraint ist die eine Möglichkeit, um eine gewisse Struktur in die Neo4j Datenbank zu bringen. So können beispielsweise Eigenschaften definiert werden, welche in der ganzen Datenbank in Kombination mit einem bestimmten Label einmalig sein müssen. Dabei kann es sich um eine Personen-Nummer handeln. Dies hat jedoch keine rückwirkenden Folgen, und es ist auch nicht zwingend, dass alle Knoten dieses Labels die entsprechende Eigenschaft besitzen. Es wird einzig die Erstellung abgebrochen, wenn Knoten existieren mit entsprechenden identischen Eigenschaften.

Um nun einen Constraint auf die Eigenschaft «persNr» zu erstellen ist folgender Befehl nötig.

CREATE CONSTRAINT ON (person:Person) ASSERT person.persNr IS UNIQUE 

Mit folgender Abfrage kann einer Person eine Nummer zugewiesen werden. Dabei ist ebenfalls keine Anpassung nötig, denn die ganze Überprüfung findet im Hintergrund statt.

MATCH (person:Person)
WHERE person.name = 'Al Pacino' 
SET person.persNr = 10023 

Falls eine Person die gleiche Nummer schon besitzt, wird die folgende Fehlermeldung erscheinen.

Node 373 already exists with label Person and property “persNr” = [10023]

Zusätzlich zum Constraint wird automatisch auch ein Index erstellt, damit neue Knoten schnell und effizient auf Duplikate geprüft werden können.

Erweiterte Konstrukte

Optimal Match

Durch die Verwendung des Schlüsselwortes OPTIONAL MATCH, können zusätzliche Musterabgleiche definiert werden. Diese werden jedoch erst in einem zweiten Schritt berücksichtigt. Falls diese nicht zutreffen, werden nur die Musterabgleiche aus dem MATCH Konstrukt verwendet.

Als Beispiel wird mit folgender Abfrage eine leere Menge zurückgegen. Dies weil der Knoten «Al Pacino» keine verbindende Kante zum Film «The Matrix» besitzt.

MATCH (person:Person { name: "Al Pacino"}) - [rel] -> (movie:Movie { title:"The Matrix"})
RETURN (person), (rel) 

Wenn auf jedem Fall eine bestimmte Rückgabe gewünscht wird, kann dies durch OPTIONAL MATCH erreicht werden. So erhält man mit folgender Abfrage den Knoten «Al Pacion» (Musterabgleich im MATCH) jedoch keine Kanten. Denn der Musterabgleich im OPTIONAL MATCH Teil trifft nicht zu.

MATCH (person:Person { name: "Al Pacino"})
OPTIONAL MATCH (person) - [rel] -> (movie:Movie { title:"The Matrix"}) 
RETURN (person), (rel) 

Union

Mithilfe von UNION können Abfragen kombiniert werden. Dabei ist zu beachten, dass beide Abfragen ihre Resultate auf eine identische Ergebnistabelle projizieren.

So werden mit der folgenden Abfrage zuerst alle Personen abgefragt, die in Verbindung mit dem Film «Man of Tai Chi» stehen und dann jene mit dem Film «The Matrix». Die Namen werden dann auf die Spalte «ActorName» projektiert. Mit der Verwendung von «UNION» anstelle von «UNION ALL» können Duplikate eliminiert werden.

MATCH (person:Person) --> (movie:Movie {title:"Man of Tai Chi"}) 
RETURN (person.name AS ActorName)
UNION ALL
MATCH (person:Person) --> (movie:Movie {title:"The Matrix"}) 
RETURN (person.name AS ActorName) 

Alias

Um ein Resultat einem Alias zuzuweisen und später weiterzuverwenden, wird das WITH – AS Konstrukt verwendet. So wird eine Menge (Knoten oder Beziehungen) durch das AS einem Alias zugewiesen.

Als Beispiel werden alle Filme abgefragt, welche Keanu Reeves als Regisseur zugeordnet haben und anschliessend dem Alias «directedMovie» zugewiesen.

MATCH (person:Person {name:"Keanu Reeves"}) - [rel:DIRECTED] -> (movie:Movie)
WITH movie AS directedMovie 
RETURN (directedMovie) 

Eine andere Möglichkeit ist, das Resultat einer Funktion zu speichern. So werden zum Beispiel die Länge der Titel aller Film gespeichert und anschliessend ihre Summer zurückgegeben.

MATCH (movie:Movie)
WITH length(movie.title) AS titleLength 
RETURN sum(titleLength) 

Im nächsten Abschnitt wird unter anderem gezeigt, wie im Schlüsselwort MATCH die Zuweisung zu einer Variabel funktioniert und so später weiterverwendet werden kann.

Foreach

Um eine Abfrage auf jedes Element einer Menge durchzuführen, existiert das Schlüsselwort FOREACH. In diesem Tutorial wurde es bereits verwendet jedoch nicht weiter ausgeführt. Diese Abfrage wird nun erneut genutzt um die Funktionsweise von FOREACH aufzuzeigen. Ziel dieser Abfrage ist es, jeder «Person-Movie»-Beziehung ein zufälliges Salär zuzuweisen. Dazu wird zuerst einen Musterabgleich ausgeführt und das Resultat der Variabel «p» zugewiesen. Danach werden alle Kanten aus dem Resultat mithilfe der Funktion «RELATIONSHIPS» extrahiert und anschliessend jeder Kante das Salär zufällig gesetzt.

MATCH p = (person:Person) --> (movie:Movie) 
FOREACH (
   rel IN RELATIONSHIPS(p) | SET rel.salary = round( rand() * 100000 )
) 

Nebst der Funktion «RELATIONSHIPS()» gibt es noch die Funktion «NODES()», um alle Knoten aus einer Ergebnis-Menge zu extrahieren.

Merge

Mittels MERGE werden entweder Knoten erstellt oder Knoten aktualisiert, die einem Muster entsprechen. So kann zum Beispiel mit der folgenden Abfrage, dem Schauspieler «Al Pacino» einen Zeitstempel Eigenschaft «lastRead» hinzugefügt werden.

MERGE (person:Person { name:"Al Pacino"}) 
SET person.lastRead = timestamp()
RETURN (person) 

Mit ON MATCH und ON CREATE kann unterschiedlich reagiert werden, je nachdem ob bereits Knoten existieren, welche dem Musterabgleich entsprechen,. So wird zum Beispiel mit ON CREATE eine Zeitstempel-Eigenschaft «dateCreated» erstellt und mit ON MATCH die Zeitstempel Eigenschaft «lastRead» erstellt/aktualisiert werden.

MERGE (person:Person { name:"Al Pacino"})
ON CREATE SET person.dateCreated = timestamp() 
ON MATCH SET person.lastRead = timestamp()
RETURN (person) 

Pfadlänge

Als Erstes muss noch ein neuer «Cinema» Knoten erstellt werden und dieser mit dem Film «Monster» verknüpft werden, dazu die folgenden zwei Abfragen.

CREATE (cinema:Cinema {name: "Capitol" })

MATCH (cinema:Cinema {name: "Capitol" }), (movie:Movie {title:"Monster" }) 
CREATE (movie) - [:WAS_SHOWN {year: 2003}] -> (cinema) 

Mit Hilfe von Pfadlängen kann nun eine Anzahl Beziehungen zwischen zwei Knoten angegeben werden. Die entsprechende Länge wird der Beziehung nach einem «*» mitgegeben. Als Beispiel alle Beziehungen die eine Länge von zwei haben.

MATCH () - [rel*2] -> () 
RETURN (rel) 

Als einzige Beziehung entspricht (Capitol) -> (Monster) -> (Charlize Theron) diesem Muster und wird zurückgegeben.

Die Pfadlänge kann auch als Bereich definiert werden. So wird die Zahl (z.B. 2) durch die zwei Zahlen, welche den Bereich begrenzen, getrennt von zwei Punkten angegeben. Zum Beispiel werden mit folgender Abfrage alle Beziehung mit einer Länge zwischen eins und drei zurückgegeben.

MATCH () - [rel*1..3] -> () 
RETURN (rel) 
Designed & Developed by Minh Tue NGUYEN