Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Effiziente Gremlin-Upserts mit fold()/coalesce()/unfold()
Ein Upsert (bzw. eine bedingte Einfügung) verwendet Eckpunkte oder Kanten, wenn bereits vorhanden, oder erstellt sie andernfalls. Effiziente Upserts können die Leistung von Gremlin-Abfragen deutlich verbessern.
Diese Seite zeigt die Verwendung des Gremlin-Musters fold()/coalesce()/unfold()
zur Erstellung effizienter Upserts. Mit der Veröffentlichung der TinkerPop Version 3.6.x, die in Neptune in der Engine-Version 1.2.1.0 eingeführt wurde, sind die neuen mergeE()
Schritte mergeV()
und in den meisten Fällen jedoch vorzuziehen. Das hier beschriebene Muster fold()/coalesce()/unfold()
ist in einigen komplexen Situationen möglicherweise weiter nützlich. Grundsätzlich sollten Sie jedoch mergeV()
und mergeE()
verwenden, wie in Effiziente Upserts mit mergeV()- und mergeE()-Schritten in Gremlin beschrieben.
Mit Upserts können Sie idempotente Einfügeoperationen schreiben: Das Ergebnis bleibt gleich, unabhängig davon, wie häufig eine Operation ausgeführt wird. Dies ist in hoch gleichzeitigen Schreibszenarien nützlich, in denen gleichzeitige Änderungen für denselben Teil eines Diagramms zumden Rollback einer oder mehrerer Transaktionen mit einer ConcurrentModificationException
erzwingen können, was eine Wiederholung erforderlich macht.
Die folgende Abfrage führt beispielsweise ein Eckpunkt-Upsert aus, indem sie zunächst im Datensatz nach dem angegebenen Eckpunkt sucht und dann die Ergebnisse zu einer Liste zusammenfasst. Bei der ersten Traversierung für den coalesce()
-Schritt wird diese Liste dann von der Abfrage geöffnet. Wenn die geöffnete Liste nicht leer ist, werden die Ergebnisse von coalesce()
ausgegeben. Wenn unfold()
jedoch eine leere Sammlung zurückgibt, da der Eckpunkt zurzeit nicht vorhanden ist, evaluiert coalesce()
die zweite bereitgestellte Traversierung. Bei dieser zweiten Traversierung erstellt die Abfrage den fehlenden Eckpunkt.
g.V('v-1').fold() .coalesce( unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org') )
Verwenden einer optimierten Form von coalesce()
für Upserts
Neptune kann den Ausdruck fold().coalesce(unfold(), ...)
optimieren, um Aktualisierungen mit einem hohen Durchsatz zu erstellen. Dies funktioniert jedoch nur, wenn beide Teile von coalesce()
entweder einen Eckpunkt oder eine Kante zurückgeben, aber sonst nichts. Wenn Sie versuchen, aus einem Teil von coalesce()
etwas anderes zurückzugeben, z. B. eine Eigenschaft, erfolgt die Neptune-Optimierung nicht. Die Abfrage kann zwar erfolgreich sein, funktioniert jedoch nicht so gut wie eine optimierte Version, insbesondere bei großen Datensätzen.
Da nicht optimierte Upsert-Abfragen die Ausführungszeiten verlängern und den Durchsatz reduzieren, lohnt sich die Verwendung des Gremlin-Endpunkts explain
, um festzustellen, ob eine Upsert-Abfrage vollständig optimiert ist. Achten Sie bei der Überprüfung von explain
-Plänen auf Zeilen, die mit + not converted into Neptune steps
und WARNING: >>
beginnen. Beispielsweise:
+ not converted into Neptune steps: [FoldStep, CoalesceStep([[UnfoldStep], [AddEdgeSte
...
WARNING: >> FoldStep << is not supported natively yet
Diese Warnungen können Ihnen helfen, die Teile einer Abfrage zu identifizieren, die ihre vollständige Optimierung verhindern.
Manchmal ist es nicht möglich, eine Abfrage vollständig zu optimieren. In diesen Situationen sollten Sie versuchen, die Schritte, die nicht optimiert werden können, an das Ende der Abfrage zu setzen, sodass die Engine so viele Schritte wie möglich optimieren kann. Diese Technik wird in einigen der Beispiele für Batch-Upserts verwendet, bei denen alle optimierten Upserts für einen Satz von Eckpunkten oder Kanten ausgeführt werden, bevor auf dieselben Eckpunkte oder Kanten zusätzliche, möglicherweise nicht optimierte Änderungen angewendet werden.
Batching von Upserts zur Verbesserung des Durchsatzes
In Schreibszenarien mit einem hohen Durchsatz können Sie Upsert-Schritte verketten, um Upserts für Eckpunkte und Kanten batchweise auszuführen. Batching reduziert den Transaktionsaufwand, der entsteht, wenn für eine große Anzahl von Eckpunkten und Kanten Upserts ausgeführt werden. Sie können den Durchsatz weiter verbessern, indem Sie Batch-Anforderungen parallel über mehrere Clients ausführen.
Als Faustregel gilt, dass pro Batch-Anforderung Upserts für ungefähr 200 Datensätze ausgeführt werden sollten. Ein Datensatz ist eine einzelne Eckpunkt- oder Kantenbezeichnung oder -eigenschaft. Ein Eckpunkt mit einer einzelnen Bezeichnung und 4 Eigenschaften generiert beispielsweise 5 Datensätze. Eine Kante mit einer Bezeichnung und einer einzelnen Eigenschaft generiert 2 Datensätze. Wenn Sie Upserts für Batches von Eckpunkten ausführen möchten, die jeweils eine einzelne Bezeichnung und 4 Eigenschaften besitzen, sollten Sie mit einer Batchgröße von 40 beginnen, da 200 / (1 + 4)
= 40
.
Sie können mit der Batchgröße experimentieren. 200 Datensätze pro Batch sind ein guter Ausgangspunkt. Die ideale Batchgröße kann je nach Workload jedoch höher oder niedriger sein. Beachten Sie jedoch, dass Neptune die Gesamtzahl der Gremlin-Schritte pro Anforderung einschränken kann. Diese Einschränkung ist nicht dokumentiert. Um jedoch auf der sicheren Seite zu sein, sollten Ihre Anforderungen nicht mehr als 1 500 Gremlin-Schritte enthalten. Neptune lehnt große Batchanforderungen mit mehr als 1 500 Schritten möglicherweise ab.
Um den Durchsatz zu erhöhen, können Sie Upserts für Batches parallel über mehrere Clients ausführen (siehe Erstellen von effizienten Multi-Thread-Gremlin-Schreibvorgängen). Die Anzahl der Clients sollte der Anzahl der Worker-Threads auf Ihrer Neptune-Writer-Instance entsprechen, die normalerweise dem 2-fachen der Anzahl vCPUs auf dem Server entspricht. Eine r5.8xlarge
Instance hat beispielsweise 32 vCPUs und 64 Worker-Threads. Für Schreibszenarien mit einem hohen Durchsatz über eine r5.8xlarge
würden Sie daher 64 Clients verwenden, die Batch-Upserts parallel in Neptune schreiben.
Jeder Client sollte eine Batch-Anforderung einreichen und mit der Einreichung einer weiteren Anforderung bis zum Abschluss der Anforderung warten. Obwohl diese mehreren Clients parallel ausgeführt werden, senden die einzelnen Clients die Anforderungen seriell. Dies stellt sicher, dass der Server kontinuierlich Anforderungen erhält, die alle Worker-Threads auslasten, ohne dass die serverseitige Anforderungswarteschlange überlastet wird (siehe Dimensionieren von DB-Instances in einem Neptune-DB-Cluster).
Vermeiden von Schritten, die mehrere Traverser generieren
Wenn ein Gremlin-Step ausgeführt wird, nimmt er einen eingehenden Traverser auf und gibt einen oder mehrere Ausgabe-Traverser aus. Die Anzahl der von einem Schritt ausgegebenen Traverser bestimmt, wie häufig der nächste Schritt ausgeführt wird.
In der Regel soll jede Batchoperation, z. B. ein Upsert von Eckpunkt A, nur einmal ausgeführt werden, sodass die Reihenfolge der Operationen wie folgt aussieht: Upsert von Eckpunkt A, dann Upsert von Eckpunkt B, dann Upsert von Eckpunkt C und so weiter. Solange ein Schritt nur ein Element erstellt oder ändert, gibt er nur einen Traverser aus, und die Schritte, die die nächste Operation darstellen, werden nur einmal ausgeführt. Wenn eine Operation hingegen mehr als ein Element erstellt oder ändert, gibt sie mehrere Traverser aus, sodass die nachfolgenden Schritte mehrfach ausgeführt werden, jeweils einmal pro ausgegebenem Traverser. Dies kann dazu führen, dass die Datenbank unnötige zusätzliche Arbeit leistet. In einigen Fällen kann es zur Erstellung unerwünschter zusätzlicher Eckpunkte, Kanten oder Eigenschaftswerte kommen.
Ein Beispiel für Fehler dieser Art ist eine Abfrage wie g.V().addV()
. Diese einfache Abfrage fügt für jeden im Diagramm gefundenen Eckpunkt einen Eckpunkt hinzu, da V()
für jeden Eckpunkt im Diagramm einen Traverser ausgibt und jeder Traverser den Aufruf von addV()
auslöst.
Informationen zur Behandlung von Operationen, die mehrere Traverser auslösen können, finden Sie unter Kombinieren von Upserts und Einfügungen.
Upserts für Eckpunkte
Sie können mittels einer Eckpunkt-ID feststellen, ob ein entsprechender Eckpunkt vorhanden ist. Dies ist der bevorzugte Ansatz, da Neptune Upserts für stark parallele Anwendungsfälle optimiert. IDs Die folgende Abfrage erstellt beispielsweise einen Eckpunkt mit einer bestimmten Eckpunkt-ID, wenn noch nicht vorhanden, oder verwendet ihn erneut, wenn vorhanden:
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .id()
Beachten Sie, dass diese Abfrage mit dem id()
-Schritt endet. Der id()
-Schritt am Ende einer Upsert-Abfrage ist nicht unbedingt erforderlich, stellt jedoch sicher, dass der Server nicht alle Eckpunkteigenschaften zurück zum Client serialisiert, was den Sperraufwand der Abfrage reduziert.
Alternativ können Sie mittels einer Eckpunkteigenschaft ermitteln, ob der Eckpunkt vorhanden ist:
g.V() .hasLabel('Person') .has('email', 'person-1@example.org') .fold() .coalesce(unfold(), addV('Person').property('email', 'person-1@example.org')) .id()
Verwenden Sie IDs nach Möglichkeit Ihre eigenen, vom Benutzer bereitgestellten Scheitelpunkte und bestimmen Sie anhand dieserIDs, ob während eines Upsert-Vorgangs ein Scheitelpunkt vorhanden ist. Auf diese Weise kann Neptune Störungen rund um den optimieren. IDs ID-basierte Upserts können in hoch gleichzeitigen Änderungsszenarien deutlich effizienter als eigenschaftsbasierte Upserts sein.
Verketten von Eckpunkt-Upserts
Sie können Eckpunkt-Upserts verketten, um sie in ein Batch einzufügen:
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .id()
Upserts für Kanten
Sie können die Option Kante IDs zum Hochsetzen von Kanten auf die gleiche Weise verwenden, wie Sie Scheitelpunkte mithilfe eines benutzerdefinierten Scheitelpunkts hochsetzen. IDs Dies ist auch hier der bevorzugte Ansatz, da Neptune so die Abfrage optimieren kann. Die folgende Abfrage erstellt beispielsweise eine Kante auf Grundlage der Kanten-ID, wenn noch nicht vorhanden, oder verwendet sie erneut, wenn vorhanden. Die Abfrage verwendet auch die IDs to
Eckpunkte from
und, wenn eine neue Kante erstellt werden muss.
g.E('e-1') .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2')) .property(id, 'e-1')) .id()
Viele Anwendungen verwenden benutzerdefinierte ScheitelpunkteIDs, überlassen es aber Neptune, um Kanten zu erzeugen. IDs Wenn Sie die ID einer Kante nicht kennen, aber Sie kennen den from
und den to
Scheitelpunkt, können Sie diese Formel verwendenIDs, um eine Kante nach oben zu verlagern:
g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2'))) .id()
Dr Eckpunktschritt in der where()
-Klausel sollte inV()
sein (oder outV()
, wenn Sie inE()
für die Suche nach der Kante verwendet haben), nicht otherV()
. Verwenden Sie hier nicht otherV()
, da die Abfrage dann nicht optimiert wird und die Leistung beeinträchtigt wird. Neptune würde beispielsweise die folgende Abfrage nicht optimieren:
// Unoptimized upsert, because of otherV() g.V('v-1') .outE('KNOWS') .where(otherV().hasId('v-2')) .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2'))) .id()
Wenn Sie die Kante oder den Scheitelpunkt im Vordergrund nicht kennen, können Sie die Eckpunkteigenschaften verwenden, IDs um die Kante oder den Scheitelpunkt nach oben zu verlagern:
g.V() .hasLabel('Person') .has('name', 'person-1') .outE('LIVES_IN') .where(inV().hasLabel('City').has('name', 'city-1')) .fold() .coalesce(unfold(), addE('LIVES_IN').from(V().hasLabel('Person') .has('name', 'person-1')) .to(V().hasLabel('City') .has('name', 'city-1'))) .id()
Wie bei Scheitelpunkt-Upserts ist es vorzuziehen, ID-basierte Kantenaufhebungen zu verwenden, die entweder eine Kanten-ID oder from
einen to
Eckpunkt verwendenIDs, anstatt eigenschaftsbasierte Upserts zu verwenden, damit Neptune den Upsert vollständig optimieren kann.
Prüfen des Vorhandenseins der from
- und to
-Eckpunkte
Beachten Sie die Konstruktion der Schritte zur Erstellung einer neuen Kante: addE().from().to()
. Diese Konstruktion stellt sicher, dass die Abfrage das Vorhandensein sowohl des from
- als auch des to
-Eckpunkts prüft. Wenn keiner dieser Eckpunkte vorhanden ist, gibt die Abfrage einen Fehler wie folgt zurück:
{ "detailedMessage": "Encountered a traverser that does not map to a value for child
...
"code": "IllegalArgumentException", "requestId": "..." }
Wenn der from
- oder der to
-Eckpunkt möglicherweise nicht vorhanden ist, sollten Sie ein Upsert diesen Eckpunkt versuchen, bevor Sie ein Upsert für die Kante zwischen den beiden Eckpunkten ausführen. Siehe Kombinieren von Eckpunkt- und Kanten-Upserts.
Es gibt eine alternative Konstruktion zum Erstellen einer Kante, die Sie nicht verwenden sollten: V().addE().to()
. Sie fügt nur dann eine Kante hinzu, wenn der from
-Eckpunkt vorhanden ist. Wenn der to
-Eckpunkt nicht vorhanden ist, generiert die Abfrage wie zuvor beschrieben einen Fehler. Wenn jedoch der from
-Eckpunkt nicht vorhanden ist, schlägt das Einfügen einer Kante ohne Meldung fehl, ohne dass ein Fehler generiert wird. Die folgende Upsert-Operation wird beispielsweise ohne Kanten-Upsert abgeschlossen, wenn der from
-Eckpunkt nicht vorhanden ist:
// Will not insert edge if from vertex does not exist g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2'))) .id()
Verketten von Kanten-Upserts
Wenn Sie Kantenüberhöhungen aneinanderreihen möchten, um eine Batch-Anfrage zu erstellen, müssen Sie jeden Upsert mit einer Scheitelpunktsuche beginnen, auch wenn Sie die Kante bereits kennen. IDs
Wenn Sie bereits wissen, welche Kanten Sie hochdrücken möchten, und die Eckpunkte und die IDs to
Eckpunkte, können Sie die from
folgende Formel verwenden: IDs
g.V('v-1') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2')) .property(id, 'e-1')) .V('v-3') .outE('KNOWS') .hasId('e-2').fold() .coalesce(unfold(), V('v-3').addE('KNOWS') .to(V('v-4')) .property(id, 'e-2')) .V('v-5') .outE('KNOWS') .hasId('e-3') .fold() .coalesce(unfold(), V('v-5').addE('KNOWS') .to(V('v-6')) .property(id, 'e-3')) .id()
Das vielleicht am häufigsten vorkommende Szenario ist, dass Sie den Eckpunkt from
und den to
Eckpunkt im Batchverfahren kennenIDs, aber nicht wissen, welche IDs der Kanten Sie hochbiegen möchten. Verwenden Sie in diesem Fall die folgende Formulierung:
g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2'))) .V('v-3') .outE('KNOWS') .where(inV().hasId('v-4')) .fold() .coalesce(unfold(), V('v-3').addE('KNOWS') .to(V('v-4'))) .V('v-5') .outE('KNOWS') .where(inV().hasId('v-6')) .fold() .coalesce(unfold(), V('v-5').addE('KNOWS').to(V('v-6'))) .id()
Wenn Sie die Kanten kennenIDs, die Sie hochdrücken möchten, aber die IDs to
Eckpunkte from
und nicht kennen (das ist ungewöhnlich), können Sie die folgende Formel verwenden:
g.V() .hasLabel('Person') .has('email', 'person-1@example.org') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-1@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-2@example.org')) .property(id, 'e-1')) .V() .hasLabel('Person') .has('email', 'person-3@example.org') .outE('KNOWS') .hasId('e-2') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-3@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-4@example.org')) .property(id, 'e-2')) .V() .hasLabel('Person') .has('email', 'person-5@example.org') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-5@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-6@example.org')) .property(id, 'e-3')) .id()
Kombinieren von Eckpunkt- und Kanten-Upserts
Manchmal möchten Sie vielleicht Upserts für Eckpunkte und die Kanten ausführen, die sie verbinden. Sie können die hier gezeigten Batch-Beispiele kombinieren. Das folgende Beispiel zeigt ein Upsert für 3 Eckpunkte und 2 Kanten:
g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2') .property('name', 'person-2@example.org')) .V('c-1') .fold() .coalesce(unfold(), addV('City').property(id, 'c-1') .property('name', 'city-1')) .V('p-1') .outE('LIVES_IN') .where(inV().hasId('c-1')) .fold() .coalesce(unfold(), V('p-1').addE('LIVES_IN') .to(V('c-1'))) .V('p-2') .outE('LIVES_IN') .where(inV().hasId('c-1')) .fold() .coalesce(unfold(), V('p-2').addE('LIVES_IN') .to(V('c-1'))) .id()
Kombinieren von Upserts und Einfügungen
Manchmal möchten Sie vielleicht Upserts für Eckpunkte und die Kanten ausführen, die sie verbinden. Sie können die hier gezeigten Batch-Beispiele kombinieren. Das folgende Beispiel zeigt ein Upsert für 3 Eckpunkte und 2 Kanten:
In der Regel werden Upserts Element für Element ausgeführt. Wenn Sie die hier gezeigten Upsert-Muster befolgen, gibt jede Upsert-Operation einen einzelnen Traverser aus, sodass die nachfolgende Operation jeweils nur einmal ausgeführt wird.
Manchmal möchten Sie jedoch vielleicht Upserts und Einfügungen kombinieren. Dies kann beispielsweise der Fall sein, wenn Sie Kanten verwenden, um Instances von Aktionen oder Ereignissen darzustellen. Möglicherweise verwendet eine Anforderung Upserts, um sicherzustellen, dass alle notwendigen Eckpunkte vorhanden sind, und dann Einfügungen, um Kanten hinzuzufügen. Achten Sie bei Anforderungen dieser Art auf die potenzielle Anzahl von Traversern, die bei jeder Operation ausgegeben werden.
Im folgenden Beispiel werden Upserts und Einfügungen kombiniert, um dem Diagramm Kanten hinzuzufügen, die Ereignisse repräsentieren:
// Fully optimized, but inserts too many edges g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2') .property('name', 'person-2@example.org')) .V('p-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-3') .property('name', 'person-3@example.org')) .V('c-1') .fold() .coalesce(unfold(), addV('City').property(id, 'c-1') .property('name', 'city-1')) .V('p-1', 'p-2') .addE('FOLLOWED') .to(V('p-1')) .V('p-1', 'p-2', 'p-3') .addE('VISITED') .to(V('c-1')) .id()
Die Abfrage sollte 5 Kanten einfügen: 2 FOLLOWED Kanten und 3 VISITED Kanten. Die geschriebene Abfrage fügt jedoch 8 Kanten ein: 2 FOLLOWED und VISITED 6. Der Grund dafür ist, dass bei der Operation, bei der die beiden FOLLOWED Kanten eingefügt werden, 2 Traverser ausgegeben werden, wodurch die nachfolgende Einfügeoperation, bei der 3 Kanten eingefügt werden, zweimal ausgeführt wird.
Die Lösung besteht darin, nach jeder Operation, die mehr als einen Traverser ausgeben könnte, einen fold()
-Schritt hinzuzufügen:
g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2'). .property('name', 'person-2@example.org')) .V('p-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-3'). .property('name', 'person-3@example.org')) .V('c-1') .fold(). .coalesce(unfold(), addV('City').property(id, 'c-1'). .property('name', 'city-1')) .V('p-1', 'p-2') .addE('FOLLOWED') .to(V('p-1')) .fold() .V('p-1', 'p-2', 'p-3') .addE('VISITED') .to(V('c-1')). .id()
Hier haben wir nach der Operation, die Kanten einfügt, einen fold()
Schritt eingefügt. FOLLOWED Das Ergebnis ist ein einzelner Traverser, sodass die nachfolgende Operation nur einmal ausgeführt wird.
Dieser Ansatz hat den Nachteil, dass die Abfrage jetzt nicht vollständig optimiert ist, da fold()
nicht optimiert ist. Der folgende Einfügevorgang, der auf fold()
folgt, wird jetzt nicht optimiert.
Wenn Sie die fold()
verwenden müssen, um die Zahl der Traverser für nachfolgende Schritte zu reduzieren, ordnen Sie die Operationen so an, dass die kostengünstigsten Operationen den nicht optimierten Teil der Abfrage einnehmen.
Upserts mit Änderung vorhandener Eckpunkte und Kanten
Manchmal möchten Sie vielleicht noch nicht vorhandene Eckpunkte oder Kanten erstellen und dann eine Eigenschaft hinzufügen oder aktualisieren, unabhängig davon, ob es sich um neue oder vorhandene Eckpunkte oder Kanten handelt.
Um eine Eigenschaft hinzuzufügen oder zu ändern, verwenden Sie den Schritt property()
. Verwenden Sie diesen Schritt außerhalb des Schritts coalesce()
. Wenn Sie versuchen, eine Eigenschaft vorhandener Eckpunkte oder Kanten innerhalb des Schritts coalesce()
zu ändern, wird die Abfrage möglicherweise nicht von der Neptune-Abfrage-Engine optimiert.
Die folgende Abfrage fügt jedem Upsert-Eckpunkt eine Zählereigenschaft hinzu oder aktualisiert diese. Jeder property()
-Schritt besitzt eine einfache Kardinalität, um sicherzustellen, dass die neuen Werte alle vorhandenen Werte ersetzen und nicht zu einer Gruppe vorhandener Werte hinzugefügt werden.
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .property(single, 'counter', 1) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .property(single, 'counter', 2) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .property(single, 'counter', 3) .id()
Wenn es einen Eigenschaftswert gibt, z. B. den Zeitstempelwert lastUpdated
, der für alle Upsert-Elemente gilt, können Sie ihn am Ende der Abfrage hinzufügen oder aktualisieren:
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .V('v-2'). .fold(). .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .V('v-1', 'v-2', 'v-3') .property(single, 'lastUpdated', datetime('2020-02-08')) .id()
Wenn es zusätzliche Bedingungen gibt, die bestimmen, ob ein Eckpunkt oder eine Kante weiter geändert werden sollte oder nicht, können Sie mit einem has()
-Schritt die Elemente filtern, die geändert werden sollen. Das folgende Beispiel verwendet einen has()
-Schritt, um Upsert-Eckpunkte basierend auf dem Wert ihrer version
-Eigenschaft zu filtern. Die Abfrage aktualisiert dann für jeden Eckpunkt, dessen Wert für version
kleiner als 3 ist, auf version
:
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org') .property('version', 3)) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org') .property('version', 3)) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org') .property('version', 3)) .V('v-1', 'v-2', 'v-3') .has('version', lt(3)) .property(single, 'version', 3) .id()