Nicola Zunino

Lesezeit: 9 Minuten

Techblog

Graphen, Klassen und SQL in OrientDB (Teil 3)

OrientDB ist eine multi-model Datenbank von Orient Technologies. Ich stelle es in einer virtieligen Artikel-Serie vor. In diesem Artikel dritten Teil des Deep Dives geht es um Graphen Klassen, und SQL bei der Not-only-SQL-DB. Knoten und Kanten In OrientDB gibt es zwei Klassen, die den Objekten einer Graphen-Datenbank entsprechen: V für Knoten und E für…

Techblog

OrientDB ist eine multi-model Datenbank von Orient Technologies. Ich stelle es in einer virtieligen Artikel-Serie vor. In diesem Artikel dritten Teil des Deep Dives geht es um Graphen Klassen, und SQL bei der Not-only-SQL-DB.

  • Im ersten Teil dieser Blog-Serie gebe ich eine kurze Einführung in Datenbank-Management-Systeme (DBMS) und zeige Installation und erste Schritte mit OrientDB. Anschließend springe ich ins Arbeiten am konkreten Beispiel und zeige gängige SQL-Abfragen in OrientDB.
  • Im zweiten Teil der Serie geht es weiter mit Insert, SELECT, UPDATE DELETE und Relationen (nein, kein JOIN ;-)).
  • Teil vier dreht sich um Transaktionen, Nutzer und Rollen, verteilte Architekturen und APIs.

Knoten und Kanten

In OrientDB gibt es zwei Klassen, die den Objekten einer Graphen-Datenbank entsprechen: V für Knoten und E für Kanten. Um Knoten und Kanten manipulieren zu können, stellen SQL und DBMS vier Befehle zur Verfügung:

  • CREATE VERTEX generiert einen Knoten, der auf eine bestimmte Klasse basiert;
  • DELETE VERTEX entfernt Knoten;
  • CREATE EDGE verbindet zwei Knoten;
  • DELETE EDGE trennt die Verbindung zwischen zwei Knoten.

Um diese Befehle ausführen zu können, müssen alle Klassen entweder V oder E erweitern. Es wird durch Vererbung realisiert, implementiert in OrientDB im Object-orientierten Modell. Es ist sehr ähnlich zu Java:

CREATE CLASS B extends A
 

In diesem Fall erbt die Klasse B von der Klasse A.

Knoten und Kanten am Beispiel Transport-Netz

Ich zeige das am Beispiel des Streckenplans einer Busgesellschaft. Die Knoten sind die Städte, die Kanten sind die Verbindungen zwischen den Städten.

Als Erstes wird die Klasse City definiert, die ein Knoten sein muss:

CREATE CLASS City extends V
 

Es ist nicht nötig, die ganze Klasse zu definieren, es reicht einige Städte hinzuzufügen:

CREATE VERTEX City SET name='Berlin'
CREATE VERTEX City SET name='München'
CREATE VERTEX City SET name='Frankfurt'
CREATE VERTEX City SET name='Stuttgart'
CREATE VERTEX City SET name='Bonn'
CREATE VERTEX City SET name='Dortmund'
 

Die so entstehenden Knoten sind mögliche Ziele. Es sind Knoten, aber auch ganz normale Zeilen einer Tabelle und man kann sie einfach so abfragen:

SELECT FROM City
 

Output:

#   |@RID  |@CLASS|name
----+------+------+-------
0   |#18:11|City  |Berlin
1   |#18:12|City  |München
2   |#18:13|City  |Frankfurt
3   |#18:14|City  |Stuttgart
4   |#18:15|City  |Bonn
5   |#18:16|City  |Dortmund
----+------+------+-------

So entstehen zum Beispiel diese Strecken:

  • von Frankfurt nach München
  • von München nach Berlin
  • von Berlin nach Stuttgart
  • von München nach Bonn
  • von Bonn nach Dortmund
  • von Dortmund nach Berlin

Für die einzelne Strecke ist es ähnlich. Es wird eine Klasse benötigt, die von E erbt:

CREATE CLASS Track extends E
 

So sind auch die Strecken gespeichert:

// von Frankfurt nach München
CREATE EDGE Track FROM #18:13 TO #18:12
// von München nach Berlin
CREATE EDGE Track FROM #18:12 TO #18:11
// von Berlin nach Stuttgart
CREATE EDGE Track FROM #18:11 TO #18:14
// von München nach Bonn
CREATE EDGE Track FROM #18:12 TO #18:15
// von Bonn nach Dortmund
CREATE EDGE Track FROM #18:15 TO #18:16
// von Dortmund nach Berlin
CREATE EDGE Track FROM #18:16 TO #18:11
 

Wie CREATE EDGE funktioniert, ist leicht zu verstehen: die Keywords FROM und TO stehen für Start und Ende der Verbindung (durch RID eingegeben). Falls man nicht direkt die RIDs kennt oder benutzen will, ist es auch leicht eine Query zu erstellen:

// von München nach Berlin
CREATE EDGE Track FROM (SELECT FROM City WHERE name='München') TO 
(SELECT FROM City WHERE name='Berlin')
 

Eine Query auf Klasse City gibt folgenden Output raus:

#   |@RID |@CLASS |name      |in_track |out_track
----+------+------+----------+---------+----------
0   |#18:11|City  |Berlin    |[size=2] |[size=1]
1   |#18:12|City  |München   |[size=1] |[size=2]
2   |#18:13|City  |Frankfurt |null     |[size=1]
3   |#18:14|City  |Stuttgart |[size=1] |null
4   |#18:15|City  |Bonn      |[size=1] |[size=1]
5   |#18:16|City  |Dortmund  |[size=1] |[size=1]
----+------+------+----------+---------+----------

Jetzt haben die Records zwei zusätzliche Felder: in_track sind die Kanten, die in den Knoten ankommen, out_track die, die vom Knoten rausgehen.

Die Functions in() und out() zeigen die Kanten, die im Knoten eingehen bzw. ausgehen; both() zeigt beide. Falls man wissen möchte, wie viele Strecken nach Berlin führen:

SELECT IN() FROM City WHERE name='Berlin'
 

Output:

#   |@CLASS|in
----+------+----
0   |null  |[2]
----+------+----

Um die Details der Kanten zu sehen, muss man die Funktion expand() benutzen:

SELECT expand(IN()) FROM City WHERE name='Berlin'
 

Output sind die Strecken, die nach Berlin bringen und von München und Dortmund starten:

#   |@RID  |@CLASS|name     |out_Track |in_Track
----+------+------+---------+----------+---------
0   |#18:12|City  |München  |[size=2]  |[size=1]
1   |#18:16|City  |Dortmund |[size=1]  |[size=1]
----+------+------+---------+----------+---------

Knoten und Kanten entfernen

Um Knoten und Kanten zu entfernen, gibt es zwei bestimmte Befehle: DELETE VERTEX und DELETE EDGE. Man kann es durch RIDs machen:

DELETE VERTEX #11:5
 

Oder mit der WHERE Klausel, aber dabei muss man auch die richtige Klasse mitgeben:

DELETE VERTEX City WHERE name='Bonn'
 

Bei den Kanten ist es ähnlich.

Indexes

Auch OrientDB verfügt über Indexes, um die Lese-Befehle zu optimieren. Es gibt drei verschiedene Index-Typen in OrientDB:

  • SB-Tree leitet sich von binären Bäumen ab und ist in den meisten Fällen die beste Lösung;
  • Hash ist eine HashMap, eine Datenstruktur aus Key/Value. Die Write-Befehle werden dadurch ein bisschen langsamer, dafür sind die Read-Befehle viel schneller. Es muss sich aber um pünktliche Lese-Befehle handeln, keine range query, die Queries aus einer Werte-Range extrahieren;
  • Lucene wird benutzt für Volltext und raumbezogene Daten.

Ein Index wird also bestimmt, wenn klar ist, welche Suchen erforderlich sind. Ein Index-Typ kann nicht geändert werden.

Index hinzufügen und löschen

Ein Index wird mit dem CREATE INDEX Befehl erzeugt. Zwei Parameter sind obligatorisch: Name und Typ. Falls der Name Klasse.Feld sein sollte, weiß die Datenbank automatisch, was indexiert werden muss.

Es gibt auch Index-Optionen:

  • für SB-Tree: UNIQUE, NOTUNIQUE, FULLTEXT und DICTIONARY funktionieren alle mit Range Queries;
  • bei Hash: UNIQUE_HASH_INDEX, NOTUNIQUE_HASH_INDEX, FULLTEXT_HASH_INDEX und DICTIONARY_HASH_INDEX.

Falls der Name nicht Klasse.Feld sein sollte, wird die Klausel ON benutzt:

CREATE INDEX Customer.surname UNIQUE
CREATE INDEX myIndex ON Customer(surname) UNIQUE
 

Ein Index kann mehrere Felder gleichzeitig indizieren:

CREATE INDEX nameOfIndex ON Student (name, surname, NUMBER) UNIQUE
 

Um einen Index zu entfernen, wird DROP INDEX verwendet:

DROP INDEX nameOfIndex
 

Dies ruft die komplette Liste aller Indexes einer Datenbank auf:

LIST INDEXES
 

Diese kurze Fassung funktioniert ebenfalls:

INDEXES

Indexes benutzen und manipulieren

Ein Index ist hauptsächlich eine Datenstruktur, über die RIDs schnell lesbar sind. Man kann auch ein Index lesen, um seinen Inhalt zu sehen, zum Beispiel mit dem SELECT Befehl:

SELECT FROM INDEX:City.name
 

Als Output bekommt man eine Tabelle mit den Spalten key und rid (das Key/Value-Paar des Indexes). Zum Beispiel:

#   |@CLASS|key       |rid
----+------+----------+------
1   |null  |Dortmund  |#18:10
2   |null  |Frankfurt |#18:8
3   |null  |München   |#18:6
4   |null  |Stuttgart |#18:7
5   |null  |Berlin    |#18:5
----+------+----------+------

Selbstverständlich steht die WHERE -Klausel zur Verfügung:

SELECT FROM INDEX:City.name WHERE KEY='München'
 

Ein Index ist manipulierbar, indem man selbst Werte hinzufügt oder löscht:

INSERT INTO INDEX:myIndex (KEY, rid) VALUES ('MyValue', #12:45)
DELETE FROM INDEX:myIndex WHERE KEY = 'MyValue'
 

EXPLAIN: eine Query untersuchen

Wenn eine Query langsam scheint, kann man untersuchen, wie gut sie ist. Dafür nutzt man den Befehl EXPLAIN, zum Beispiel:

EXPLAIN SELECT FROM City WHERE name='München'
 

Als Output bekommt man dann nicht die Zeilen des Result-Sets, sondern Informationen zur Performance der Query:

  • elapsed: die Zeit, um die Query durchzuführen;
  • resultType und resultSize: Das Erste kann collection, document oder number sein und sagt aus, was man als Resultat bekommt; falls resultType eine collection sein sollte, bekommt man die Anzahl der Elemente;
  • recordReads, die Records, die vom Drive gelesen werden und documentReads, die Anzahl der gelesene Dokumente: die beiden Zahlen stimmen nicht immer überein;

Mit EXPLAIN kann man weitere Infos abfragen, etwa über Indexes: involvedIndexes gibt aus, welche Indexes benutzt wurden; und indexReads gibt Auskunft über die Anzahl der Records, die durch die Indexes gelesen wurden. Mit diesen Infos ist es dann möglich, die Queries und die Performances der Anwendungen zu optimieren.


Das war der dritte Teil meines DeepDives zu OrientDB.

  • Im ersten Teil geht es um Installation, erste Schritten und gängigen SQL-Abfragen in OrientDB.
  • Teil zwei behandelt Insert, SELECT, UPDATE DELETE und Relationen.
  • Teil vier dreht sich um Transaktionen, Nutzer und Rollen, verteilte Architekturen und APIs.

Über den Autor

Nicola Zunino

WE, Web Engineering

Nicola arbeitet seit 2017 bei MaibornWolff. Zuerst war er als Frontend Entwickler tätig, mit Schwerpunkte JavaScript und Frameworks wie Angular und React, entdeckte dann jedoch, dass ihm Architekturen und Analysen, also designen, mehr Spaß macht. Als ehemaliger Dozent, unterstützt er interne Schulungen (Clean Code) und Entwicklung der Kollegen. Als Doktor Ingenieur hat er sich in Daten Optimierung und Datenbanken spezialisiert: Datenanalyse, Pattern Matching und Clustering sind noch seine Leidenschaften. 

LinkedIn: https://www.linkedin.com/in/nicola-zunino-22852037/, Twitter: @NicolaZStrong, Instagram: @nicola_zunino, nicola.zunino@maibornwolff.de