Teil 3: Konversationen in relationalen Datenbanken
Es gibt viele verschiedene Ansätze um Konversationen in Datenbanken abzubilden. Welche Ansätze dafür geeignet sind und warum möchte ich dir hier erklären.
Um zu entscheiden, welche Datenbank die richtige ist, um eine geführte Chatbot-Konversation abzubilden, musst du zunächst wissen, welche Strukturen Konversationen aufweisen. Dazu habe ich bereits einen eigenen Artikel verfasst. Darin habe ich aufgezeigt, warum Konversationsverläufe mit ihren netzwerkartigen Strukturen nicht mit Bäumen abbildbar sind und warum es sich stattdessen um Netzwerkstrukturen handelt. Ich gehe daher davon aus, dass Gespräche in geführten Konversationen im Wesentlichen aus den folgenden Elementen bestehen, die untereinander miteinander verbunden sein können:
- Fragen
- Antwortvorschläge
- Verbindungen zwischen ihnen
Im weiteren Verlauf dieser Blog-Serie werden noch weitere Elemente hinzukommen. Der Einfachheit halber reduziere ich die Konversation hier jedoch auf ihren Kern.
Relationale Ansätze
Hast du dich schon einmal gefragt, warum die Verwaltung relationaler Daten meist in tabellarischer Form erfolgt? Schaut man sich eine klassische CRUD an, so wird schnell deutlich, warum das so ist.
Oft gibt es einen tabellarischen bzw. listenähnlichen Überblick, von dem aus Aktionen wie CREATE, EDIT und DELETE ausgeführt werden können. Die Visualisierung der Daten entspricht dabei ihrer Repräsentation in der Datenbank. Das hat den großen Vorteil, dass man das, was man sieht, nicht erst für die Datenbank in eine andere Struktur übersetzen muss. Eine tabellarische CRUD ist also eine Art WYSIWYG (What you see is what you get) für tabellarische Daten. Zeilen und Spalten in einer Liste von Datensätzen repräsentieren Zeilen und Spalten in einer Datenbanktabelle.
Es würde für Entwickler viel gedankliche Arbeit und umständliches Coding erfordern, die Daten im Frontend zum Beispiel als Baum oder Netzwerk darzustellen. An diesem NestedSet-Beispiel auf GitHub siehst du, was an grundlegender Programmierung erforderlich ist, um Baumstrukturen performant in einer MySQL-Tabelle zu speichern, damit man sie in nur einem Query auslesen kann. Auch die Generierung des Baums im Frontend braucht wesentlich mehr Logik als eine einfache Tabelle.
Was bedeutet das für die Speicherung von Netzwerkstrukturen?
Netzwerke sind weitaus komplexer als Bäume. Denn sie ermöglichen es, dass einzelne Äste wieder zu einem neuen Stamm zusammenwachsen. Wir haben also eine endliche Anzahl an Items mit jeweils n Verbindungen zu anderen Items, die ihrerseits wieder n Verbindungen zu anderen Items haben.
Übertragen auf unser Modell aus Fragen und vorgegebenen Antwortmöglichkeiten bedeutet das folgendes:
- Wir brauchen eine Tabelle, in der wir die Fragen mit ihren Eigenschaften speichern
- Wir brauchen eine Tabelle, in der wir die Antworten samt Eigenschaften speichern
- Wir brauchen eine Tabelle in der wir die Verbindungen zwischen den Fragen und Antworten speichern
Fragen (questions):
id | label |
---|---|
1 | Hey! How are you? |
2 | Why are you stressed? |
3 | What about your job? |
4 | What do you want to do next? |
Antwortmöglichkeiten (suggestions):
id | label |
---|---|
1 | I'am stressed |
2 | I'am fine |
3 | My job is stupid :-( |
4 | It’s not your business. |
5 | My boss is stupid |
6 | Please! Stop talking! You stupid bot! |
7 | Book a holiday for me. |
Verbindungen zwischen den Items (edges):
id | from | to | from_type | to_type |
---|---|---|---|---|
1 | 1 | 1 | question | suggestion |
2 | 1 | 2 | question | suggestion |
3 | 2 | 3 | question | suggestion |
4 | 2 | 4 | question | suggestion |
5 | 3 | 4 | question | suggestion |
6 | 3 | 5 | question | suggestion |
7 | 1 | 4 | question | question |
8 | 6 | 2 | question | question |
9 | 4 | 6 | question | suggestion |
10 | 4 | 7 | question | suggestion |
11 | 1 | 2 | suggestion | question |
11 | 2 | 4 | suggestion | question |
11 | 3 | 4 | suggestion | question |
11 | 5 | 4 | suggestion | question |
Du erkennst bereits hier, dass es schwierig wird, netzwerkartige Konversationen in tabellarischer Form abzubilden, obwohl es theoretisch möglich ist. Die Problematik der wir hier gegenüber stehen, besteht im Wesentlichen darin, dass die Daten unstrukturiert miteinander verbunden sind. Fragen können mit Antworten aber auch direkt mit anderen Fragen verbunden sein. Antworten führen zu weiteren Fragen. Erhöht man die Anzahl der Items und fügt weitere Itemarten hinzu, wird das Problem wesentlich komplexer.
Auch das Aufspalten der Edges-Tabelle in mehrere Tabellen mit eindeutigen Verbindungstypen würde die Komplexität hier nicht reduzieren und das Problem nur verlagern.
Stell dir nun vor, du müsstest deinem User einen Editor für Konversationen bauen, der auf diesem Datenmodell beruht. Auch wenn es theoretisch möglich wäre, elegant und einfach ist es nicht.
Neben der Speicherung gibt es nun aber noch ein ganz anderes Problem: Stell dir vor, du musst aus der Datenbank auslesen, welche Frage nun die nächste sinnvolle ist. Und das am Besten elegant und ohne große Umwege. Am Besten mit nur einem Query. Dass wird dir mit einer Sprache wie SQL nicht gelingen. Du musst dafür wiederkehrende Subquerys benutzen oder auf Stored Procedures zurückgreifen und eine relativ komplexe Programmlogik entwerfen. Eine mögliche Prozedur könnte so aussehen:
- Wähle die erste Frage aus der Datenbank aus
- Wähle alle Antwortmöglichkeiten aus, die zu dieser Frage gehören
- Prüfe, ob die Frage oder eine oder mehrere der Antworten erfüllt wurden
- Wähle zu jedem beantworteten Vorschlag die nächsten Fragen aus
- Beginne die Prozedur für diese Fragen wieder von vorne...
Document-Stores
Reine Document-Stores wie MongoDB weisen ähnliche Problematiken auf. Du kannst dich entweder dafür entscheiden, deine Daten auf Dokumentebene zu schachteln oder alternativ auf Ebene der Records referenzieren. Letztendlich wirst du aber auch hier keine Möglichkeit finden, unstrukturierte Verbindungen elegant zu speichern und auszulesen. Die Problematik bleibt die gleiche.
Graphansätze
Sicher hast du es bereits geahnt. Die einzig sinnvolle Möglichkeit, um unstrukturierte Verbindungen zwischen verschiedenen Items abzubilden, sind Graphdatenbanken. Sie erlauben es dir, deine Netzwerkstruktur genau so abzubilden, wie du sie auch in der Realität erwarten würdest. Das eröffnet dir die Möglichkeit, dass du die Netzwerkstruktur, die du mit einem Editor im Frontend konzipierst, auch genauso in der Datenbank abgelegt werden kann.
Wie das genau aussehen kann, zeige ich dir im nächsten Beitrag.