Skip to content

Problem with two-way relationship #2177

Closed
@mdelmoral

Description

@mdelmoral

Hi, I'm a pretty novice in Neo4J and SDN and I'm developing a followers/likes system.
My pom.xml has SDN 6.0.5

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

My User node is:

@Node("User")
@Data
@NoArgsConstructor
@EqualsAndHashCode(exclude = {"likes", "hates"})
public class User {

    @Id
    private String name;

    @Relationship(type = "LIKES")
    private Set<User> likes;

    @Relationship(type = "HATES")
    private Set<User> hates;

    @Version
    private Long version;

    @CreatedDate
    private Instant createdAt;

    @LastModifiedDate
    private Instant lastModified;

    public User(String name) {
        this.name = name;
        this.likes = new HashSet<>();
        this.hates = new HashSet<>();
    }

    public void liking(User user) {
        if (likes == null) {
            this.likes = new HashSet<>();
        }
        this.likes.add(user);
    }
}

The method to create the "likes/follows" is:

@Transactional
public void setLikes(String userA, String userB) {
        final User userThatLikes = userRepository.findById(userA).orElseThrow();
        final User userLiked = userRepository.findById(userB).orElseThrow();

        userThatLikes.liking(userLiked);

        userRepository.save(userThatLikes);
}

I have only two users for this test: AAAA and BBBB
If AAAA likes BBBB the relationship is created well (AAAA - LIKES -> BBBB) ✅
The save method traces are:

2021-03-12 10:43:48.342 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`User`) WHERE hlp.name = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`User`) SET n = $__properties__ RETURN id(n) UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__) SET n += $__properties__ RETURN id(n)
2021-03-12 10:43:48.342 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__id__: "AAAA", __version__: 0, __properties__: {createdAt: 2021-03-12T09:43:07.292113Z, name: "AAAA", lastModified: 2021-03-12T09:43:48.340836Z, version: 1}}
2021-03-12 10:43:48.389 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`LIKES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:43:48.389 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "AAAA"}
2021-03-12 10:43:48.396 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`User`) WHERE hlp.name = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`User`) SET n = $__properties__ RETURN id(n) UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__) SET n += $__properties__ RETURN id(n)
2021-03-12 10:43:48.396 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__id__: "BBBB", __version__: 0, __properties__: {createdAt: 2021-03-12T09:43:11.659346Z, name: "BBBB", lastModified: 2021-03-12T09:43:48.392363Z, version: 1}}
2021-03-12 10:43:48.437 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId MERGE (startNode)-[relProps:`LIKES`]->(endNode) RETURN id(relProps)
2021-03-12 10:43:48.438 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {toId: 39352, fromId: "AAAA"}
2021-03-12 10:43:48.441 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`LIKES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:43:48.442 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "BBBB"}
2021-03-12 10:43:48.444 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`HATES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:43:48.444 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "BBBB"}
2021-03-12 10:43:48.465 DEBUG 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`HATES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:43:48.465 TRACE 11221 --- [nio-8080-exec-9] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "AAAA"}

But now, if BBBB tries to like to AAAA (BBBB - LIKES -> AAAA) ❌ , I get the next error:

2021-03-12 10:45:07.676 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`User`) WHERE hlp.name = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`User`) SET n = $__properties__ RETURN id(n) UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__) SET n += $__properties__ RETURN id(n)
2021-03-12 10:45:07.676 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {__id__: "BBBB", __version__: 1, __properties__: {createdAt: 2021-03-12T09:43:11.659346Z, name: "BBBB", lastModified: 2021-03-12T09:45:07.674080Z, version: 2}}
2021-03-12 10:45:07.724 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`LIKES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:45:07.724 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "BBBB"}
2021-03-12 10:45:07.733 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`User`) WHERE hlp.name = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`User`) SET n = $__properties__ RETURN id(n) UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__) SET n += $__properties__ RETURN id(n)
2021-03-12 10:45:07.734 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {__id__: "AAAA", __version__: 1, __properties__: {createdAt: 2021-03-12T09:43:07.292113Z, name: "AAAA", lastModified: 2021-03-12T09:45:07.732774Z, version: 2}}
2021-03-12 10:45:07.774 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`) WHERE startNode.name = $fromId MATCH (endNode) WHERE id(endNode) = $toId MERGE (startNode)-[relProps:`LIKES`]->(endNode) RETURN id(relProps)
2021-03-12 10:45:07.775 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {toId: 39354, fromId: "BBBB"}
2021-03-12 10:45:07.780 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
MATCH (startNode:`User`)-[rel:`LIKES`]->(:`User`) WHERE (startNode.name = $fromId AND NOT (id(rel) IN $__knownRelationShipIds__)) DELETE rel
2021-03-12 10:45:07.780 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {__knownRelationShipIds__: [], fromId: "AAAA"}
2021-03-12 10:45:07.783 DEBUG 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : Executing:
OPTIONAL MATCH (hlp:`User`) WHERE hlp.name = $__id__ WITH hlp WHERE hlp IS NULL CREATE (n:`User`) SET n = $__properties__ RETURN id(n) UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__) SET n += $__properties__ RETURN id(n)
2021-03-12 10:45:07.784 TRACE 11221 --- [nio-8080-exec-1] org.springframework.data.neo4j.cypher    : with parameters:
:params {__id__: "BBBB", __version__: 1, __properties__: {createdAt: 2021-03-12T09:43:11.659346Z, name: "BBBB", lastModified: 2021-03-12T09:45:07.782973Z, version: 2}}
2021-03-12 10:45:07.851 ERROR 11221 --- [nio-8080-exec-1] o.s.t.i.TransactionInterceptor           : Application exception overridden by rollback exception

org.springframework.dao.OptimisticLockingFailureException: An entity with the required version does not exist.
	at org.springframework.data.neo4j.core.Neo4jTemplate.saveRelatedNode(Neo4jTemplate.java:567) ~[spring-data-neo4j-6.0.5.jar:6.0.5]
	at org.springframework.data.neo4j.core.Neo4jTemplate.lambda$processNestedRelations$16(Neo4jTemplate.java:520) ~[spring-data-neo4j-6.0.5.jar:6.0.5]
	at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:387) ~[spring-data-commons-2.4.5.jar:2.4.5]

Apart from the version problem, I find the ...UNION MATCH (n) WHERE (n.name = $__id__ AND n.version = $__version__)... quite strange because if my DB has other different nodes (Posts, Groups...) with name property they will take into account in the query. Wouldn't it be better something like ...UNION MATCH (n:User) WHERE...?

Maybe I mapped wrongly the two directions LIKES relationship but I don't understand why it works the first like and doesn't in the second in opposite direction.

If you need further information, please, let me know.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions