From 0124f32b7e206add073f6bcbe8a22fafe30a3033 Mon Sep 17 00:00:00 2001 From: Gerrit Meier Date: Wed, 21 Jun 2023 15:40:22 +0200 Subject: [PATCH] GH-2749 - Skip relationship mapping if no relationships are available. Closes #2749 --- .../mapping/DefaultNeo4jEntityConverter.java | 16 +++++++++++----- .../integration/imperative/RepositoryIT.java | 13 +++++++++++++ .../reactive/ReactiveRepositoryIT.java | 15 +++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java index 960a87e8c..0a438cea8 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java @@ -651,20 +651,26 @@ final class DefaultNeo4jEntityConverter implements Neo4jEntityConverter { String collectionName = relationshipDescription.generateRelatedNodesCollectionName(genericNodeDescription); Value list = values.get(collectionName); + boolean relationshipListEmptyOrNull = Values.NULL.equals(list); List relationshipsAndProperties = new ArrayList<>(); - if (Values.NULL.equals(list)) { + String elementId = IdentitySupport.getElementId(values); + Long internalId = IdentitySupport.getInternalId(values); + boolean hasGeneratedIdValue = elementId != null || internalId != null; + + if (relationshipListEmptyOrNull && hasGeneratedIdValue) { String sourceNodeId; Function sourceIdSelector; Function targetIdSelector = relationshipDescription.isIncoming() ? Relationship::startNodeElementId : Relationship::endNodeElementId; - if (IdentitySupport.getElementId(values) == null) { + + if (internalId != null) { // this can happen when someone used dto mapping and added the "classical" approach - sourceNodeId = Optional.ofNullable(IdentitySupport.getInternalId(values)).map(l -> Long.toString(l)).orElseThrow(); + sourceNodeId = Long.toString(internalId); Function hlp = relationshipDescription.isIncoming() ? Relationship::endNodeId : Relationship::startNodeId; sourceIdSelector = hlp.andThen(l -> Long.toString(l)); } else { - sourceNodeId = IdentitySupport.getElementId(values); + sourceNodeId = elementId; sourceIdSelector = relationshipDescription.isIncoming() ? Relationship::endNodeElementId : Relationship::startNodeElementId; } @@ -732,7 +738,7 @@ final class DefaultNeo4jEntityConverter implements Neo4jEntityConverter { } allMatchingTypeRelationshipsInResult.removeAll(relationshipsProcessed); } - } else { + } else if (!relationshipListEmptyOrNull) { for (Value relatedEntity : list.asList(Function.identity())) { Neo4jPersistentEntity concreteTargetNodeDescription = diff --git a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java index 0cff07d3e..4471571e7 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java @@ -4439,6 +4439,16 @@ class RepositoryIT { assertThat(repository.findByHobbiesHobbyName("Bowling").getName()).isEqualTo("Freddie"); } + + @Test + void findByCustomQueryOnlyWithPropertyReturn( + @Autowired PersonWithRelationshipWithPropertiesRepository repository) { + doWithSession(session -> + session.run( + "CREATE (:PersonWithRelationshipWithProperties{name:'Freddie'})-[:LIKES{since: 2020, active: true}]->(:Hobby{name: 'Bowling'})").consume()); + + assertThat(repository.justTheNames().getName()).isEqualTo("Freddie"); + } } @Test // GH-2706 @@ -4526,6 +4536,9 @@ class RepositoryIT { PersonWithRelationshipWithProperties findByHobbiesSinceAndHobbiesActive(int since1, boolean active); PersonWithRelationshipWithProperties findByHobbiesHobbyName(String hobbyName); + + @Query("MATCH (p:PersonWithRelationshipWithProperties) return p {.name}") + PersonWithRelationshipWithProperties justTheNames(); } interface PetRepository extends Neo4jRepository { diff --git a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveRepositoryIT.java b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveRepositoryIT.java index 141729164..8bf42da23 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveRepositoryIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveRepositoryIT.java @@ -1572,6 +1572,18 @@ class ReactiveRepositoryIT { StepVerifier.create(repository.findByHobbiesSinceOrHobbiesActive(2019, false)).verifyComplete(); } + @Test + void findByCustomQueryOnlyWithPropertyReturn( + @Autowired ReactivePersonWithRelationshipWithPropertiesRepository repository) { + doWithSession(session -> + session.run( + "CREATE (:PersonWithRelationshipWithProperties{name:'Freddie'})-[:LIKES{since: 2020, active: true}]->(:Hobby{name: 'Bowling'})").consume() + ); + + StepVerifier.create(repository.justTheNames()) + .assertNext(person -> assertThat(person.getName()).isEqualTo("Freddie")).verifyComplete(); + } + @Test void findByPropertyOnRelationshipWithPropertiesAnd( @Autowired ReactivePersonWithRelationshipWithPropertiesRepository repository) { @@ -2744,6 +2756,9 @@ class ReactiveRepositoryIT { Mono findByHobbiesSinceOrHobbiesActive(int since1, boolean active); Mono findByHobbiesSinceAndHobbiesActive(int since1, boolean active); + + @Query("MATCH (p:PersonWithRelationshipWithProperties) return p {.name}") + Mono justTheNames(); } interface ReactiveHobbyWithRelationshipWithPropertiesRepository