Description
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.