Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
05-03-2022 01:01 AM
Hello ! I'm having problems to update the relation property.
Given the :
public class SomeEntity {
@Id
@GeneratedValue
protected Long id;
@Property
protected String name;
@Relationship
protected Map<String, List<SomeLink>> relationships;
}
@RelationshipProperties
public class SomeLink {
@Id
@GeneratedValue
protected Long id;
@Property
protected boolean active = true;
@TargetNode
protected SomeEntity target;
}
@Transactional
public interface SomeEntityRepository extends Neo4jRepository<SomeEntity, Long> {
@Query("MATCH p=(n)-[*0..1]->(m) WHERE id(n)=$id RETURN n, collect(relationships(p)), collect(m);")
Optional<SomeEntity> findByIdWithLevelOneLinks(@Param("id") Long id);
}
As a result, we have the graphe like:
(n1:SomeEntity)-[r1:SomeLink]->(n2:SomeEntity)-[r2:SomeLink]->(n3:SomeEntity)-[...]->(...)
I want to update r1
, but r2
is removed if I use findByIdWithLevelOneLinks
in the repository. It is comprehensible.
I tried to use the projections as suggested in https://community.neo4j.com/t/relationships-eliminated-after-updating-node-properties/55117/4?u=pand...
public interface SomeEntityWithoutRelationship {
Long getId();
String getName();
}
public interface SomeEntityNodeWithOneLevelLinks {
Long getId();
String getName();
Map<String, List<SomeLinkWithoutTargetRelationships>> getRelationships();
}
public interface SomeLinkWithoutTargetRelationships {
Long getId();
boolean isActive();
SomeEntityWithoutRelationship getTarget();
}
Since projections only contains getters, I can't update the DTO that I retrieved. So I continue to use SomeEntity
in the repository and when I update the node, I tried to do a Projection projection = neo4jTemplate.saveAs(someEntityObject, SomeEntityNodeWithOneLevelLinks.class);
But r2
is still detached.
Please note that this is a simplified class. In our real class of SomeEntity
and SomeLink
, there are special setters and some composite properties. As a result, it is difficult to use Class-based Projections or Cypher-DSL.
I tried to solve this problem for several days but I still can't find a solution. Can someone help me ?
Thanks in advance for your response.
Solved! Go to Solution.
05-11-2022 10:23 AM
Alright. Nobody needs to downgrade There should be a 6.2.5-GH2533-2-SNAPSHOT
available now. There were more changes required than expected to make it work but I think it is now possible to do what you want. At least the example shows me the node properties and the related node because the projections points to the entity itself.
05-03-2022 11:47 PM
You are on the right track: Loading the entity as it is (without all relationships) and persisting it via the Neo4jTemplate#saveAs(entity, YourProjection.class)
should do the job.
Sorry to hear that this is not working for you as expected. I am happy to investigate this because it might be the combination of dynamic relationships and relationship properties. Could you provide a reproducer for the problem? Maybe I have overseen something but I could not make it fail in our test base.
05-04-2022 09:00 AM
Hi Gerrit !
Thanks for your confirmation that I'm not on the wrong way.
I have created a simple demo here: GitHub - Pandalei97/neo4jDemoBugRelationship
I defined hard coded examples in the controller to save time.
When I reproduce the problem, I found that it occurs when we use @GeneratedValue
. When we use hard coded user-defined ID, it doesn't seem to detach the relationship. I hope that can give you a clearer vision of the solution.
05-05-2022 05:25 AM
I think that I have found the problem in the code base. It was the dynamic relationship.
Created an issue to track Projection support for dynamic relationships · Issue #2533 · spring-projects/spring-data-neo4j · Git...
Also there is now a 6.2.5-GH-2533-SNAPSHOT
available to test. Would be helpful to get your feedback on this.
To use the snapshot, you have to add Spring's snapshot (and milestone) repository to your pom.
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
Thanks for the reproducer, it was really helpful.
05-06-2022 05:19 AM
Thanks a lot for the quick bugfix ! I have do several tests, it seems to work correctly now. I will do some more tests and give you a feedback these days.
05-06-2022 05:34 AM
Thanks for the feedback. No hurry, I am still working on the tests for this.
05-10-2022 07:54 AM
Hi Gerrit,
I have found another use case where SDN doesn't seem to work correctly when multiple dynamic relationships exists in a class.
I have updated the demo in GitHub - Pandalei97/neo4jDemoBugRelationship
The example is defined in the endpoint createAnotherExample
. In the demo we can see that the nodes aren't created correctly.
Thanks in advance for your feedback.
05-11-2022 12:35 AM
But you are only persisting it via neo4jTemplate.saveAs(n1, EntityWithOnlyOtherRelationshipProjection.class);
.
where EntityWithOnlyOtherRelationshipProjection
does only define the otherRelationships
.
This is the result I am looking at:
Changing EntityWithOnlyOtherRelationshipProjection
to extend EntityWithOneLevelRelationshipProjection
will result in:
05-11-2022 01:13 AM
Yes, that's exactly what I have. The target node of N1 is an empty node, which eventually leads to all links pointing to this same node.
Is it expected ?
05-11-2022 01:21 AM
Stupid me. Sorry, I have only looked at the mentioned problem with the relationship(s) and not the data in the target node itself. No, this should not happen, or I have missed something obvious.
I will have a look and keep you updated.
05-11-2022 02:46 AM
Status update
And now I know (again) why we initially did not support dynamic relationships in projections:
When it comes to the detection of the defined paths in the projection interface/object tree, we rely on PropertyPath
functionality of Spring Data commons. There is one limitation: It does take the Map
key but not the value as a valid part of the path. Adding projection.relationships.id
will throw an exception, if I enforce this because it already claims that relationships
is a String
and not the projection/entity class.
We have some kind of workaround this restriction in a related place in SDN and I am currently trying to replace the types. I cannot promise anything at this point and might rollback the already merged changes of GH-2533 and re-open it for later.
Better not supporting this feature at the moment instead of a broken/incomplete solution.
05-11-2022 02:59 AM
Thanks for the investigation. This means that projection should not be used when we have dynamic relationships ?
Is there another solution to avoid the detachment of the relationship in this case or should I downgrade to SDN 5 ?
05-11-2022 10:23 AM
Alright. Nobody needs to downgrade There should be a 6.2.5-GH2533-2-SNAPSHOT
available now. There were more changes required than expected to make it work but I think it is now possible to do what you want. At least the example shows me the node properties and the related node because the projections points to the entity itself.
05-11-2022 11:23 AM
Thanks a lot ! I will give you a feedback soon. You are a real hero !!
05-12-2022 02:52 AM
Hi Gerrit,
I think there may still exist a little problem for the entities without properties. This may be rare, but sometimes it's possible to get node with only relationships but no properties.
I just updated the reproducer. I created two simplified examples defined in with two new endpoints createNodeWithoutProperties1
and createNodeWithoutProperties2
.
The expected graphe to have for the two examples:
createNodeWithoutProperties1
:
(n1) -> (n2) -> (n3)
(n2)-> (m1)
createNodeWithoutProperties2
:
(n1) -> (n2) -> (n3)
(n1)-> (m1)
Result by using the projections:
createNodeWithoutProperties1
:
(n1)
createNodeWithoutProperties2
:
(n1)-> (m1)
Excuse me for creating strange test cases...
05-12-2022 05:56 AM
No worries, I really appreciate how hard you try to "challenge" me.
It helps not only you but also the other users. But I need a little bit more input on this, I might miss something in my interpretation.
Given your projection:
public interface EntityWithOneLevelRelationshipProjection {
Long getId();
String getName();
Map<String, String> getAdditionalProperties();
Map<String, List<LinkWithoutTargetRelationshipProjection>> getRelationships();
}
This only mentions the relationships
field. The n1.setOtherRelationships
part will get ignored and as a consequence also the other relationships / nodes. Using n1.setRelationships
with the right types, would do the magic.
05-12-2022 10:57 AM
Hi Gerrit,
Sorry for the late response. It took me some time trying to correct the reproducer. You are right, I made a mistake in my demo, I was a bit lost with the ugly variable names.
I have recreated other classes to make the demo clearer, since this time I don't think it's the problem of the projection. This time the problem happens even without projection but on the save.
You can find the a minimised demo here in the endpoint createRootNode
: https://github.com/Pandalei97/neo4jDemoBugRelationship/blob/main/src/main/java/com/example/demo/cont...
The classes used in the demo are :
RootNode
, PropertyNode
, SinglePropertyNode
and MultiplePropertyNode
.
SinglePropertyNode
, MultiplePropertyNode
are inherited from PropertyNode
.
Ideally, I should get a graphe
But with the reproducer, we've got
There may be a problem in the management of the inheritance. Because if we move the property name
from PropertyNode
into SinglePropertyNode
and MultiplePropertyNode
, we will get the expected graphe.
05-13-2022 12:57 AM
The problem here is the data model (and Lombok providing a false safety feeling).
You have to add @EqualsAndHashCode(callSuper = true)
to the sub-classes SinglePropertyNode
and MultiPropertyNode
.
As you have right observed, the behaviour changes when pushing name
down to the child entities. This would make @Data
consider them for equals/hashCode
. But it does not include them per default, if they are part of the parent class.
As a consequence, SDN will consider the M2
as the very same object as M1
and not persist M2
at all. Of course when not even looking into M2
it won't discover the connection to S3
. (The outcome depends on the random order, the relationships are processed, sometimes it could also be that M2
is first in the "cache" and M1
is the skipped entity).
On a side note and before the question arises: I do not know the details, how Lombok processes the Maps
. Given the fact that all PropertyNodes
are the same, even a deep equals into the elements will evaluate to true
because in the end, they only differ in their names.
05-13-2022 01:53 AM
Yes, we should not rely too much on Lombok and we will take more care of the equator. Thanks for the detailed explications !
I have no more questions for the moment. I really appreciate our exchanges these days, I have really learned a lot !
05-13-2022 03:28 AM
I can only second this. It is good to see people digging deep into the functionality we provide and find problems with corner cases.
Fun fact: When I implemented the projections for the dynamic relationships on Wednesday, I also remembered why we have forbidden this in the first place and the flag isDynamicRelationship
would not process.
But now we have dropped Spring Data commons' strict PropertyPath
at this place to be more flexible.
Thanks a lot for your patience and detailed reports.
All the sessions of the conference are now available online