2017-10-04 2 views
2

Je suis nouveau sur Neo4j et il doit y avoir quelque chose que je ne comprends pas sur les bases.Neo4j, chargement en masse avec commandes Cypher

J'ai beaucoup d'objets en Java et je veux les utiliser pour remplir un graphique Neo4j, en utilisant le pilote Java et Cypher. Mon code fonctionne comme ceci:

// 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() 
)); 

(en effet, il est un peu plus compliqué, parce que j'ai une douzaine de types de nœuds et environ le même nombre de types de relation). Maintenant, c'est très lent, comme plus de 1 heure pour charger 300k nœuds et 1M relations (sur un MacBookPro assez rapide, avec Neo4j prenant 12/16GB de RAM).

Suis-je dans le mauvais sens? Devrais-je utiliser le batch inserter à la place? (Je préférerais pouvoir accéder au graphDB via le réseau). Est-ce que je gagnerais quelque chose en regroupant plus d'insertions dans une transaction? (De la documentation, il semble que les transactions ne sont utiles que pour les besoins de retour en arrière et d'isolation).

Répondre

0

Je viens de Neo4j en Python, mais je pense que la question est ici avec vos commandes Cypher. J'ai deux suggestions.

Il peut être plus rapide de faire correspondre les bords séparément. Sur mon point de repère primitif, je vois une différence de 24 ms vs 15ms avec ce (EDIT: Cette référence est douteuse):

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

Une autre option consiste à utiliser UNWIND. Je l'utilise avec l'interface BOLT pour envoyer moins de transactions mais sans utiliser le Batch Inserter. Pardonnez l'implémentation de Python que je copie ici, et j'espère que vous pouvez regarder ceci avec les docs de Javascript Neo4j Driver pour le convertir.

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

Merci, mais je ne pense pas que ça marcherait dans mon cas. 24-15ms ne sont pas très différents, étant donné que mon application est de remplir son graphique interne (en utilisant des cartes de hachage) en moins de 3 minutes, alors que Neo4j prend autant de temps à faire de même. Cela ne peut pas être juste, cela devrait prendre plus ou moins la même chose. En ce qui concerne WIND, je pense que l'envoi d'une liste en tant que paramètre finirait par devenir une requête trop importante, étant donné que j'ai tellement de nœuds et d'arêtes. – zakmck

+0

Mon benchmark est peut-être éteint, mais je suggère fortement au moins d'essayer UNWIND. Votre liste de charge utile ne doit pas nécessairement être * toutes * vos données, vous pouvez les découper (comme je le fais dans la pratique, pas dans le code ci-dessus). Si la charge utile est de taille ~ 100k et que vous pouvez remplir vos bords avec N transactions, cela vous fera gagner beaucoup de temps par rapport aux transactions individuelles N * 100k. – sjc

+0

merci @sjc, je vois le point sur UNWIND, je vais essayer. – zakmck

0

Je pense que cela vaut la peine de signaler mon expérience à ce sujet.

J'ai suivi la suggestion @sjc et essayé avec UNWIND. Cependant, ce n'était pas si simple, car Cypher ne vous permet pas de définir des étiquettes de nœuds ou des types de relations (et j'ai une douzaine d'étiquettes et de types de relations). Mais finalement, j'ai été capable de boucler tous les types possibles et d'envoyer assez d'éléments (environ 1000) à chaque morceau UNWIND. Le code utilisant UNWIND est beaucoup plus rapide, mais pas assez rapide, à mon avis (devrait être OK sur un PC décent et avec quelques millions de nœuds, pas très bon avec des centaines de millions de nœuds, ou plus). Le composant d'insertion est beaucoup plus rapide (quelques secondes pour télécharger 1 à 2 millions de nœuds), bien qu'il nécessite de réduire l'accès HTTP et j'ai eu beaucoup de problèmes avec sa dépendance à Lucene 5.4, parce que j'ai besoin utiliser à l'intérieur d'une application (qui produit des données) qui utilise Lucene 6, et des choses terribles se sont produites lorsque j'ai essayé d'échanger simplement 5.4 avec 6 dans le classpath. J'ai autour de cela there is some mechanism to make this possible, mais il ne semble pas facile et n'est certainement pas si bien documenté.

Je certainement ne pas attendre à tous ces problèmes pour l'exécution d'une telle opération efficace de base.