2017-10-04 1 views
2

Ich bin neu bei Neo4j und da muss etwas sein, was ich nicht verstehe.Neo4j, Massenladung mit Cypher-Befehlen

Ich habe viele Objekte in Java und möchte sie verwenden, um ein Neo4j-Diagramm mit dem Java-Treiber und Cypher zu füllen. Mein Code funktioniert wie folgt:

// nodes 
for (Person person: persons) 
    session.run (String.format ( 
    "CREATE (:Person { id: '%s', name: \"%s\", surname: \"%s\" })", 
    person.getId(), person.getName(), person.getSurname() 
)); 

// relations 
session.run ("CREATE INDEX ON :Person(id)"); 

for (Friendship friendship: friendships) 
    session.run (String.format ( 
    "MATCH (from:Person { id: '%s' }), (to:Person { id: '%s' })\n" + 
    "CREATE (from)-:KNOWS->(to)\n", 
    friendship.getFrom().getId(), 
    friendship.getTo().getId() 
)); 

(in der Tat, es ist etwas komplizierter, weil ich ein Dutzend Knotentypen und etwa die gleiche Anzahl von Beziehungstypen haben).

Jetzt ist das sehr langsam, wie mehr als 1 Stunde, 300k Knoten und 1M Relationen zu laden (auf einem ziemlich schnellen MacBookPro, mit Neo4j, das 12/16 GB RAM nimmt).

Mache ich es falsch? Sollte ich stattdessen die batch inserter verwenden? (Ich würde es vorziehen, auf die graphDB über das Netzwerk zugreifen zu können). Würde ich etwas gewinnen, wenn ich mehr Einfügungen in eine Transaktion zusammenfasse? (Aus der Dokumentation geht hervor, dass Transaktionen nur für das Zurückrollen und für Isolierungszwecke nützlich sind).

Antwort

0

Ich komme von Neo4j in Python, aber ich denke, das Problem hier ist mit Ihren Cypher-Befehlen. Ich habe zwei Vorschläge.

Es kann schneller sein, Kanten separat anzupassen. Auf meinem primitiven Benchmark ich einen Unterschied von 24 ms vs 15ms mit diesem (siehe EDIT: Dieser Benchmark ist zweifelhaft):

MATCH (from:Person { id: '%s' }) 
MATCH (to:Person { id: '%s' }) 
CREATE (from)-:KNOWS->(to) 

Eine weitere Option ist UNWIND zu verwenden. Ich benutze dies mit der BOLT-Schnittstelle, um weniger Transaktionen zu senden, aber ohne den Batch-Inserter zu verwenden. Verzeihen Sie die Python-Implementierung, die ich hier kopiere, und hoffentlich können Sie diese zusammen mit den Javascript Neo4j Treiberdokumenten betrachten, um sie zu konvertieren.

payload = {"list":[{"a":"Name1","b":"Name2"},{"a":"Name3","b":"Name4"}]} 

statement = "UNWIND {list} AS d " 
statement += "MATCH (A:Person {name: d.a}) " 
statement += "MATCH (B:Person {name: d.b}) " 
statement += "MERGE (A)-[:KNOWS]-(B) " 

tx = session.begin_transaction() 
tx.run(statement,payload) 
tx.commit() 
+0

Danke, aber ich denke nicht, dass es in meinem Fall funktionieren würde. 24-15ms sind nicht viel anders, wenn man bedenkt, dass meine Anwendung das interne Diagramm (unter Verwendung von Hash-Maps) in weniger als 3 Minuten auffüllt, während Neo4j ewig braucht, um das gleiche zu tun. Das kann nicht stimmen, es sollte mehr oder weniger das Gleiche sein. Wie für WIND, ich denke, senden eine Liste als Parameter würde schließlich in eine zu große Abfrage, in Anbetracht der Tatsache, dass ich so viele Knoten und Kanten haben. – zakmck

+0

Mein Benchmark gibt es vielleicht nicht, aber ich schlage vor, zumindest UNWIND zu versuchen. Deine Payload-Liste muss nicht * all * deine Daten sein, du könntest sie ausschneiden (wie ich es in der Praxis tue, nicht im obigen Beispielcode). Wenn die Nutzlast eine Größe von ~ 100k hat und Sie Ihre Kanten mit N Transaktionen füllen können, wird das eine Menge Zeit im Vergleich zu N * 100k einzelnen Transaktionen sparen. – sjc

+0

danke @sjc, ich sehe den Punkt auf UNWIND, ich werde es versuchen. – zakmck

0

Ich denke, es ist es wert, meine Erfahrung zu diesem Thema zu melden.

Ich habe den @ sjc Vorschlag gefolgt und mit UNWIND versucht. Das war jedoch nicht so einfach, weil Cypher Ihnen keine parametrisierten Knotenbeschriftungen oder Relationstypen zulässt (und ich habe ein Dutzend Labels und Relationstypen). Aber schließlich war ich in der Lage, alle möglichen Typen zu durchlaufen und genug Gegenstände (ungefähr 1000) an jeden UNWIND-Block zu senden.

Der Code mit UNWIND ist viel schneller, aber nicht schnell genug, meiner Meinung nach (sollte OK sein auf einem anständigen PC und mit wenigen Millionen Knoten, nicht sehr gut mit Hunderten von Millionen von Knoten oder mehr).

Die Inserter-Komponente ist viel schneller (einige Sekunden zum Hochladen 1-2 Millionen Knoten), obwohl es erfordert, um den HTTP-Zugriff zu senken, und ich hatte eine Menge Probleme mit seiner Abhängigkeit von Lucene 5.4, weil ich brauche innerhalb einer Anwendung (die Daten erzeugt) zu verwenden, die Lucene 6 verwendet, und schreckliche Dinge passierten, als ich versuchte, einfach 5.4 mit 6 im Klassenpfad zu tauschen. Ich habe um diese there is some mechanism to make this possible, aber es scheint nicht einfach und ist sicherlich nicht so gut dokumentiert.

Ich habe definitiv nicht alle solche Probleme erwartet, um solch eine grundlegende Operation effizient auszuführen.