Re: (C# Wrapper) Transférer relation d'un node A à un node B

30 views
Skip to first unread message
Message has been deleted
Message has been deleted

Benoit Simard

unread,
Apr 14, 2016, 9:36:28 AM4/14/16
to neo...@googlegroups.com
Bonjour,

Une petite quesion en passant, pourquoi ne pas fusionner A dans ou B (ou inversement) au lieu de créer un noeud de fusion ?
Ils ont des propriétés en commun ou pas ?

Sincèrement.

Le 14/04/2016 15:32, Bastien Debret a écrit :

Hello,


j'utilise la librairie .NET


voici mon use-case:


J'ai un node A et B qui doivent être fusionnés car représentant la même entité.


Je crée donc un node C, puis copie les valeurs des nodes A & B dans C. J'aimerais ensuite copier (ou déplacer) toutes les relations pointant ou partant des nodes A & B vers C pour compléter cette fusion, et ce, dynamiquement, puisque ne sachant pas exactement quelles types de relations pointes sur A & B. 


(a:Label)-[r]-()
(b:Label)-[r2]-()
(c:Label)

J'ai essayer différentes stratégies sans succès, tel que:


        using (GraphClient graphClient = GetGraphClient())
        {
            var inputString = string.Format("({0}:{1})-[r]->(n), ({2}:{3})", "a", typeof(Label).Name, "c", typeof(Label).Name);
            var query = graphClient.Cypher
                    .SendQueryOnMaster()
                    .Match(inputString)
                    .Where((Label a) => a.Id == from.Id)
                    .AndWhere((Label c) => c.Id == to.Id)
                    .Create("(c)-[r2:type(r)]->(n)");
            query.ExecuteWithoutResults();
        }


Y-aurait-il une façon ou un moyen détourner de réaliser cette copie ?


Merci d'avance, Bastien.


Benoit Simard

unread,
Apr 14, 2016, 10:41:09 AM4/14/16
to neo...@googlegroups.com
Yop,

Voici  mon dataset de test :

----
CREATE (a:Test {props1:"1", props2:"2"})
CREATE (a)-[:LINKED_TO {props1:1}]->(:Other {id:1})
CREATE (a)-[:LINKED_TO {props2:2}]->(:Other {id:2})

CREATE (b:Test {props3:"3", props4:"2"})
CREATE (b)-[:LINKED_TO {props3:3}]->(:Other {id:3})
CREATE (b)-[:LINKED_TO {props4:4}]->(:Other {id:4})
----

Et les requêtes pour la fusion de B dans A :

Requête de fusion
----
MATCH    
    (a:Test {props1:"1"}),
    (b:Test {props3:"3"})
SET a += b
WITH a,b
  MATCH (b)-[r:LINKED_TO]->(c)
  WITH COLLECT(r) AS rels, a, b, c
    FOREACH (rel in rels |
        CREATE (a)-[r:LINKED_TO]->(c)
        set r+=rel
    )
----

Suppression du noeud B
----
MATCH  (b:Test {props3:"3"}) WHERE not "props1" IN keys(b) DETACH DELETE b
----

Ici cela fonctionne puisque la relation est nommée (il n'existe pas moyen de variabiliser le type de la relation dans le create).
Soit on change la query avec des optional match, soit on fait une boucle for dans le code sur les relations.

Après pour l'integration avec le driver .NET je vous laisse voir ^^

Sincèrement.


Le 14/04/2016 15:33, Bastien Debret a écrit :
Petite précision, j'utilise la version: 2.3.2 - ENTERPRISE de Neo4J
Message has been deleted

Benoit Simard

unread,
Apr 15, 2016, 4:19:02 AM4/15/16
to neo...@googlegroups.com
Re,

Oui c'est bien une boucle FOR en C# dont je parlais, et pour l'OPTIONAL MATCH, vous avez raison il faudra changer la query à chaque ajout de type.
Donc pour l'instant, il y a des limitations.

Avec l'arrivée de la version 3.0 et des procédures, cela change la donne.
Il existe déjà des procédures pour faire du refactoring dans le graph : https://github.com/jexp/neo4j-apoc-procedures/#graph-refactoring

A+


Le 15/04/2016 10:01, Bastien Debret a écrit :
Bonjour Benoît,

merci pour votre réponse rapide,

A & B ont les mêmes propriétés, avec des valeurs différentes, et la fusion peut séléctionner aussi bien des champs de A que des champs de B dépendant de leur valeur, mais effectivement, on pourrait choisir un des deux nodes et mettre à jour les propriétés qui doivent l'être avec les propriétés du second node.

Par rapport à la variabilisation du type, le but était d'exécuter une seule requête Cypher (contrairement au FOR) pour des soucis d'optimisation (Latence), et l'OPTIONAL MATCH nécessiterait une mise à jour de la requête cypher à chaque fois qu'un nouveau type de relation serait rajoutée, pas idéal côté maintenabilité ^^

Quand vous parlez de la boucle FOR vous parlez bien d'une boucle FOR C# qui itèrerait la fonction copie de relation sur l'ensemble des noms de relations ?

Cordialement, Bastien.
Message has been deleted
Message has been deleted
Message has been deleted

Benoit Simard

unread,
Apr 15, 2016, 7:08:49 AM4/15/16
to neo...@googlegroups.com
Bravo ! Et merci pour le partage :D

Le 15/04/2016 13:03, Bastien Debret a écrit :
Petite update, ça fonctionne nickel, voici mon code C# si jamais ça peut aider des gens dans le futur:

        internal static void DuplicateRelationships<T>(T from, T to) where T : IDataObject
        {
            string aVariable = "a";
            string bVariable = "b";
            string nVariable = "n";
            string relationVariable = "r";
            string newRelation = "r2";
            string relationsVariable = "rels";
            string relationPostCollectVariable = "rel";

            Guid fromId = from.Id;
            Guid toId = to.Id;

            foreach (string relation in CypherVerbs.GetAllVerbs())
            {
                using (GraphClient graphClient = GetGraphClient())
                {
                    /*-[r]->*/
                    graphClient.Cypher
                        .SendQueryOnMaster()
                        .Match(string.Format("({0}:{1})-[{2}:{3}]->({4}), ({5}:{6})", aVariable, from.GetType().Name, relationVariable, relation, nVariable, bVariable, to.GetType().Name))
                        .Where((T a) => a.Id == fromId)
                        .AndWhere((T b) => b.Id == toId)
                        .With(string.Format("COLLECT({0}) AS {1}, {2}, {3}, {4}", relationVariable, relationsVariable, aVariable, bVariable, nVariable))
                        .ForEach(string.Format("({0} in {1} | ", relationPostCollectVariable, relationsVariable))
                        .Create(string.Format("({0})-[{1}:{2}]->({3})", bVariable, newRelation, relation, nVariable))
                        .Set(string.Format("{0} += {1})", newRelation, relationPostCollectVariable))
                        .ExecuteWithoutResults();

                    /*<-[r]-*/
                    graphClient.Cypher
                        .SendQueryOnMaster()
                        .Match(string.Format("({0}:{1})<-[{2}:{3}]-({4}), ({5}:{6})", aVariable, from.GetType().Name, relationVariable, relation, nVariable, bVariable, to.GetType().Name))
                        .Where((T a) => a.Id == fromId)
                        .AndWhere((T b) => b.Id == toId)
                        .With(string.Format("COLLECT({0}) AS {1}, {2}, {3}, {4}", relationVariable, relationsVariable, aVariable, bVariable, nVariable))
                        .ForEach(string.Format("({0} IN {1} | ", relationPostCollectVariable, relationsVariable))
                        .Create(string.Format("({0})<-[{1}:{2}]-({3})", bVariable, newRelation, relation, nVariable))
                        .Set(string.Format("{0} += {1})", newRelation, relationPostCollectVariable))
                        .ExecuteWithoutResults();
                }
            }
        }

Le vendredi 15 avril 2016 11:21:46 UTC+2, Bastien Debret a écrit :
Super, merci à vous pour le support, effectivement la release 3.0 offre pas mal de possibilités et va repondre à mon use-case ;)

Je vais faire les tests du FOR C#
Reply all
Reply to author
Forward
0 new messages