GH-2727 - Fix multiple levels of projecting properties mapping.

Closes #2727
This commit is contained in:
Gerrit Meier
2023-06-01 15:26:26 +02:00
parent f330c7650d
commit ce3dc986e4
12 changed files with 426 additions and 16 deletions

View File

@@ -744,21 +744,7 @@ public enum CypherGenerator {
if (property.isDynamicLabels() || property.isComposite()) {
continue;
}
PropertyFilter.RelaxedPropertyPath from;
if (relationshipDescription == null) {
from = parentPath.append(property.getFieldName());
} else if (relationshipDescription.hasRelationshipProperties()) {
@SuppressWarnings("ConstantConditions") // All prerequisites have been checked by the persistence context at this point.
String relationshipPropertyTargetNodeFieldName =
((Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity())
.getPersistentProperty(TargetNode.class).getFieldName();
from = parentPath.append(relationshipPropertyTargetNodeFieldName + "." + property.getFieldName());
} else {
from = parentPath.append(property.getFieldName());
}
PropertyFilter.RelaxedPropertyPath from = parentPath.append(property.getFieldName());
if (!includeField.test(from)) {
continue;
@@ -828,7 +814,10 @@ public enum CypherGenerator {
Neo4jPersistentEntity<?> endNodeDescription = (Neo4jPersistentEntity<?>) relationshipDescription.getTarget();
processedRelationships.add(relationshipDescription);
PropertyFilter.RelaxedPropertyPath newParentPath = parentPath.append(relationshipDescription.getFieldName());
PropertyFilter.RelaxedPropertyPath newParentPath = relationshipDescription.hasRelationshipProperties()
? parentPath.append(relationshipDescription.getFieldName()).append(((Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity())
.getPersistentProperty(TargetNode.class).getFieldName())
: parentPath.append(relationshipDescription.getFieldName());
if (relationshipDescription.isDynamic()) {
Relationship relationship = relationshipDescription.isOutgoing()

View File

@@ -145,6 +145,13 @@ import org.springframework.data.neo4j.integration.issues.gh2639.Sales;
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;
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelEntity;
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelEntityRepository;
import org.springframework.data.neo4j.integration.issues.gh2727.FirstLevelProjection;
import org.springframework.data.neo4j.integration.issues.gh2727.SecondLevelEntity;
import org.springframework.data.neo4j.integration.issues.gh2727.SecondLevelEntityRelationship;
import org.springframework.data.neo4j.integration.issues.gh2727.ThirdLevelEntity;
import org.springframework.data.neo4j.integration.issues.gh2727.ThirdLevelEntityRelationship;
import org.springframework.data.neo4j.integration.misc.ConcreteImplementationTwo;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.repository.query.QueryFragmentsAndParameters;
@@ -1030,6 +1037,59 @@ class IssuesIT extends TestBase {
}
@Test
@Tag("GH-2727")
void mapsProjectionChainWithRelationshipProperties(@Autowired FirstLevelEntityRepository firstLevelEntityRepository) {
String secondLevelValue = "someSecondLevelValue";
String thirdLevelValue = "someThirdLevelValue";
final List<SecondLevelEntityRelationship> secondLevelEntityRelationships = new ArrayList<>();
for (int i = 0; i < 2; i++) {
final List<ThirdLevelEntityRelationship> thirdLevelEntityRelationships = new ArrayList<>();
for (int j = 0; j < 3; j++) {
final ThirdLevelEntity thirdLevelEntity = new ThirdLevelEntity();
thirdLevelEntity.setSomeValue(thirdLevelValue);
final ThirdLevelEntityRelationship thirdLevelRelationship = new ThirdLevelEntityRelationship();
thirdLevelRelationship.setTarget(thirdLevelEntity);
thirdLevelRelationship.setOrder(j + 1);
thirdLevelEntityRelationships.add(thirdLevelRelationship);
}
final SecondLevelEntity secondLevelEntity = SecondLevelEntity.builder()
.thirdLevelEntityRelationshipProperties(thirdLevelEntityRelationships)
.someValue(secondLevelValue)
.build();
final SecondLevelEntityRelationship secondLevelRelationship = new SecondLevelEntityRelationship();
secondLevelRelationship.setTarget(secondLevelEntity);
secondLevelRelationship.setOrder(i + 1);
secondLevelEntityRelationships.add(secondLevelRelationship);
}
final FirstLevelEntity firstLevelEntity = FirstLevelEntity.builder()
.secondLevelEntityRelationshipProperties(secondLevelEntityRelationships)
.name("Test")
.build();
firstLevelEntityRepository.save(firstLevelEntity);
FirstLevelProjection firstLevelProjection = firstLevelEntityRepository.findOneById(firstLevelEntity.getId());
assertThat(firstLevelProjection).isNotNull();
assertThat(firstLevelProjection.getSecondLevelEntityRelationshipProperties()).hasSize(2)
.allSatisfy(secondLevelRelationship -> {
assertThat(secondLevelRelationship.getTarget().getSomeValue().equals(secondLevelValue));
assertThat(secondLevelRelationship.getOrder()).isGreaterThan(0);
assertThat(secondLevelRelationship.getTarget().getThirdLevelEntityRelationshipProperties())
.isNotEmpty()
.allSatisfy(thirdLevel -> {
assertThat(thirdLevel.getOrder()).isGreaterThan(0);
assertThat(thirdLevel.getTarget()).isNotNull();
assertThat(thirdLevel.getTarget().getSomeValue()).isEqualTo(thirdLevelValue);
});
});
}
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(namedQueriesLocation = "more-custom-queries.properties")

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2011-2023 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.gh2727;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
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 java.util.List;
/**
* @author Gerrit Meier
*/
@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@Node("FirstLevel")
@EqualsAndHashCode(of = {"id"})
public class FirstLevelEntity {
@Id
@GeneratedValue
private Long id;
private String name;
@Relationship("HasSecondLevel")
private List<SecondLevelEntityRelationship> secondLevelEntityRelationshipProperties;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2011-2023 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.gh2727;
import org.springframework.data.neo4j.repository.Neo4jRepository;
/**
* @author Gerrit Meier
*/
public interface FirstLevelEntityRepository extends Neo4jRepository<FirstLevelEntity, Long> {
FirstLevelProjection findOneById(Long id);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2011-2023 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.gh2727;
import java.util.Set;
/**
* @author Gerrit Meier
*/
public interface FirstLevelProjection {
Long getId();
Set<SecondLevelRelationshipProjection> getSecondLevelEntityRelationshipProperties();
/**
*
*/
interface SecondLevelRelationshipProjection {
Long getId();
SecondLevelProjection getTarget();
Integer getOrder();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2011-2023 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.gh2727;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Property;
import org.springframework.data.neo4j.core.schema.RelationshipId;
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
import org.springframework.data.neo4j.core.schema.TargetNode;
/**
* @author Gerrit Meier
* @param <T> relationship properties type
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@RelationshipProperties
public class OrderedRelation<T> implements Comparable<OrderedRelation<T>> {
@RelationshipId
@GeneratedValue
private Long id;
@TargetNode
private T target;
@Property
private Integer order;
@Override
public int compareTo(final OrderedRelation<T> o) {
return order - o.order;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2011-2023 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.gh2727;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
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 java.util.List;
/**
* @author Gerrit Meier
*/
@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@Node("SecondLevel")
@EqualsAndHashCode(of = {"id"})
public class SecondLevelEntity {
@Id
@GeneratedValue
private Long id;
private String someValue;
@Relationship("HasThirdLevel")
private List<ThirdLevelEntityRelationship> thirdLevelEntityRelationshipProperties;
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2011-2023 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.gh2727;
/**
* @author Gerrit Meier
*/
public class SecondLevelEntityRelationship extends OrderedRelation<SecondLevelEntity> {
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2011-2023 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.gh2727;
import java.util.Set;
/**
* @author Gerrit Meier
*/
public interface SecondLevelProjection {
Long getId();
String getSomeValue();
Set<ThirdLevelRelationshipProjection> getThirdLevelEntityRelationshipProperties();
/**
*
*/
interface ThirdLevelRelationshipProjection {
Long getId();
ThirdLevelProjection getTarget();
Integer getOrder();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2011-2023 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.gh2727;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
/**
* @author Gerrit Meier
*/
@SuperBuilder
@NoArgsConstructor
@Getter
@Setter
@Node("ThirdLevel")
@EqualsAndHashCode(of = {"id"})
public class ThirdLevelEntity {
@Id
@GeneratedValue
private Long id;
private String someValue;
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2011-2023 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.gh2727;
/**
* @author Gerrit Meier
*/
public class ThirdLevelEntityRelationship extends OrderedRelation<ThirdLevelEntity> {
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2011-2023 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.gh2727;
/**
* @author Gerrit Meier
*/
public interface ThirdLevelProjection {
Long getId();
String getSomeValue();
}