Ein Teil meines Diagramms hat das folgende Schema:
Der Hauptteil des Diagramms ist die Domäne, mit der einige Personen verknüpft sind. Person hat eine eindeutige Einschränkung für die E-Mail-Eigenschaft, da ich auch Daten aus anderen Quellen habe und das passt gut.
Eine Person kann in meinem Fall ein Administrator sein, wo er einige Geräte / Kalender mit ihm verknüpft hat. Ich bekomme diese Daten von einer SQL-Datenbank, wo ich einige Tabellen importiere, um das ganze Bild zu kombinieren. Ich beginne mit einer Tabelle, die zwei Spalten hat, E-Mail des Admins und seine Benutzer-ID. Diese Benutzer-ID ist nur für die Produktionsdatenbank spezifisch und wird auch nicht global für andere Quellen verwendet. Deshalb verwende ich E-Mail als globale ID für Personen. Ich verwende derzeit die folgende Abfrage, um eine Benutzer-ID zu importieren, mit der alle Produktionstabellen verknüpft sind. Ich erhalte immer den aktuellen Schnappschuss der Benutzereinstellungen und Informationen. Diese Abfrage läuft 4x / Tag:
%Vor%Und dann importiere ich alle Daten, die mit dieser Benutzer-ID verknüpft sind, aus anderen Tabellen.
Jetzt tritt das Problem auf, weil der Benutzer aus der Produktionsdatenbank seine E-Mail ändern kann. So, wie ich das jetzt importiere, werde ich mit zwei Personen enden, die dieselbe user_id haben, und anschließend werden alle Geräte / Kalender mit beiden Personen verknüpft, da sie beide dieselbe user_id teilen. Das ist also keine genaue Darstellung der Realität. Wir müssen auch das Verbinden / Trennen von Geräten mit bestimmten user_id im Laufe der Zeit erfassen, da man ein Gerät verbinden / trennen und es an einen Freund ausleihen kann, der einen anderen Admin hat (user_id).
Wie ändere ich mein Graph-Modell (Import-Abfrage), so dass:
Diese Antwort basiert auf Ian Robinsons Post über zeitbasierte versionierte Grafiken .
Ich weiß nicht, ob diese Antwort ALLE die Anforderungen der Frage abdeckt, aber ich glaube, das kann einige Einsichten liefern.
Außerdem denke ich, dass Sie nur an einer strukturellen Versionierung interessiert sind (das heißt: Sie sind nicht an Abfragen über die Änderungen des Namens des Domänenbenutzers im Laufe der Zeit interessiert). Schließlich verwende ich eine Teildarstellung Ihres Graphenmodells, aber ich glaube, dass die hier gezeigten Konzepte im gesamten Graphen angewendet werden können.
In Anbetracht dieser Cypher, um einen anfänglichen Graphenzustand zu erstellen:
%Vor%Ergebnis:
Das obige Diagramm enthält 3 Personenknoten. Diese Knoten sind Mitglieder eines Domänenknotens. Der Personenknoten mit person_id = 1
ist mit einem Gerät mit device_id = 1
verbunden. Außerdem ist person_id = 1
der aktuelle Administrator. Die Eigenschaften from
und to
in den Beziehungen :ADMIN
und :CONNECTED_DEVICE
werden verwendet, um den Verlauf der Graphenstruktur zu verwalten. from
repräsentiert einen Startpunkt in der Zeit und to
einen Endpunkt in der Zeit. Zur Vereinfachung verwende ich 0 als Anfangszeit des Graphen und 1000 als Endzeitkonstante. In einem realen Weltdiagramm kann die aktuelle Zeit in Millisekunden verwendet werden, um Zeitpunkte darzustellen. Außerdem kann Long.MAX_VALUE
stattdessen als EOT-Konstante verwendet werden. Eine Beziehung mit to = 1000
bedeutet, dass es keine aktuelle obere Grenze für die damit verbundene Periode gibt.
Mit diesem Diagramm kann ich den aktuellen Administrator erreichen:
%Vor%Das Ergebnis wird sein:
%Vor%Gegeben ein Gerät, um den aktuellen verbundenen Benutzer zu erhalten:
%Vor%Ergebnis:
%Vor%Um den aktuellen Administrator und die aktuelle Person, die mit einem Gerät verbunden ist, abzufragen, wird die End-of-Time-Konstante verwendet.
Abfrage der Ereignisse zum Verbinden / Trennen von Geräten:
%Vor%Ergebnis:
%Vor% Das obige Ergebnis zeigt, dass person_id = 1
bis heute mit device_id = 1
des Anfangs verbunden ist.
Beachten Sie, dass der aktuelle Zeitpunkt 30 ist. Jetzt trennt user_id = 1
von device_id = 1
. user_id = 2
stellt eine Verbindung her. Um diese strukturelle Änderung darzustellen, werde ich die folgende Abfrage ausführen:
Die resultierende Grafik lautet:
Nach dieser Strukturänderung lautet die Verbindungshistorie von device_id = 1
:
Das obige Ergebnis zeigt, dass user_id = 1
mit device_id = 1
von 0 bis 30 verbunden war. person_id = 2
ist momentan mit device_id = 1
verbunden.
Jetzt ist die aktuelle Person, die mit device_id = 1
verbunden ist, person_id = 2
:
Derselbe Ansatz kann auch für die Verwaltung des Administratorverlaufs verwendet werden.
Offensichtlich hat dieser Ansatz einige Nachteile:
Aber wenn Sie wirklich ein Versionsschema benötigen, ist dieser Ansatz meines Erachtens eine gute Option oder (zumindest) ein guter Startpunkt.
Als erstes müssen Sie die Benutzer-IDs zuverlässig auflösen, damit sie konsistent und global eindeutig sind. Jetzt hast du gesagt:
Die Benutzer-IDist nur für die Produktionsdatenbank spezifisch und nicht global für andere Quellen verwendet
Daraus kann ich 2 Dinge ableiten
Das bedeutet, dass source + user.id
eine GUID ist. (Sie können die Hauptverbindungs-URL hashen oder jede Quelle extern benennen.) Ich gehe davon aus, dass Sie Benutzer nicht über mehrere Quellen zusammenführen, da das Duplizieren und Zusammenführen von Daten über ein beliebiges Netzwerk ein Paradox der Aktualisierungsreihenfolge erzeugt, das so weit wie möglich vermieden werden sollte zwei Quellen listen verschiedene neue Kontaktnummern auf, wer ist richtig?).
Die Abfragelogik sollte unabhängig von der Versionsverfolgung sein, die Sie möglicherweise ausführen. Wenn Ihre Versionierung Probleme mit der Logik verursacht, fügen Sie eine Meta-Bezeichnung wie :Versioned
mit indizierter Eigenschaft isLatest
hinzu und heften Sie eine Where n.isLatest
an, um die alten "Müll" -Daten aus Ihren Ergebnissen herauszufiltern.
Damit Sie sich keine Sorgen über die Version machen müssen, können die Abfragen 1 und 2 normal behandelt werden.
Um Personen zu finden, die Admins sind, würde ich empfehlen, der Person nur die Bezeichnung :Admin
hinzuzufügen und sie zu entfernen, wenn sie nicht länger gilt (je nach Bedarf). Dies wird mit dem Label "Admin" indexiert. Sie können auch einfach eine "isAdmin" -Eigenschaft verwenden (was wahrscheinlich ist, dass Sie sie bereits in der db speichern, also konsistenter.) Also wäre die letzte Abfrage nur MATCH (p:Person:Admin)
oder MATCH (p:Person{isAdmin:true})
.
Wenn die alten Versionsinformationen herausgefiltert wurden, lautet die Abfrage, wer ein Gerät hat, einfach MATCH (p:Person:Versioned{isCurrent:true})-[:HasDevice{isConnected:true}]->(d:Device:Versioned{isCurrent:true})
Dieses Bit dreht sich wirklich nur um "Was ist dein Schema?"
Hier wird es wirklich knifflig. Je nachdem, wie Sie die Daten versionieren, können Sie am Ende Ihre Datengröße sprengen und Ihre DB-Leistung zunichte machen. Sie müssen sich wirklich fragen "Warum versioniere ich das?", "Wie oft wird dieses Update / gelesen?", "Wer wird es benutzen und was werden sie damit machen?". Wenn Sie zu irgendeinem Zeitpunkt antworten "Ich weiß es nicht", sollten Sie dies entweder nicht tun, oder Ihre Daten in einer Datenbank sichern, die dies für Sie wie SQLAlchemy-Continuum . (Verwandte Antwort )
Wenn Sie dies in Neo4j tun müssen, dann würde ich eine Delta-Kette empfehlen. Wenn Sie zum Beispiel {a:1, b:2}
in {a:1, b:null, c:3}
geändert haben, hätten Sie (:Thing{a:1, b:null, c:3})-[_DELTA{timestamp:<value>}]->(:_ThingDelta{b: 2, c:null})
. Auf diese Weise können Sie die Eigenschaften der Delta-Kette in einer Karte anwenden, um einen Wert in der Vergangenheit zu erhalten. Also MATCH (a:Thing) OPTIONAL MATCH (a)-[d:_DELTA*]->(d) WHERE d.timestamp >= <value> WITH reduce(v = {_id:ID(a)}, n IN nodes(p)| v += PROPERTIES(n)) AS OldVersion
. Dies kann jedoch sehr mühsam werden und Ihren DB Space auffressen. Daher würde ich Ihnen wärmstens empfehlen, ein existierendes DB-Versions-Ding um jeden Preis zu verwenden, wenn Sie können.