From 9e2edadd8b3a875e7f2bd18cfdf806db4278c34d Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Thu, 24 Oct 2024 14:08:58 +0200 Subject: [PATCH] fix: Double check for a collection to be present in the mapping without directional prefix. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a direct follow up on #2918, in which we added the suffix to the collection names. That change didn’t take custom queries into account and people are most likely still using the known pattern of `source_REL_target` and I would like to not break them. Therefor, we check now in the values list if a collection without suffix exists if we didn’t find one with and if so, use that. This fixes #2963. --- .../mapping/DefaultNeo4jEntityConverter.java | 5 ++ .../neo4j/integration/issues/IssuesIT.java | 21 +++++++ .../integration/issues/gh2963/MyModel.java | 62 +++++++++++++++++++ .../issues/gh2963/MyRepository.java | 48 ++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyModel.java create mode 100644 src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyRepository.java 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 23b142ad0..8c72cef47 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 @@ -675,6 +675,11 @@ final class DefaultNeo4jEntityConverter implements Neo4jEntityConverter { String collectionName = relationshipDescription.generateRelatedNodesCollectionName(baseDescription); Value list = values.get(collectionName); boolean relationshipListEmptyOrNull = Values.NULL.equals(list); + if (relationshipListEmptyOrNull) { + collectionName = collectionName.replaceFirst("_" + relationshipDescription.isOutgoing() + "\\z", ""); + } + list = values.get(collectionName); + relationshipListEmptyOrNull = Values.NULL.equals(list); List relationshipsAndProperties = new ArrayList<>(); diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java b/src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java index f2b9e7d35..114a70043 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/IssuesIT.java @@ -187,6 +187,8 @@ import org.springframework.data.neo4j.integration.issues.gh2908.LocatedNodeWithS import org.springframework.data.neo4j.integration.issues.gh2908.Place; import org.springframework.data.neo4j.integration.issues.gh2918.ConditionNode; import org.springframework.data.neo4j.integration.issues.gh2918.ConditionRepository; +import org.springframework.data.neo4j.integration.issues.gh2963.MyModel; +import org.springframework.data.neo4j.integration.issues.gh2963.MyRepository; import org.springframework.data.neo4j.integration.issues.qbe.A; import org.springframework.data.neo4j.integration.issues.qbe.ARepository; import org.springframework.data.neo4j.integration.issues.qbe.B; @@ -1677,6 +1679,25 @@ class IssuesIT extends TestBase { } } + @Tag("GH-2963") + @Test + void customQueriesShouldKeepWorkingWithoutSpecifyingTheRelDirectionInTheirQueries(@Autowired MyRepository myRepository) { + // set up data in database + MyModel myNestedModel = new MyModel(); + myNestedModel.setName("nested"); + + MyModel myRootModel = new MyModel(); + myRootModel.setName("root"); + myRootModel.setMyNestedModel(myNestedModel); + + String uuid = myRepository.save(myRootModel).getUuid(); + Optional rootModelFromDbCustom = myRepository.getByUuidCustomQuery(uuid); + assertThat(rootModelFromDbCustom).map(MyModel::getMyNestedModel).isPresent(); + + rootModelFromDbCustom = myRepository.getByUuidCustomQueryV2(uuid); + assertThat(rootModelFromDbCustom).map(MyModel::getMyNestedModel).isPresent(); + } + @Configuration @EnableTransactionManagement @EnableNeo4jRepositories(namedQueriesLocation = "more-custom-queries.properties") diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyModel.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyModel.java new file mode 100644 index 000000000..b6902a36b --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyModel.java @@ -0,0 +1,62 @@ +/* + * Copyright 2011-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.integration.issues.gh2963; + +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; +import org.springframework.data.neo4j.core.support.UUIDStringGenerator; + +/** + * @author Andreas Rümpel + * @author Michael J. Simons + */ +@Node +public class MyModel { + @Id + @GeneratedValue(generatorClass = UUIDStringGenerator.class) + private String uuid; + + private String name; + + @Relationship(value = "REL_TO_MY_NESTED_MODEL") + private MyModel myNestedModel; + + public MyModel getMyNestedModel() { + return myNestedModel; + } + + public void setMyNestedModel(MyModel myNestedModel) { + this.myNestedModel = myNestedModel; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyRepository.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyRepository.java new file mode 100644 index 000000000..81eaacce2 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2963/MyRepository.java @@ -0,0 +1,48 @@ +/* + * Copyright 2011-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.integration.issues.gh2963; + +import java.util.Optional; + +import org.springframework.data.neo4j.repository.query.Query; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Andreas Rümpel + * @author Michael J. Simons + */ +public interface MyRepository extends CrudRepository { + + @Query(""" + MATCH (root:MyModel {uuid: $uuid}) + RETURN root { + .*, MyModel_REL_TO_MY_NESTED_MODEL_MyModel: [ + (root)-[:REL_TO_MY_NESTED_MODEL]->(nested:MyModel) | nested {. *} + ] + } + """) + Optional getByUuidCustomQuery(String uuid); + + @Query(""" + MATCH (root:MyModel {uuid: $uuid}) + RETURN root { + .*, MyModel_REL_TO_MY_NESTED_MODEL_MyModel_true: [ + (root)-[:REL_TO_MY_NESTED_MODEL]->(nested:MyModel) | nested {. *} + ] + } + """) + Optional getByUuidCustomQueryV2(String uuid); +}