Skip to content

Commit e5de744

Browse files
committed
GH-2319 - Mapping performance optimization.
Original pull request: #2319
1 parent 2e7ecef commit e5de744

File tree

1 file changed

+63
-52
lines changed

1 file changed

+63
-52
lines changed

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

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.function.Function;
3131
import java.util.function.Predicate;
3232
import java.util.function.Supplier;
33+
import java.util.stream.Collectors;
3334
import java.util.stream.StreamSupport;
3435

3536
import org.neo4j.driver.Value;
@@ -105,7 +106,7 @@ public <R> R read(Class<R> targetType, MapAccessor mapAccessor) {
105106
}
106107

107108
try {
108-
return map(queryRoot, queryRoot, rootNodeDescription, new HashSet<>());
109+
return map(queryRoot, queryRoot, rootNodeDescription);
109110
} catch (Exception e) {
110111
throw new MappingException("Error mapping " + mapAccessor.toString(), e);
111112
}
@@ -241,11 +242,13 @@ private static MapAccessor mergeRootNodeWithRecord(Node node, MapAccessor record
241242
* @return The mapped entity
242243
*/
243244
private <ET> ET map(MapAccessor queryResult, MapAccessor allValues, Neo4jPersistentEntity<ET> nodeDescription) {
244-
return map(queryResult, allValues, nodeDescription, null);
245+
Collection<Relationship> relationshipsFromResult = extractRelationships(allValues);
246+
Collection<Node> nodesFromResult = extractNodes(allValues);
247+
return map(queryResult, nodeDescription, null, relationshipsFromResult, nodesFromResult);
245248
}
246249

247-
private <ET> ET map(MapAccessor queryResult, MapAccessor allValues, Neo4jPersistentEntity<ET> nodeDescription,
248-
@Nullable Object lastMappedEntity) {
250+
private <ET> ET map(MapAccessor queryResult, Neo4jPersistentEntity<ET> nodeDescription,
251+
@Nullable Object lastMappedEntity, Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
249252

250253
// if the given result does not contain an identifier to the mapped object cannot get temporarily saved
251254
Long internalId = getInternalId(queryResult);
@@ -258,13 +261,8 @@ private <ET> ET map(MapAccessor queryResult, MapAccessor allValues, Neo4jPersist
258261
Neo4jPersistentEntity<ET> concreteNodeDescription = (Neo4jPersistentEntity<ET>) nodeDescriptionAndLabels
259262
.getNodeDescription();
260263

261-
Predicate<String> includeAllFields = (field) -> true;
262-
263-
Collection<RelationshipDescription> relationships = nodeDescription
264-
.getRelationshipsInHierarchy(includeAllFields);
265-
266-
ET instance = instantiate(concreteNodeDescription, queryResult, allValues, relationships,
267-
nodeDescriptionAndLabels.getDynamicLabels(), lastMappedEntity);
264+
ET instance = instantiate(concreteNodeDescription, queryResult,
265+
nodeDescriptionAndLabels.getDynamicLabels(), lastMappedEntity, relationshipsFromResult, nodesFromResult);
268266

269267
PersistentPropertyAccessor<ET> propertyAccessor = concreteNodeDescription.getPropertyAccessor(instance);
270268

@@ -283,7 +281,7 @@ private <ET> ET map(MapAccessor queryResult, MapAccessor allValues, Neo4jPersist
283281
knownObjects.storeObject(internalId, instance);
284282
// Fill associations
285283
concreteNodeDescription.doWithAssociations(
286-
populateFrom(queryResult, allValues, propertyAccessor, isConstructorParameter));
284+
populateFrom(queryResult, propertyAccessor, isConstructorParameter, relationshipsFromResult, nodesFromResult));
287285
}
288286
ET bean = propertyAccessor.getBean();
289287

@@ -357,9 +355,9 @@ private boolean containsOnePlainNode(MapAccessor queryResult) {
357355
.filter(value -> value.hasType(nodeType)).count() == 1L;
358356
}
359357

360-
private <ET> ET instantiate(Neo4jPersistentEntity<ET> nodeDescription, MapAccessor values, MapAccessor allValues,
361-
Collection<RelationshipDescription> relationships, Collection<String> surplusLabels,
362-
@Nullable Object lastMappedEntity) {
358+
private <ET> ET instantiate(Neo4jPersistentEntity<ET> nodeDescription, MapAccessor values,
359+
Collection<String> surplusLabels, @Nullable Object lastMappedEntity,
360+
Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
363361

364362
ParameterValueProvider<Neo4jPersistentProperty> parameterValueProvider = new ParameterValueProvider<Neo4jPersistentProperty>() {
365363
@Override
@@ -373,7 +371,7 @@ public Object getParameterValue(PreferredConstructor.Parameter parameter) {
373371
String propertyFieldName = matchingProperty.getFieldName();
374372
return r.getFieldName().equals(propertyFieldName);
375373
}).findFirst().get();
376-
return createInstanceOfRelationships(matchingProperty, values, allValues, relationshipDescription).orElse(null);
374+
return createInstanceOfRelationships(matchingProperty, values, relationshipDescription, relationshipsFromResult, nodesFromResult).orElse(null);
377375
} else if (matchingProperty.isDynamicLabels()) {
378376
return createDynamicLabelsProperty(matchingProperty.getTypeInformation(), surplusLabels);
379377
} else if (matchingProperty.isEntityWithRelationshipProperties()) {
@@ -408,22 +406,22 @@ private PropertyHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryR
408406
};
409407
}
410408

411-
private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryResult, MapAccessor allValues,
412-
PersistentPropertyAccessor<?> propertyAccessor, Predicate<Neo4jPersistentProperty> isConstructorParameter) {
409+
private AssociationHandler<Neo4jPersistentProperty> populateFrom(MapAccessor queryResult,
410+
PersistentPropertyAccessor<?> propertyAccessor, Predicate<Neo4jPersistentProperty> isConstructorParameter, Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
413411
return association -> {
414412

415413
Neo4jPersistentProperty persistentProperty = association.getInverse();
416414
if (isConstructorParameter.test(persistentProperty)) {
417415
return;
418416
}
419417

420-
createInstanceOfRelationships(persistentProperty, queryResult, allValues, (RelationshipDescription) association)
418+
createInstanceOfRelationships(persistentProperty, queryResult, (RelationshipDescription) association, relationshipsFromResult, nodesFromResult)
421419
.ifPresent(value -> propertyAccessor.setProperty(persistentProperty, value));
422420
};
423421
}
424422

425423
private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty persistentProperty, MapAccessor values,
426-
MapAccessor allValues, RelationshipDescription relationshipDescription) {
424+
RelationshipDescription relationshipDescription, Collection<Relationship> relationshipsFromResult, Collection<Node> nodesFromResult) {
427425

428426
String typeOfRelationship = relationshipDescription.getType();
429427
String sourceLabel = relationshipDescription.getSource().getPrimaryLabel();
@@ -464,30 +462,35 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
464462
List<Object> relationshipsAndProperties = new ArrayList<>();
465463

466464
if (Values.NULL.equals(list)) {
465+
Long sourceNodeId = getInternalId(values);
466+
467+
Function<Relationship, Long> sourceIdSelector = relationshipDescription.isIncoming() ? Relationship::endNodeId : Relationship::startNodeId;
468+
Function<Relationship, Long> targetIdSelector = relationshipDescription.isIncoming() ? Relationship::startNodeId : Relationship::endNodeId;
469+
470+
// Retrieve all matching relationships from the result's list(s)
471+
Collection<Relationship> allMatchingTypeRelationshipsInResult =
472+
extractMatchingRelationships(relationshipsFromResult, relationshipDescription, typeOfRelationship,
473+
(possibleRelationship) -> sourceIdSelector.apply(possibleRelationship).equals(sourceNodeId));
467474

468-
// Retrieve all relationships from the result's list(s)
469-
Collection<Relationship> allMatchingTypeRelationshipsInResult = extractMatchingRelationships(allValues, relationshipDescription, typeOfRelationship);
470475
// Retrieve all nodes from the result's list(s)
471-
Collection<Node> allNodesWithMatchingLabelInResult = extractMatchingNodes(allValues, targetLabel);
476+
Collection<Node> allNodesWithMatchingLabelInResult = extractMatchingNodes(nodesFromResult, targetLabel);
472477

473-
Function<Relationship, Long> targetIdSelector = relationshipDescription.isIncoming() ? Relationship::startNodeId : Relationship::endNodeId;
474-
Function<Relationship, Long> sourceIdSelector = relationshipDescription.isIncoming() ? Relationship::endNodeId : Relationship::startNodeId;
475-
Long sourceNodeId = getInternalId(values);
476478
for (Node possibleValueNode : allNodesWithMatchingLabelInResult) {
477479
long targetNodeId = possibleValueNode.id();
480+
481+
Neo4jPersistentEntity<?> concreteTargetNodeDescription =
482+
getMostConcreteTargetNodeDescription(genericTargetNodeDescription, possibleValueNode);
483+
478484
Set<Relationship> relationshipsProcessed = new HashSet<>();
479485
for (Relationship possibleRelationship : allMatchingTypeRelationshipsInResult) {
480-
if (targetIdSelector.apply(possibleRelationship) == targetNodeId && sourceIdSelector.apply(possibleRelationship).equals(sourceNodeId)) {
481-
482-
Neo4jPersistentEntity<?> concreteTargetNodeDescription =
483-
getMostConcreteTargetNodeDescription(genericTargetNodeDescription, possibleValueNode);
486+
if (targetIdSelector.apply(possibleRelationship) == targetNodeId) {
484487

485-
Object mappedObject = map(possibleValueNode, allValues, concreteTargetNodeDescription);
488+
Object mappedObject = map(possibleValueNode, concreteTargetNodeDescription, null, relationshipsFromResult, nodesFromResult);
486489
if (relationshipDescription.hasRelationshipProperties()) {
487490

488-
Object relationshipProperties = map(possibleRelationship, allValues,
491+
Object relationshipProperties = map(possibleRelationship,
489492
(Neo4jPersistentEntity) relationshipDescription.getRelationshipPropertiesEntity(),
490-
mappedObject);
493+
mappedObject, relationshipsFromResult, nodesFromResult);
491494
relationshipsAndProperties.add(relationshipProperties);
492495
mappedObjectHandler.accept(possibleRelationship.type(), relationshipProperties);
493496
} else {
@@ -504,17 +507,17 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
504507
Neo4jPersistentEntity<?> concreteTargetNodeDescription =
505508
getMostConcreteTargetNodeDescription(genericTargetNodeDescription, relatedEntity);
506509

507-
Object valueEntry = map(relatedEntity, allValues, concreteTargetNodeDescription);
510+
Object valueEntry = map(relatedEntity, concreteTargetNodeDescription, null, relationshipsFromResult, nodesFromResult);
508511

509512
if (relationshipDescription.hasRelationshipProperties()) {
510513
String relationshipSymbolicName = sourceLabel
511514
+ RelationshipDescription.NAME_OF_RELATIONSHIP + targetLabel;
512515
Relationship relatedEntityRelationship = relatedEntity.get(relationshipSymbolicName)
513516
.asRelationship();
514517

515-
Object relationshipProperties = map(relatedEntityRelationship, allValues,
518+
Object relationshipProperties = map(relatedEntityRelationship,
516519
(Neo4jPersistentEntity) relationshipDescription.getRelationshipPropertiesEntity(),
517-
valueEntry);
520+
valueEntry, relationshipsFromResult, nodesFromResult);
518521
relationshipsAndProperties.add(relationshipProperties);
519522
mappedObjectHandler.accept(relatedEntity.get(RelationshipDescription.NAME_OF_RELATIONSHIP_TYPE).asString(), relationshipProperties);
520523
} else {
@@ -540,46 +543,54 @@ private Optional<Object> createInstanceOfRelationships(Neo4jPersistentProperty p
540543
}
541544
}
542545

543-
private Collection<Node> extractMatchingNodes(MapAccessor allValues, String targetLabel) {
546+
private Collection<Node> extractMatchingNodes(Collection<Node> allNodesInResult, String targetLabel) {
544547

545-
Collection<Node> allNodesWithMatchingLabelInResult = new ArrayList<>();
546548
Predicate<Node> onlyWithMatchingLabels = n -> n.hasLabel(targetLabel);
549+
return allNodesInResult.stream()
550+
.filter(onlyWithMatchingLabels)
551+
.collect(Collectors.toSet());
552+
}
553+
554+
private Collection<Node> extractNodes(MapAccessor allValues) {
555+
Collection<Node> allNodesInResult = new ArrayList<>();
547556
StreamSupport.stream(allValues.values().spliterator(), false)
548557
.filter(MappingSupport.isListContainingOnly(listType, this.nodeType))
549558
.flatMap(entry -> MappingSupport.extractNodes(listType, entry).stream())
550-
.filter(onlyWithMatchingLabels)
551-
.forEach(allNodesWithMatchingLabelInResult::add);
559+
.forEach(allNodesInResult::add);
552560

553-
if (allNodesWithMatchingLabelInResult.isEmpty()) {
561+
if (allNodesInResult.isEmpty()) {
554562
StreamSupport.stream(allValues.values().spliterator(), false)
555563
.filter(this.nodeType::isTypeOf)
556564
.map(Value::asNode)
557-
.filter(onlyWithMatchingLabels)
558-
.forEach(allNodesWithMatchingLabelInResult::add);
565+
.forEach(allNodesInResult::add);
559566
}
560567

561-
return allNodesWithMatchingLabelInResult;
568+
return allNodesInResult;
562569
}
563570

564-
private Collection<Relationship> extractMatchingRelationships(MapAccessor allValues, RelationshipDescription relationshipDescription, String typeOfRelationship) {
571+
private Collection<Relationship> extractMatchingRelationships(Collection<Relationship> relationshipsFromResult, RelationshipDescription relationshipDescription, String typeOfRelationship, Predicate<Relationship> relationshipPredicate) {
565572

566-
Collection<Relationship> allMatchingTypeRelationshipsInResult = new ArrayList<>();
567573
Predicate<Relationship> onlyWithMatchingType = r -> r.type().equals(typeOfRelationship) || relationshipDescription.isDynamic();
574+
return relationshipsFromResult.stream()
575+
.filter(onlyWithMatchingType.and(relationshipPredicate))
576+
.collect(Collectors.toSet());
577+
}
578+
579+
private Collection<Relationship> extractRelationships(MapAccessor allValues) {
580+
Collection<Relationship> allRelationshipsInResult = new ArrayList<>();
568581
StreamSupport.stream(allValues.values().spliterator(), false)
569582
.filter(MappingSupport.isListContainingOnly(listType, this.relationshipType))
570583
.flatMap(entry -> MappingSupport.extractRelationships(listType, entry).stream())
571-
.filter(onlyWithMatchingType)
572-
.forEach(allMatchingTypeRelationshipsInResult::add);
584+
.forEach(allRelationshipsInResult::add);
573585

574-
if (allMatchingTypeRelationshipsInResult.isEmpty()) {
586+
if (allRelationshipsInResult.isEmpty()) {
575587
StreamSupport.stream(allValues.values().spliterator(), false)
576588
.filter(this.relationshipType::isTypeOf)
577589
.map(Value::asRelationship)
578-
.filter(onlyWithMatchingType)
579-
.forEach(allMatchingTypeRelationshipsInResult::add);
590+
.forEach(allRelationshipsInResult::add);
580591
}
581592

582-
return allMatchingTypeRelationshipsInResult;
593+
return allRelationshipsInResult;
583594
}
584595

585596
private static Value extractValueOf(Neo4jPersistentProperty property, MapAccessor propertyContainer) {

0 commit comments

Comments
 (0)