Neo4j wie man ein zeitverändertes Diagramm modelliert

8

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:

  1. Die Abfrage, wer derzeit der Administrator ist, erfordert keine komplexen Abfragen
  2. Die Abfrage, wer gerade mit dem Gerät verbunden ist, erfordert keine komplexen Abfragen
  3. Die Abfrage des Verlaufs kann etwas komplexer sein.
Tomaž Bratanič 14.08.2017, 08:01
quelle

2 Antworten

7

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.

Der anfängliche Graphzustand:

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.

Abfragen:

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.

Ändern der Graphenstruktur

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:

%Vor%

Die resultierende Grafik lautet:

Nach dieser Strukturänderung lautet die Verbindungshistorie von device_id = 1 :

%Vor%

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 :

%Vor%

Derselbe Ansatz kann auch für die Verwaltung des Administratorverlaufs verwendet werden.

Offensichtlich hat dieser Ansatz einige Nachteile:

  • Sie müssen eine Reihe zusätzlicher Beziehungen verwalten
  • Teurere Abfragen
  • Komplexere Abfragen

Aber wenn Sie wirklich ein Versionsschema benötigen, ist dieser Ansatz meines Erachtens eine gute Option oder (zumindest) ein guter Startpunkt.

    
Bruno Peres 22.08.2017, 12:27
quelle
2

Auflösen einer GUID

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-ID

ist nur für die Produktionsdatenbank spezifisch und nicht global   für andere Quellen verwendet

Daraus kann ich 2 Dinge ableiten

  1. Benutzer bestehen aus mehreren Quellen.
  2. Für jede Quelle haben Benutzer eine eindeutige ID.

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?).

Aktuelle Daten abfragen

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.

  1. 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}) .

  2. 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?"

Datenhistorie

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.

    
Tezra 18.08.2017 16:09
quelle

Tags und Links