0

I am using Spring Data Neo4j 4 . Lets say I have nodes of type Person and the corresponding Java domain object is

 @NodeEntity
 public class Person {

     @GraphId private Long id;

     private String name;

     private String lastName;

     @Relationship(type = "KNOWS")
     Set<Person> myTypes;


 }

And I am trying to create a graph like `

John-KNOWS->George and John-KNOWS->Ann. Ann-KNOWS->Dug

`. Below is how I am setting up the domain objects and persisting.

    Person john = new Person("John","P");
    Person george = new Person("George","B");
    Person Ann = new Person("Ann","M");
    Person Dug = new Person("Dug","S");
    Set<Person> Persons= new HashSet<Person>();
    Set<Person> Persons1= new HashSet<Person>();
    Persons1.add(Dug);
    Ann.setMyTypes(Persons1);
    Persons.add(george);
    Persons.add(Ann);
    john.setMyTypes(Persons);

    personRepository.save(john);
    personRepository.save(george);
    personRepository.save(Ann);
    personRepository.save(Dug);

But to my surprise only the nodes are created without relationships . I was debugging this and after this line personRepository.save(john) The nodes with relationships got created but during subsequent saves all the relationships got deleted. Below is what is printed in the logs and clearly says that relationships are deleted.

 Request: UNWIND {rows} as row CREATE (n:`Person`) SET n=row.props RETURN   row.nodeRef as ref, ID(n) as id, row.type as type with params {rows=[{nodeRef=-2115049587, type=node, props={name=George, attrib=B}}, {nodeRef=-341599918, type=node, props={name=Ann, attrib=M}}, {nodeRef=-737678933, type=node, props={name=John, attrib=P}}, {nodeRef=-1025122203, type=node, props={name=Dug, attrib=S}}]}
 Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`KNOWS`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type with params {rows=[{relRef=-1807300324, endNodeId=2448, type=rel, startNodeId=2450}, {relRef=-272508006, endNodeId=2451, type=rel, startNodeId=2449}, {relRef=-845107952, endNodeId=2449, type=rel, startNodeId=2450}]}
 Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MATCH (startNode)-[rel:`KNOWS`]->(endNode) DELETE rel with params {rows=[{endNodeId=2448, startNodeId=2450}]}
 Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MATCH (startNode)-[rel:`KNOWS`]->(endNode) DELETE rel with params {rows=[{endNodeId=2449, startNodeId=2450}]}
 Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MATCH (startNode)-[rel:`KNOWS`]->(endNode) DELETE rel with params {rows=[{endNodeId=2451, startNodeId=2449}]}

Not sure where I am going wrong or this is how it is supposed to work

Luanne
  • 19,145
  • 1
  • 39
  • 51
Manu Bhat
  • 135
  • 2
  • 8

2 Answers2

2

This is a case when the relationships are ambiguous for the OGM because they exist between the same type but in a specific direction.

(john)-[:KNOWS]->(george) also implies that George should have an incoming relationship from John, but the domain model does not reflect this. On subsequent saves, as you've noticed, relationships are deleted because the mapper is unable to figure out what to keep because of the ambiguity.

When you have these sort of relationships between entities of the same type, being explicit about the relationship direction is the solution. In your case, the relationships from one person to another are always in the outgoing direction, so adding the direction like this makes it clear that the OGM is not to worry about incoming relationships of the same type.

@Relationship(type = "KNOWS", direction = "OUTGOING")
Set<Person> myTypes;

Also, you don't have to save every person- in your example, saving John is sufficient because the OGM can traverse from John to George and Ann and hence to Dug.

Luanne
  • 19,145
  • 1
  • 39
  • 51
  • Thanks. But above is only a simple use case. What I am up to , is importing data from a huge excel file and if I have to figure out what to save and what not to save , in itself becomes a huge task. Secondly, I do not know the relationship direction before hand in many of the cases. Is there a way I can specify the direction during run time ? Below post says we cannot http://stackoverflow.com/questions/18517000/dynamicrelationshiptype-in-spring-data-neo4j-or-defining-relationship-types-at-r – Manu Bhat Jul 06 '16 at 05:59
  • 1
    No, you can't specify directions at runtime with SDN/OGM. The OGM may not be the right tool for the job i.e. importing from excel – Luanne Jul 06 '16 at 06:05
0

I think the problem is with the way you are saving the Person objects. One solution would be to save the person objects first. Then add the relationships and resave the only the entities in which you have added the relationships and not the added entities.

For Example

    Person john = new Person("John","P");
    Person Ann = new Person("Ann","M");

    Set<Person> Persons= new HashSet<Person>();
    Persons.add(Ann);
    john.setMyTypes(Persons);
    personRepository.save(john);

The above will save both John and Ann as well as will create a relationship of "KNOWS" between them. No need to save Ann again.

If you try to save Ann again, after the above code

personRepository.save(Ann); 

the relationship will get deleted as neo4j will see it as an entity which does not have any relationship as the relationship was not there when Ann object was created earlier

Soumya
  • 1,833
  • 5
  • 34
  • 45