Die Abfragesprache für die Neo4J Graphendatenbank ist Cypher, die sich irgendwie eine Mischung aus SQL und JSON darstellt. Das Schöne an der Sprache ist, wie einfach sie zu lernen ist, weil die Struktur sehr klar ist und einfach alles so funktioniert wie man es sich denkt. Komplexe Konstrukte wie GROUP BY aus SQL gibt es nicht und die Aggregatsfunktionen fügen sich sehr viel angenehmer in alles
ein als bei SQL.
Die Grundlegende Struktur einer Abfrage ist auch sehr logisch:
*
MATCH
*
WHERE
*
RETURN
*
ORDER BY
*
LIMIT
Also ich sage welches Graphen-Gebilde ich such. Dann wird festgelegt welche Eigenschaften es erfüllen soll. Das geschieht anhand der Relations und Attribute der Komponenten, die man vorher fest gelegt hat (einfache statische vorgaben wie Ids kann man sogar schon vorher fest legen wie
z.B. " (e:example{id:1})").
Nun definiert man wie das ResultSet aussehen soll. Nodes, Relations, Attribute, Ergebnisse von Aggregatsfunktionen.. kann man beliebig mischen und auch eine CASE-Anweisung ist vorhanden. Dann Legt noch die Sortierung fest und möglicher Weise eine Limitierung des Resulsets.
Besonders schön sind Funktionen wie
collect(), die einem Möglichkeiten bieten, die SQL einen einfach nicht nicht bieten kann.
Gehen wir mal von einer Datenbank mit Benutzern und Gruppen aus.
SQL:
SELECT g.id group_id,
u.id user_id,
u.name user_name
FROM groups g,
users u,
users_groups ug
WHERE ug.group_id = g.id
AND u.id = ug.user_id
ORDER BY g.id
Danach müssen wir durch eine Schleife laufen und immer wenn die Group-Id
sich ändert eine neue Liste für die User aufmachen und die solange füllen
bis die nächste Liste erzeugt wird. Die listen kommen dann in eine Map
wo die Group der Key für ihre Liste ist.
Jeder hat so etwas bestimmt schon mal gemacht, um keine einzelnen Queries für jede Group absetzen zu müssen. Ein großes Query ist schneller als viele kleine, weil weniger Overhead für die Connection-Verwaltung gebraucht wird und auch weniger Objekte erzeugt werden, was immer gut für die Performance
ist.
mit Neo4j geht es sehr viel einfacher.
Cypher:
MATCH (g.group)<-[m:member]-(u:user)
RETURN
g.id as group_id,
collect(u)
ORDER BY g.id
Hiermit erhält man eine Liste aller Groups mit 2 Werten. Der erste Wert ist die Id und der zweite ist die Liste der zur Group gehörigen User. Damit ist keine zusätzliche Iteration über die Ergebnismenge mehr nötig, um solch ein Konstrukt zu erzeugen.
Wenn man nun noch die Anzahl der User pro Group direkt haben möchte
muss man das Query nur minimal anpassen. Kein GROUP BY oder ähnliches.
Cypher:
MATCH (g.group)<-[m:member]-(u:user)
RETURN
g.id as group_id,
count(u) as cnt,
collect(u)
ORDER BY g.id
Komplizierter wird es mit Befehlen wie UNWIND oder FOREACH, aber im Vergleich zu entsprechenden SQL Lösungen sind diese auch noch sehr einfach und unkompliziert.
Ein Blick über den SQL-Tellerrand lohnt auf jeden Fall, wenn man Aufwand und Zeit sparen möchte bei Abfragen, die oft geschachtelte Listen nutzen und man es Leid ist, diese aus den Resultsets wieder zu rekonstruieren.