Skip to content

Commit 6062794

Browse files
meistermeiermichael-simons
authored andcommitted
GH-2462 - Support mapping of intermediate hiearchy relationships.
Closes #2462
1 parent 9aac9e5 commit 6062794

File tree

4 files changed

+21
-31
lines changed

4 files changed

+21
-31
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ private MapProjection projectPropertiesAndRelationships(PropertyFilter.RelaxedPr
542542
List<Object> propertiesProjection = projectNodeProperties(parentPath, nodeDescription, nodeName, relationshipDescription, includedProperties);
543543
List<Object> contentOfProjection = new ArrayList<>(propertiesProjection);
544544

545-
contentOfProjection.addAll(generateListsFor(parentPath, relationships, nodeName, includedProperties, processedRelationships));
545+
contentOfProjection.addAll(generateListsFor(parentPath, nodeDescription, relationships, nodeName, includedProperties, processedRelationships));
546546
return Cypher.anyNode(nodeName).project(contentOfProjection);
547547
}
548548

@@ -604,7 +604,7 @@ private List<Object> projectNodeProperties(PropertyFilter.RelaxedPropertyPath pa
604604
/**
605605
* @see CypherGenerator#projectNodeProperties
606606
*/
607-
private List<Object> generateListsFor(PropertyFilter.RelaxedPropertyPath parentPath, Collection<RelationshipDescription> relationships, SymbolicName nodeName,
607+
private List<Object> generateListsFor(PropertyFilter.RelaxedPropertyPath parentPath, Neo4jPersistentEntity<?> nodeDescription, Collection<RelationshipDescription> relationships, SymbolicName nodeName,
608608
Predicate<PropertyFilter.RelaxedPropertyPath> includedProperties, List<RelationshipDescription> processedRelationships) {
609609

610610
List<Object> mapProjectionLists = new ArrayList<>();
@@ -620,17 +620,17 @@ private List<Object> generateListsFor(PropertyFilter.RelaxedPropertyPath parentP
620620
continue;
621621
}
622622

623-
generateListFor(parentPath, relationshipDescription, nodeName, processedRelationships, fieldName, mapProjectionLists, includedProperties);
623+
generateListFor(parentPath, nodeDescription, relationshipDescription, nodeName, processedRelationships, fieldName, mapProjectionLists, includedProperties);
624624
}
625625

626626
return mapProjectionLists;
627627
}
628628

629-
private void generateListFor(PropertyFilter.RelaxedPropertyPath parentPath, RelationshipDescription relationshipDescription, SymbolicName nodeName,
629+
private void generateListFor(PropertyFilter.RelaxedPropertyPath parentPath, Neo4jPersistentEntity<?> nodeDescription, RelationshipDescription relationshipDescription, SymbolicName nodeName,
630630
List<RelationshipDescription> processedRelationships, String fieldName, List<Object> mapProjectionLists, Predicate<PropertyFilter.RelaxedPropertyPath> includedProperties) {
631631

632632
String relationshipType = relationshipDescription.getType();
633-
String relationshipTargetName = relationshipDescription.generateRelatedNodesCollectionName(relationshipDescription.getSource());
633+
String relationshipTargetName = relationshipDescription.generateRelatedNodesCollectionName(nodeDescription);
634634
String sourcePrimaryLabel = relationshipDescription.getSource().getMostAbstractParentLabel(relationshipDescription.getSource());
635635
String targetPrimaryLabel = relationshipDescription.getTarget().getPrimaryLabel();
636636
List<String> targetAdditionalLabels = relationshipDescription.getTarget().getAdditionalLabels();

src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jEntityConverter.java

+8-22
Original file line numberDiff line numberDiff line change
@@ -342,16 +342,8 @@ private <ET> void populateProperties(MapAccessor queryResult, Neo4jPersistentEnt
342342
// store unless we temporarily put it there.
343343
knownObjects.storeObject(internalId, mappedObject);
344344

345-
// Fill associations
346-
// If the nodeDescription is a more abstract class as the concrete node description,
347-
// it might contain associations not named CONCRETE_TYPE_TARGET but ABSTRACT_TYPE_TARGET.
348-
// Those won't get caught by the most concrete node description.
349-
if (nodeDescription != concreteNodeDescription) {
350-
nodeDescription.doWithAssociations(
351-
populateFrom(queryResult, propertyAccessor, isConstructorParameter, objectAlreadyMapped, relationshipsFromResult, nodesFromResult));
352-
}
353345
concreteNodeDescription.doWithAssociations(
354-
populateFrom(queryResult, propertyAccessor, isConstructorParameter, objectAlreadyMapped, relationshipsFromResult, nodesFromResult));
346+
populateFrom(queryResult, nodeDescription, propertyAccessor, isConstructorParameter, objectAlreadyMapped, relationshipsFromResult, nodesFromResult));
355347
}
356348

357349
@Nullable
@@ -432,12 +424,12 @@ public <T> T getParameterValue(PreferredConstructor.Parameter<T, Neo4jPersistent
432424
// If we cannot find any value it does not mean that there isn't any.
433425
// The result set might contain associations not named CONCRETE_TYPE_TARGET but ABSTRACT_TYPE_TARGET.
434426
// For this we bubble up the hierarchy of NodeDescriptions.
435-
result = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, relationshipsFromResult, nodesFromResult, null)
427+
result = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, nodeDescription, relationshipsFromResult, nodesFromResult)
436428
.orElseGet(() -> {
437429
NodeDescription<?> parentNodeDescription = nodeDescription.getParentNodeDescription();
438430
T resultValue = null;
439431
while (parentNodeDescription != null) {
440-
Optional<Object> value = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, relationshipsFromResult, nodesFromResult, parentNodeDescription);
432+
Optional<Object> value = createInstanceOfRelationships(matchingProperty, values, relationshipDescription, parentNodeDescription, relationshipsFromResult, nodesFromResult);
441433
if (value.isPresent()) {
442434
resultValue = (T) value.get();
443435
break;
@@ -491,7 +483,7 @@ private static Object getValueOrDefault(boolean ownerIsKotlinType, Class<?> rawT
491483
return value == null && !ownerIsKotlinType && rawType.isPrimitive() ? ReflectionUtils.getPrimitiveDefault(rawType) : value;
492484
}
493485

494-
private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryResult,
486+
private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryResult, NodeDescription<?> baseDescription,
495487
PersistentPropertyAccessor<?> propertyAccessor, Predicate<Neo4jPersistentProperty> isConstructorParameter,
496488
boolean objectAlreadyMapped, Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
497489

@@ -534,14 +526,14 @@ private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor que
534526
return;
535527
}
536528

537-
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, relationshipsFromResult, nodesFromResult, null)
529+
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, baseDescription, relationshipsFromResult, nodesFromResult)
538530
.ifPresent(value -> propertyAccessor.setProperty(persistentProperty, value));
539531
};
540532
}
541533

542534
private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty persistentProperty, MapAccessor values,
543-
RelationshipDescription relationshipDescription, Collection<Relationship> relationshipsFromResult,
544-
Collection<Node> nodesFromResult, @Nullable NodeDescription<?> nodeDescription) {
535+
RelationshipDescription relationshipDescription, NodeDescription<?> baseDescription, Collection<Relationship> relationshipsFromResult,
536+
Collection<Node> nodesFromResult) {
545537

546538
String typeOfRelationship = relationshipDescription.getType();
547539
String sourceLabel = relationshipDescription.getSource().getPrimaryLabel();
@@ -575,13 +567,7 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
575567
mappedObjectHandler = (type, mappedObject) -> value.add(mappedObject);
576568
}
577569

578-
// Generate name driven by the (nullable) NodeDescription of necessary,
579-
// because it might contain associations not named CONCRETE_TYPE_TARGET but ABSTRACT_TYPE_TARGET
580-
String collectionName = relationshipDescription.generateRelatedNodesCollectionName(
581-
nodeDescription != null
582-
? nodeDescription
583-
: relationshipDescription.getSource()
584-
);
570+
String collectionName = relationshipDescription.generateRelatedNodesCollectionName(baseDescription);
585571

586572
Value list = values.get(collectionName);
587573

src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jPersistentEntity.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -512,11 +512,15 @@ public void addChildNodeDescription(NodeDescription<?> child) {
512512
}
513513

514514
@Override
515-
public Set<NodeDescription<?>> getChildNodeDescriptionsInHierarchy() {
516-
Set<NodeDescription<?>> childNodes = new HashSet<>(childNodeDescriptions);
515+
public List<NodeDescription<?>> getChildNodeDescriptionsInHierarchy() {
516+
List<NodeDescription<?>> childNodes = new ArrayList<>(childNodeDescriptions);
517517

518518
for (NodeDescription<?> childNodeDescription : childNodeDescriptions) {
519-
childNodes.addAll(childNodeDescription.getChildNodeDescriptionsInHierarchy());
519+
for (NodeDescription<?> grantChildNodeDescription : childNodeDescription.getChildNodeDescriptionsInHierarchy()) {
520+
if (!childNodes.contains(grantChildNodeDescription)) {
521+
childNodes.add(grantChildNodeDescription);
522+
}
523+
}
520524
}
521525
return childNodes;
522526
}

src/test/java/org/springframework/data/neo4j/integration/imperative/InheritanceMappingIT.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ void shouldMatchPolymorphicKotlinInterfacesWhenFetchingAll(@Autowired CinemaRepo
406406
}
407407

408408
@Test // GH-2452
409-
void asdf(@Autowired ParentClassWithRelationshipRepository repository) {
409+
void loadAndPopulateRelationshipFromTheHierarchy(@Autowired ParentClassWithRelationshipRepository repository) {
410410

411411
long childId;
412412
try (Session session = driver.session(bookmarkCapture.createSessionConfig())) {

0 commit comments

Comments
 (0)