Attributknoten definieren, welche Werte an Instanzen eines Knoten gesetzt
und ausgelesen werden können.
Im selbstbeschreibenden Datenmodell des Graphen ist ein Attributknoten eine
Instanz des Knotens attributknoten
.
Sein eindeutiger typ
hat die Form <knoten>_<attribut>
, während sein
name
zusätzlich den Datentyp des Attributknoten enthält.
Wie im Kapitel über Knoten näher beschrieben
wird, gibt es an jedem Knoten die Attributknoten <knoten>_name
,
<knoten>_ungueltig
und <knoten>_loeschbar
.
Durch Datenfunktionen können Werte von Attributknoten automatisch ausgerechnet werden.
Bezeichnung und Datentyp eines Attributknoten werden durch Instanzen des
Knoten attribut
angegeben.
Jeder Attributknoten ist mit genau einem Knoten und genau einem Attribut
verknüpft, die ihn zusammen vollständig definieren.
Datentypen sind Instanzen des Knoten datentyp
, die wiederum mit jeweils
genau einer Instanz des Knoten speicherort
verknüpft sind.
Die Implementierung des Graphen kennt zwei Speicherorte für Attribut-Werte:
MySQL
-Datentypen werden in der Datenbank abgelegt, während der einzige
Dateisystem
-Datentyp Dateisystem datei
direkt im Dateisystem abgelegte
Dateien beschreibt.
Folgende in der Datenbank abgelegte Datentypen kennt der Graph:
MySQL string
für kurze Texte,
MySQL text
für längere Texte (longtext
in der Datenbank) und
MySQL guid
für Instanz-GUIDs (32 hexadzimale Ziffern) haben.MySQL boolean
wird für Wahrheitswerte verwendet (in der Datenbank als
tinyint
abgelegt).MySQL date
für Daten, MySQL time
für
Uhrzeiten und MySQL datetime
für die Kombination aus beiden zur
Verfügung.MySQL integer
für ganze, MySQL float
für Fließkomma- und MySQL decimal1
bis MySQL decimal5
für
Festkomma-Zahlen mit ein bis fünf Nachkomma-Stellen definiert.Ein Attributknoten kann zusätzlich noch mit einer Instanz von mimetype
verknüpft sein.
Dieser gibt den MIME-/Datei-Typen der Attribut-Werte an, z.B.
application/json
oder application/pdf
.
Am Attribut kann ein Standard-MIME-Typ angegeben werden.
Dieser kann an allen Attributknoten, die dieses Attribut verwenden, jeweils
überschrieben werden.
Insbesondere für die numerischen Datentypen – integer
, float
und
decimal1
bis decimal5
– kann zusätzlich eine einheit
verknüpft sein.
Auch diese kann an Attributknoten überschrieben werden.
Schließlich kann für jeden Attributknoten angegeben werden, ob er eindeutig ist, ob in der Datenbank ein Index für ihn angelegt werden soll, und ob er der primäre Attributknoten seines Knoten sein soll.
Der Wert eines Attributknoten für eine Knoten-Instanz wird durch die
API-Funktion
setze(string $node_guid, string $attributknoten_typ, string $wert): ?boolean
gesetzt.
Er kann durch die API-Funktion
attribut(string $node_guid, string $attributknoten_typ): ?string
wieder
ausgelesen werden.
Außerdem existiert die API-Funktion
attribute(string $node_guid, string $knoten_typ, string $attribute): ?array<string, ?string>
,
mit der die Werte mehrerer Attribute einer Knoten-Instanz gleichzeitig
ausgelesen werden können.
Hierdurch werden nicht nur API-Aufrufe, sondern intern auch Anfragen an die
Datenbank gespart.
Die API-Funktion
attributsknoten(string $attributknoten_typ, string $wert): ?string
kann
verwendet werden, um für eindeutige Attributknoten die Knoten-Instanz
anhand des Wertes für diesen zu finden, wobei die Eindeutigkeit gerade
bedeutet, dass höchstens eine solche Instanz existiert.
Datenfunktionen sind Funktionen, die Werte von Attributknoten an Instanzen automatisch berechnen und setzen. Sie werden im Graphen als PHP-Code abgelegt, der Zugriff auf die Graph-API hat.
Datenfunktionen dürfen keine Seiteneffekte haben. Daher sind die erlaubten Aufrufe der API wie in deren Dokumentation angegeben eingeschränkt.
Ihre Funktionalität ist sogar noch weiter eingeschränkt: Sie dürfen nur Attribut-Werte von direkt verknüpften Knoten-Instanzen abfragen. Dies stellt sicher, dass einzelne Datenfunktionen keine beliebig komplexen Abfragen an den Graphen machen können. (Durch Datenfunktionen, die wiederum die Neuberechnung anderer Datenfunktionen auslösen, sind jedoch trotzdem sehr aufwändige Aufruf-Hierarchien möglich. Bei Programmier-Fehlern kann es sogar zu Endlos-Schleifen kommen.)
Die Werte, die von Datenfunktionen berechnet werden, müssen sich immer
deterministisch aus den von ihr gelesenen Graph-Strukturen ergeben.
Daher ist auch der Aufruf von Funktionen, die Informationen von außerhalb
des Graphen beziehen – beispielsweise date()
– nicht erlaubt.
Diese Einschränkungen werden unter anderem durch das Kritisieren von
Datenfunktionen sichergestellt.
Dieses ist im Graphmodul graphx
implementiert.
Es analysiert den Code einer Datenfunktion und markiert nicht erlaubte
API-Aufrufe.
Die ebenfalls in graphx
zur Verfügung stehenden Tasks zum Anlegen und
Ändern von Datenfunktionen können dann nicht abgeschlossen werden, solange
die Kritik Fehler liefert.
Der Graph analysiert außerdem automatisch, von welchen anderen
Verknüpfungen und Attribut-Werten die Berechnung abhängt.
Die Ergebnisse dieser Analyse legt er als Instanzen des Knoten
benutztattributknoten
in sich selbst ab.
Bei jeder Veränderung von Verknüpfungen und Attribut-Werten wird dann eine
Neuberechnung aller eventuell betroffenen Datenfunktionen
angestoßen/getriggert.
Wenn der Code für eine Datenfunktion angepasst wird, stimmen die bisher
berechneten Werte eventuell nicht mehr.
In diesem Fall können durch initialisieren
einer Datenfunktion die Werte
für alle Instanzen neu berechnet werden.
Die API-Funktion
berechne(string $node_guid, string $datenfunktion_name): ?mixed
kann
verwendet werden, um den momentanen Wert einer Datenfunktion an einer
Instanz auszurechnen.
Diese API-Funktion sollte in einem konsistenten Graphen das selbe Ergebnis
liefern wie der entsprechende attribut()
-Aufruf und ist daher vor allem
für die interne Konsistenz-Prüfung von Interesse.
Um möglichst wenig Code manuell schreiben zu müssen und die
Seiteneffekt-Freiheit von Datenfunktionen möglichst automatisch zu
gewährleisten, werden im Graphmodul graphx
Datenfunktions-Typen bereit
gestellt.
Diese erlauben durch das Konfigurieren möglichst weniger Verknüpfungen und Attribute das automatische Generieren des Codes von Datenfunktionen. Datenfunktions-Typen sind für immer wieder benötigte Arten von Funktionen definiert. Beispiele hierfür sind Aliasse, die Werte von Attributknoten an benachbarten Knoten übernehmen, Konkatenationen, die Werte von anderen Attributknoten am aktuellen Knoten zu Zeichenketten zusammensetzen, und arithmetische und logische Operationen auf den Werten von anderen Attributknoten.