Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
04-13-2022 10:50 AM
Hello. I am implementing a basic node - [relationship] - node, using a NameEntity class, which has a Relationship(type="LINK", direction = INCOMING) annotation.
The Link class has a TargetNode NameEntity.
I have a unit test which creates three nodes, with two relationships. The first time I run the unit test, the nodes and relationships are created:
The second time I run the unit test, duplicate relationships are being created:
I'm pretty new to neo4j (i'm using the community version 4.4.4).
I'm not expecting the duplicate second relationship to be created (with the same Link type). I appreciate I am not using a Version property.
Is it the default behaviour for neo4j to create a second relationship (with the same attributes). Is there a way for this second (duplicate) relationship to not be created?
I have attached copied NameEntity, and Link pojo, the unit test, and the Cypher which is run (first and second) time I run the unit test.
Any help would be appreciated.
Thanks
Miles.
public interface NameRepository extends ReactiveNeo4jRepository<NameEntity, String>
@Node("Name")
public class NameEntity {
@Id
private String name;
private String type;
@Relationship(type = "LINK", direction = INCOMING)
private Set<Link> nameLinks;
public NameEntity(final String name, final String type) {
this.name = name;
this.type = type;
}
public NameEntity() {}
@RelationshipProperties
@Getter
public class Link {
@RelationshipId
private Long id;
private String type;
@TargetNode
private NameEntity nameEntity;
public Link(NameEntity nameEntity, String type) {
this.nameEntity = nameEntity;
this.type = type;
}
public Link() {}
2022-04-13 18:31:33.243 DEBUG 25444 --- [o-auto-1-exec-1] .d.n.c.t.ReactiveNeo4jTransactionManager : Creating new transaction with name [org.springframework.data.neo4j.repository.support.SimpleReactiveNeo4jRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2022-04-13 18:31:33.464 WARN 25444 --- [o4jDriverIO-2-2] o.s.d.n.c.m.DefaultNeo4jIsNewStrategy : Instances of class com.chocksaway.neo4j.entity.NameEntity with an assigned id will always be treated as new without version property!
2022-04-13 18:31:33.529 DEBUG 25444 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:31:33.639 DEBUG 25444 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:31:33.655 DEBUG 25444 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MATCH (startNode:`Name`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId CREATE (startNode)<-[relProps:`LINK`]-(endNode) SET relProps += $__properties__ RETURN id(relProps)
2022-04-13 18:31:33.663 DEBUG 25444 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:31:33.669 DEBUG 25444 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MATCH (startNode:`Name`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId CREATE (startNode)<-[relProps:`LINK`]-(endNode) SET relProps += $__properties__ RETURN id(relProps)
2022-04-13 18:31:33.682 DEBUG 25444 --- [o4jDriverIO-2-2] .d.n.c.t.ReactiveNeo4jTransactionManager : Initiating transaction commit
2022-04-13 18:31:33.762 INFO 25444 --- [ionShutdownHook] o.neo4j.driver.internal.InternalDriver : Closing driver instance 2107393518
2022-04-13 18:31:33.764 INFO 25444 --- [ionShutdownHook] o.n.d.i.async.pool.ConnectionPoolImpl : Closing connection pool towards localhost:7687
2022-04-13 18:33:44.751 DEBUG 16572 --- [o-auto-1-exec-1] .d.n.c.t.ReactiveNeo4jTransactionManager : Creating new transaction with name [org.springframework.data.neo4j.repository.support.SimpleReactiveNeo4jRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2022-04-13 18:33:44.981 WARN 16572 --- [o4jDriverIO-2-2] o.s.d.n.c.m.DefaultNeo4jIsNewStrategy : Instances of class com.chocksaway.neo4j.entity.NameEntity with an assigned id will always be treated as new without version property!
2022-04-13 18:33:45.044 DEBUG 16572 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:33:45.143 DEBUG 16572 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:33:45.160 DEBUG 16572 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MATCH (startNode:`Name`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId CREATE (startNode)<-[relProps:`LINK`]-(endNode) SET relProps += $__properties__ RETURN id(relProps)
2022-04-13 18:33:45.168 DEBUG 16572 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MERGE (nameEntity:`Name` {name: $__id__}) SET nameEntity += $__properties__ RETURN nameEntity
2022-04-13 18:33:45.185 DEBUG 16572 --- [o4jDriverIO-2-2] org.springframework.data.neo4j.cypher : Executing:
MATCH (startNode:`Name`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId CREATE (startNode)<-[relProps:`LINK`]-(endNode) SET relProps += $__properties__ RETURN id(relProps)
2022-04-13 18:33:45.211 DEBUG 16572 --- [o4jDriverIO-2-2] .d.n.c.t.ReactiveNeo4jTransactionManager : Initiating transaction commit
2022-04-13 18:33:45.290 INFO 16572 --- [ionShutdownHook] o.neo4j.driver.internal.InternalDriver : Closing driver instance 1728266914
2022-04-13 18:33:45.293 INFO 16572 --- [ionShutdownHook] o.n.d.i.async.pool.ConnectionPoolImpl : Closing connection pool towards localhost:7687
@Test
public void testAddName() throws URISyntaxException {
RestTemplate restTemplate = new RestTemplate();
final String baseUrl = "http://localhost:"+randomServerPort+"/name";
final Link nameEntity1 = new Link(new NameEntity("name001", "Person"), "Link");
final Link nameEntity2 = new Link(new NameEntity("name002", "Person"), "Link");
final NameEntity bob = new NameEntity("Bob", "Person");
bob.setNameLinks(Set.of(nameEntity1, nameEntity2));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
final ResponseEntity<String> response = restTemplate.postForEntity(baseUrl, bob, String.class);
Assertions.assertEquals(200, response.getStatusCodeValue());
Assertions.assertTrue(response.hasBody());
Assertions.assertTrue(response.getBody().contains("Bob"));
}
04-13-2022 09:05 PM
Have you implemented hashcode and equals method in Link class.Equals method has to be more detailed (verbose) on source node, target node and direction of link.
Please consider this aspect and review your class design.Data structure Set even though does not allow duplicates but be precise on hashcode and equals implementation so that you do not see what you don't expect to see.
Many thanks
Mr Sameer Sudhir G
04-14-2022 01:56 AM
Hi Sameer,
Thank you for your answer. Is there a concrete example for reference?
Thanks
Miles.
04-22-2022 06:38 AM
You already have the important bit in your code:
@RelationshipId
private Long id;
This has to match the previous existing one. Otherwise there is no chance for SDN to recognize it as something already existing.
Your use-case could also be that you want to create another Link
with the very same properties, which is a total valid operation.
Maybe you would need some custom merge logic in your "controller/service" that e.g. loads the right entities (NameEntity
) first, if they exist and decides if the "new" relationships are really new or you would just use the existing ones.
All the sessions of the conference are now available online