GH-2809 - Find the right id/element id function for assignedIds.
* Avoid using the outdated id function wherever possible. * Never fall back to `toString(id((element))` (in Neo4j 4.4 scenarios) Closes #2809
This commit is contained in:
@@ -401,7 +401,7 @@ public final class Neo4jTemplate implements
|
||||
neo4jMappingContext.getRequiredBinderFunctionFor((Class<T>) entityToBeSaved.getClass())
|
||||
);
|
||||
Optional<Entity> newOrUpdatedNode = neo4jClient
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels)))
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer))))
|
||||
.bind(entityToBeSaved)
|
||||
.with(binderFunction)
|
||||
.fetchAs(Entity.class)
|
||||
@@ -416,7 +416,7 @@ public final class Neo4jTemplate implements
|
||||
}
|
||||
|
||||
Object elementId = newOrUpdatedNode.map(node -> {
|
||||
if (!entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds()) {
|
||||
if (!entityMetaData.isUsingDeprecatedInternalId() && TemplateSupport.rendererRendersElementId(renderer)) {
|
||||
return IdentitySupport.getElementId(node);
|
||||
}
|
||||
return node.id();
|
||||
@@ -764,6 +764,7 @@ public final class Neo4jTemplate implements
|
||||
// Remove all relationships before creating all new if the entity is not new and the relationship
|
||||
// has not been processed before.
|
||||
// This avoids the usage of cache but might have significant impact on overall performance
|
||||
boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer);
|
||||
if (!isParentObjectNew && !stateMachine.hasProcessedRelationship(fromId, relationshipDescription)) {
|
||||
|
||||
List<Object> knownRelationshipsIds = new ArrayList<>();
|
||||
@@ -780,7 +781,7 @@ public final class Neo4jTemplate implements
|
||||
}
|
||||
}
|
||||
|
||||
Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription);
|
||||
Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription, canUseElementId);
|
||||
|
||||
neo4jClient.query(renderer.render(relationshipRemoveQuery))
|
||||
.bind(convertIdValues(sourceEntity.getIdProperty(), fromId)) //
|
||||
@@ -858,7 +859,7 @@ public final class Neo4jTemplate implements
|
||||
// create new dynamic relationship properties
|
||||
if (relationshipDescription.hasRelationshipProperties() && isNewRelationship && idProperty != null) {
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship(
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, true);
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, true, canUseElementId);
|
||||
|
||||
List<Object> row = Collections.singletonList(properties);
|
||||
statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, row);
|
||||
@@ -879,7 +880,7 @@ public final class Neo4jTemplate implements
|
||||
} else { // plain (new or to update) dynamic relationship or dynamic relationships with properties to update
|
||||
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship(
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, false);
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, false, canUseElementId);
|
||||
|
||||
List<Object> row = Collections.singletonList(properties);
|
||||
statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, row);
|
||||
@@ -921,7 +922,7 @@ public final class Neo4jTemplate implements
|
||||
// batch operations
|
||||
if (!(relationshipDescription.hasRelationshipProperties() || relationshipDescription.isDynamic() || plainRelationshipRows.isEmpty())) {
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeSimpleRelationshipBatch(
|
||||
sourceEntity, relationshipDescription, plainRelationshipRows);
|
||||
sourceEntity, relationshipDescription, plainRelationshipRows, canUseElementId);
|
||||
statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, plainRelationshipRows);
|
||||
neo4jClient.query(renderer.render(statementHolder.getStatement()))
|
||||
.bindAll(statementHolder.getProperties())
|
||||
@@ -929,7 +930,7 @@ public final class Neo4jTemplate implements
|
||||
} else if (relationshipDescription.hasRelationshipProperties()) {
|
||||
if (!relationshipPropertiesRows.isEmpty()) {
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeRelationshipsWithPropertiesBatch(false,
|
||||
sourceEntity, relationshipDescription, updateRelatedValuesToStore, relationshipPropertiesRows);
|
||||
sourceEntity, relationshipDescription, updateRelatedValuesToStore, relationshipPropertiesRows, canUseElementId);
|
||||
statementHolder = statementHolder.addProperty(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM, relationshipPropertiesRows);
|
||||
|
||||
neo4jClient.query(renderer.render(statementHolder.getStatement()))
|
||||
@@ -938,7 +939,7 @@ public final class Neo4jTemplate implements
|
||||
}
|
||||
if (!newRelatedValuesToStore.isEmpty()) {
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForImperativeRelationshipsWithPropertiesBatch(true,
|
||||
sourceEntity, relationshipDescription, newRelatedValuesToStore, newRelationshipPropertiesRows);
|
||||
sourceEntity, relationshipDescription, newRelatedValuesToStore, newRelationshipPropertiesRows, canUseElementId);
|
||||
List<Object> all = new ArrayList<>(neo4jClient.query(renderer.render(statementHolder.getStatement()))
|
||||
.bindAll(statementHolder.getProperties())
|
||||
.fetchAs(Object.class)
|
||||
@@ -996,7 +997,7 @@ public final class Neo4jTemplate implements
|
||||
return tree;
|
||||
});
|
||||
Optional<Entity> optionalSavedNode = neo4jClient
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels)))
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer))))
|
||||
.bind(entity).with(binderFunction)
|
||||
.fetchAs(Entity.class)
|
||||
.one();
|
||||
|
||||
@@ -438,7 +438,8 @@ public final class ReactiveNeo4jTemplate implements
|
||||
includedProperties, entityMetaData,
|
||||
neo4jMappingContext.getRequiredBinderFunctionFor((Class<T>) entityToBeSaved.getClass()));
|
||||
|
||||
Mono<Entity> idMono = this.neo4jClient.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels)))
|
||||
boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer);
|
||||
Mono<Entity> idMono = this.neo4jClient.query(() -> renderer.render(cypherGenerator.prepareSaveOf(entityMetaData, dynamicLabels, canUseElementId)))
|
||||
.bind(entityToBeSaved)
|
||||
.with(binderFunction)
|
||||
.fetchAs(Entity.class)
|
||||
@@ -452,7 +453,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
|
||||
PersistentPropertyAccessor<T> propertyAccessor = entityMetaData.getPropertyAccessor(entityToBeSaved);
|
||||
return idMono.doOnNext(newOrUpdatedNode -> {
|
||||
var elementId = !entityMetaData.isUsingDeprecatedInternalId() && entityMetaData.isUsingInternalIds()
|
||||
var elementId = !entityMetaData.isUsingDeprecatedInternalId() && canUseElementId
|
||||
? IdentitySupport.getElementId(newOrUpdatedNode)
|
||||
: newOrUpdatedNode.id();
|
||||
TemplateSupport.setGeneratedIdIfNecessary(entityMetaData, propertyAccessor, elementId, Optional.of(newOrUpdatedNode));
|
||||
@@ -901,6 +902,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
// Remove all relationships before creating all new if the entity is not new and the relationship
|
||||
// has not been processed before.
|
||||
// This avoids the usage of cache but might have significant impact on overall performance
|
||||
boolean canUseElementId = TemplateSupport.rendererRendersElementId(renderer);
|
||||
if (!isParentObjectNew && !stateMachine.hasProcessedRelationship(fromId, relationshipDescription)) {
|
||||
|
||||
List<Object> knownRelationshipsIds = new ArrayList<>();
|
||||
@@ -919,7 +921,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
}
|
||||
}
|
||||
|
||||
Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription);
|
||||
Statement relationshipRemoveQuery = cypherGenerator.prepareDeleteOf(sourceEntity, relationshipDescription, canUseElementId);
|
||||
|
||||
relationshipDeleteMonos.add(
|
||||
neo4jClient.query(renderer.render(relationshipRemoveQuery))
|
||||
@@ -990,7 +992,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
|
||||
boolean isNewRelationship = idValue == null;
|
||||
CreateRelationshipStatementHolder statementHolder = neo4jMappingContext.createStatementForSingleRelationship(
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, isNewRelationship);
|
||||
sourceEntity, relationshipDescription, relatedValueToStore, isNewRelationship, canUseElementId);
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(Constants.FROM_ID_PARAMETER_NAME, convertIdValues(sourceEntity.getRequiredIdProperty(), fromId));
|
||||
@@ -1086,7 +1088,7 @@ public final class ReactiveNeo4jTemplate implements
|
||||
return tree;
|
||||
});
|
||||
return neo4jClient
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels)))
|
||||
.query(() -> renderer.render(cypherGenerator.prepareSaveOf(targetNodeDescription, dynamicLabels, TemplateSupport.rendererRendersElementId(renderer))))
|
||||
.bind(entity).with(binderFunction)
|
||||
.fetchAs(Entity.class)
|
||||
.one();
|
||||
|
||||
@@ -427,10 +427,10 @@ public final class TemplateSupport {
|
||||
* @return {@literal true} if renderer will use elementId
|
||||
*/
|
||||
static boolean rendererCanUseElementIdIfPresent(Renderer renderer, Neo4jPersistentEntity<?> targetEntity) {
|
||||
return !targetEntity.isUsingDeprecatedInternalId() && targetEntity.isUsingInternalIds() && rendererRendersElementId(renderer);
|
||||
return !targetEntity.isUsingDeprecatedInternalId() && rendererRendersElementId(renderer);
|
||||
}
|
||||
|
||||
private static boolean rendererRendersElementId(Renderer renderer) {
|
||||
static boolean rendererRendersElementId(Renderer renderer) {
|
||||
return renderer.render(Cypher.returning(Functions.elementId(Cypher.anyNode("n"))).build())
|
||||
.equals("RETURN elementId(n)");
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ public enum CypherGenerator {
|
||||
}
|
||||
|
||||
public Statement prepareSaveOf(NodeDescription<?> nodeDescription,
|
||||
UnaryOperator<OngoingMatchAndUpdate> updateDecorator) {
|
||||
UnaryOperator<OngoingMatchAndUpdate> updateDecorator, boolean canUseElementId) {
|
||||
|
||||
String primaryLabel = nodeDescription.getPrimaryLabel();
|
||||
List<String> additionalLabels = nodeDescription.getAdditionalLabels();
|
||||
@@ -367,7 +367,7 @@ public enum CypherGenerator {
|
||||
Statement updateIfExists;
|
||||
|
||||
var neo4jPersistentEntity = (Neo4jPersistentEntity<?>) nodeDescription;
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity);
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId);
|
||||
|
||||
if (neo4jPersistentEntity.hasVersionProperty()) {
|
||||
Property versionProperty = rootNode.property(neo4jPersistentEntity.getRequiredVersionProperty().getName());
|
||||
@@ -436,7 +436,7 @@ public enum CypherGenerator {
|
||||
|
||||
@NonNull
|
||||
public Statement prepareSaveOfRelationship(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationship, @Nullable String dynamicRelationshipType) {
|
||||
RelationshipDescription relationship, @Nullable String dynamicRelationshipType, boolean canUseElementId) {
|
||||
final Node startNode = neo4jPersistentEntity.isUsingInternalIds()
|
||||
? anyNode(START_NODE_NAME)
|
||||
: node(neo4jPersistentEntity.getPrimaryLabel(), neo4jPersistentEntity.getAdditionalLabels())
|
||||
@@ -450,22 +450,22 @@ public enum CypherGenerator {
|
||||
startNode.relationshipTo(endNode, type) :
|
||||
startNode.relationshipFrom(endNode, type)).named(RELATIONSHIP_NAME);
|
||||
|
||||
var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity);
|
||||
var startNodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId);
|
||||
return match(startNode)
|
||||
.where(startNodeIdFunction.apply(startNode).isEqualTo(idParameter))
|
||||
.match(endNode)
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
|
||||
.merge(relationshipFragment)
|
||||
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Function<Node, Expression> getNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity) {
|
||||
private static Function<Node, Expression> getNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity, boolean canUseElementId) {
|
||||
|
||||
Function<Node, Expression> startNodeIdFunction;
|
||||
var idProperty = entity.getRequiredIdProperty();
|
||||
if (entity.isUsingInternalIds()) {
|
||||
if (entity.isUsingDeprecatedInternalId()) {
|
||||
if (entity.isUsingDeprecatedInternalId() || !canUseElementId) {
|
||||
startNodeIdFunction = Functions::id;
|
||||
} else {
|
||||
startNodeIdFunction = Functions::elementId;
|
||||
@@ -476,13 +476,13 @@ public enum CypherGenerator {
|
||||
return startNodeIdFunction;
|
||||
}
|
||||
|
||||
private static Function<Node, Expression> getEndNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity) {
|
||||
private static Function<Node, Expression> getEndNodeIdFunction(@Nullable Neo4jPersistentEntity<?> entity, boolean canUseElementId) {
|
||||
|
||||
Function<Node, Expression> startNodeIdFunction;
|
||||
if (entity == null) {
|
||||
return Functions::elementId;
|
||||
}
|
||||
if (!entity.isUsingDeprecatedInternalId() && entity.isUsingInternalIds()) {
|
||||
if (!entity.isUsingDeprecatedInternalId() && canUseElementId) {
|
||||
startNodeIdFunction = Functions::elementId;
|
||||
} else {
|
||||
startNodeIdFunction = Functions::id;
|
||||
@@ -490,12 +490,12 @@ public enum CypherGenerator {
|
||||
return startNodeIdFunction;
|
||||
}
|
||||
|
||||
private static Function<Relationship, Expression> getRelationshipIdFunction(RelationshipDescription relationshipDescription) {
|
||||
private static Function<Relationship, Expression> getRelationshipIdFunction(RelationshipDescription relationshipDescription, boolean canUseElementId) {
|
||||
|
||||
Function<Relationship, Expression> result = Functions::elementId;
|
||||
Function<Relationship, Expression> result = canUseElementId ? Functions::elementId : Functions::id;
|
||||
if (relationshipDescription.hasRelationshipProperties()) {
|
||||
Neo4jPersistentEntity<?> entity = (Neo4jPersistentEntity<?>) relationshipDescription.getRelationshipPropertiesEntity();
|
||||
if (entity != null && entity.isUsingDeprecatedInternalId()) {
|
||||
if ((entity != null && entity.isUsingDeprecatedInternalId()) || !canUseElementId) {
|
||||
result = Functions::id;
|
||||
} else {
|
||||
result = Functions::elementId;
|
||||
@@ -506,7 +506,7 @@ public enum CypherGenerator {
|
||||
|
||||
@NonNull
|
||||
public Statement prepareSaveOfRelationships(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationship, @Nullable String dynamicRelationshipType) {
|
||||
RelationshipDescription relationship, @Nullable String dynamicRelationshipType, boolean canUseElementId) {
|
||||
|
||||
final Node startNode = neo4jPersistentEntity.isUsingInternalIds()
|
||||
? anyNode(START_NODE_NAME)
|
||||
@@ -525,9 +525,9 @@ public enum CypherGenerator {
|
||||
return Cypher.unwind(parameter(Constants.NAME_OF_RELATIONSHIP_LIST_PARAM)).as(row)
|
||||
.with(row)
|
||||
.match(startNode)
|
||||
.where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idProperty))
|
||||
.where(getNodeIdFunction(neo4jPersistentEntity, canUseElementId).apply(startNode).isEqualTo(idProperty))
|
||||
.match(endNode)
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
|
||||
.merge(relationshipFragment)
|
||||
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))
|
||||
.build();
|
||||
@@ -537,7 +537,8 @@ public enum CypherGenerator {
|
||||
public Statement prepareSaveOfRelationshipWithProperties(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationship,
|
||||
boolean isNew,
|
||||
@Nullable String dynamicRelationshipType) {
|
||||
@Nullable String dynamicRelationshipType,
|
||||
boolean canUseElementId) {
|
||||
|
||||
Assert.isTrue(relationship.hasRelationshipProperties(),
|
||||
"Properties required to create a relationship with properties");
|
||||
@@ -555,13 +556,13 @@ public enum CypherGenerator {
|
||||
startNode.relationshipFrom(endNode, type))
|
||||
.named(RELATIONSHIP_NAME);
|
||||
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity);
|
||||
var relationshipIdFunction = getRelationshipIdFunction(relationship);
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId);
|
||||
var relationshipIdFunction = getRelationshipIdFunction(relationship, canUseElementId);
|
||||
|
||||
StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode)
|
||||
.where(nodeIdFunction.apply(startNode).isEqualTo(idParameter))
|
||||
.match(endNode)
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
|
||||
|
||||
StatementBuilder.ExposesSet createOrMatch = isNew
|
||||
? startAndEndNodeMatch.create(relationshipFragment)
|
||||
@@ -575,7 +576,7 @@ public enum CypherGenerator {
|
||||
|
||||
@NonNull
|
||||
public Statement prepareUpdateOfRelationshipsWithProperties(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationship, boolean isNew) {
|
||||
RelationshipDescription relationship, boolean isNew, boolean canUseElementId) {
|
||||
|
||||
Assert.isTrue(relationship.hasRelationshipProperties(),
|
||||
"Properties required to create a relationship with properties");
|
||||
@@ -598,8 +599,8 @@ public enum CypherGenerator {
|
||||
.as(row)
|
||||
.with(row);
|
||||
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity);
|
||||
var relationshipIdFunction = getRelationshipIdFunction(relationship);
|
||||
var nodeIdFunction = getNodeIdFunction(neo4jPersistentEntity, canUseElementId);
|
||||
var relationshipIdFunction = getRelationshipIdFunction(relationship, canUseElementId);
|
||||
|
||||
// we only need start and end node querying if we have to create a new relationship...
|
||||
if (isNew) {
|
||||
@@ -607,7 +608,7 @@ public enum CypherGenerator {
|
||||
.match(startNode)
|
||||
.where(nodeIdFunction.apply(startNode).isEqualTo(idProperty))
|
||||
.match(endNode)
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget()).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
|
||||
.where(getEndNodeIdFunction((Neo4jPersistentEntity<?>) relationship.getTarget(), canUseElementId).apply(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
|
||||
.create(relationshipFragment)
|
||||
.mutate(RELATIONSHIP_NAME, relationshipProperties)
|
||||
.returning(getReturnedIdExpressionsForRelationship(relationship, relationshipFragment))
|
||||
@@ -632,7 +633,8 @@ public enum CypherGenerator {
|
||||
@NonNull
|
||||
public Statement prepareDeleteOf(
|
||||
Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipDescription
|
||||
RelationshipDescription relationshipDescription,
|
||||
boolean canUseElementId
|
||||
) {
|
||||
final Node startNode = neo4jPersistentEntity.isUsingInternalIds() ? anyNode(START_NODE_NAME)
|
||||
: node(neo4jPersistentEntity.getPrimaryLabel(), neo4jPersistentEntity.getAdditionalLabels())
|
||||
@@ -652,8 +654,8 @@ public enum CypherGenerator {
|
||||
|
||||
Parameter<?> idParameter = parameter(Constants.FROM_ID_PARAMETER_NAME);
|
||||
return match(relationship)
|
||||
.where(getNodeIdFunction(neo4jPersistentEntity).apply(startNode).isEqualTo(idParameter))
|
||||
.and(getRelationshipIdFunction(relationshipDescription).apply(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not())
|
||||
.where(getNodeIdFunction(neo4jPersistentEntity, canUseElementId).apply(startNode).isEqualTo(idParameter))
|
||||
.and(getRelationshipIdFunction(relationshipDescription, canUseElementId).apply(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not())
|
||||
.delete(relationship.getRequiredSymbolicName())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -538,29 +538,31 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
|
||||
public CreateRelationshipStatementHolder createStatementForImperativeSimpleRelationshipBatch(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipDescription,
|
||||
List<Object> plainRelationshipRows) {
|
||||
List<Object> plainRelationshipRows, boolean canUseElementId) {
|
||||
|
||||
return createStatementForSingleRelationship(neo4jPersistentEntity, (DefaultRelationshipDescription) relationshipDescription,
|
||||
plainRelationshipRows);
|
||||
plainRelationshipRows, canUseElementId);
|
||||
}
|
||||
|
||||
public CreateRelationshipStatementHolder createStatementForImperativeRelationshipsWithPropertiesBatch(boolean isNew,
|
||||
Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipDescription,
|
||||
Object relatedValues,
|
||||
List<Map<String, Object>> relationshipPropertiesRows) {
|
||||
List<Map<String, Object>> relationshipPropertiesRows,
|
||||
boolean canUseElementId) {
|
||||
|
||||
List<MappingSupport.RelationshipPropertiesWithEntityHolder> relationshipPropertyValues = ((Collection<?>) relatedValues).stream()
|
||||
.map(MappingSupport.RelationshipPropertiesWithEntityHolder.class::cast).collect(Collectors.toList());
|
||||
|
||||
return createStatementForRelationshipWithPropertiesBatch(isNew, neo4jPersistentEntity, relationshipDescription,
|
||||
relationshipPropertyValues, relationshipPropertiesRows);
|
||||
relationshipPropertyValues, relationshipPropertiesRows, canUseElementId);
|
||||
}
|
||||
|
||||
public CreateRelationshipStatementHolder createStatementForSingleRelationship(Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipContext,
|
||||
Object relatedValue,
|
||||
boolean isNewRelationship) {
|
||||
boolean isNewRelationship,
|
||||
boolean canUseElementId) {
|
||||
|
||||
if (relationshipContext.hasRelationshipProperties()) {
|
||||
MappingSupport.RelationshipPropertiesWithEntityHolder relatedValueEntityHolder =
|
||||
@@ -586,22 +588,22 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
}
|
||||
return createStatementForRelationshipWithProperties(
|
||||
neo4jPersistentEntity, relationshipContext,
|
||||
dynamicRelationshipType, relatedValueEntityHolder, isNewRelationship
|
||||
dynamicRelationshipType, relatedValueEntityHolder, isNewRelationship, canUseElementId
|
||||
);
|
||||
} else {
|
||||
return createStatementForSingleRelationship(neo4jPersistentEntity, (DefaultRelationshipDescription) relationshipContext,
|
||||
relatedValue);
|
||||
relatedValue, canUseElementId);
|
||||
}
|
||||
}
|
||||
|
||||
private CreateRelationshipStatementHolder createStatementForRelationshipWithProperties(
|
||||
Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipDescription, @Nullable String dynamicRelationshipType,
|
||||
MappingSupport.RelationshipPropertiesWithEntityHolder relatedValue, boolean isNewRelationship) {
|
||||
MappingSupport.RelationshipPropertiesWithEntityHolder relatedValue, boolean isNewRelationship, boolean canUseElementId) {
|
||||
|
||||
Statement relationshipCreationQuery = CypherGenerator.INSTANCE.prepareSaveOfRelationshipWithProperties(
|
||||
neo4jPersistentEntity, relationshipDescription, isNewRelationship,
|
||||
dynamicRelationshipType);
|
||||
dynamicRelationshipType, canUseElementId);
|
||||
|
||||
Map<String, Object> propMap = new HashMap<>();
|
||||
// write relationship properties
|
||||
@@ -615,10 +617,11 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
RelationshipDescription relationshipDescription,
|
||||
List<MappingSupport.RelationshipPropertiesWithEntityHolder> relatedValues,
|
||||
List<Map<String, Object>> relationshipPropertiesRows) {
|
||||
List<Map<String, Object>> relationshipPropertiesRows,
|
||||
boolean canUseElementId) {
|
||||
|
||||
Statement relationshipCreationQuery = CypherGenerator.INSTANCE
|
||||
.prepareUpdateOfRelationshipsWithProperties(neo4jPersistentEntity, relationshipDescription, isNew);
|
||||
.prepareUpdateOfRelationshipsWithProperties(neo4jPersistentEntity, relationshipDescription, isNew, canUseElementId);
|
||||
List<Object> relationshipRows = new ArrayList<>();
|
||||
Map<String, Object> relationshipPropertiesEntries = new HashMap<>();
|
||||
if (isNew) {
|
||||
@@ -636,7 +639,7 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
|
||||
private CreateRelationshipStatementHolder createStatementForSingleRelationship(
|
||||
Neo4jPersistentEntity<?> neo4jPersistentEntity,
|
||||
DefaultRelationshipDescription relationshipDescription, Object relatedValue) {
|
||||
DefaultRelationshipDescription relationshipDescription, Object relatedValue, boolean canUseElementId) {
|
||||
|
||||
String relationshipType;
|
||||
if (!relationshipDescription.isDynamic()) {
|
||||
@@ -649,7 +652,7 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
}
|
||||
|
||||
Statement relationshipCreationQuery = CypherGenerator.INSTANCE.prepareSaveOfRelationships(
|
||||
neo4jPersistentEntity, relationshipDescription, relationshipType);
|
||||
neo4jPersistentEntity, relationshipDescription, relationshipType, canUseElementId);
|
||||
return new CreateRelationshipStatementHolder(relationshipCreationQuery, Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,12 @@ public interface Neo4jPersistentEntity<T>
|
||||
* @return True if the underlying domain classes uses {@code id()} to compute internally generated ids.
|
||||
*/
|
||||
default boolean isUsingDeprecatedInternalId() {
|
||||
for (NodeDescription<?> nodeDescription : getChildNodeDescriptionsInHierarchy()) {
|
||||
if (nodeDescription.isUsingInternalIds() && ((Neo4jPersistentEntity<?>) nodeDescription).getIdProperty() != null
|
||||
&& Neo4jPersistentEntity.DEPRECATED_GENERATED_ID_TYPES.contains(((Neo4jPersistentEntity<?>) nodeDescription).getIdProperty().getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return isUsingInternalIds() && Neo4jPersistentEntity.DEPRECATED_GENERATED_ID_TYPES.contains(getRequiredIdProperty().getType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class CypherGeneratorTest {
|
||||
when(relationshipDescription.isDynamic()).thenReturn(true);
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity,
|
||||
relationshipDescription, "REL");
|
||||
relationshipDescription, "REL", true);
|
||||
|
||||
String expectedQuery = "MATCH (startNode:`Entity1`) WHERE startNode.id = $fromId MATCH (endNode)"
|
||||
+ " WHERE elementId(endNode) = $toId MERGE (startNode)<-[relProps:`REL`]-(endNode) RETURN elementId(relProps) AS __elementId__";
|
||||
@@ -71,7 +71,7 @@ class CypherGeneratorTest {
|
||||
when(relationshipDescription.isDynamic()).thenReturn(true);
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity,
|
||||
relationshipDescription, "REL");
|
||||
relationshipDescription, "REL", true);
|
||||
|
||||
String expectedQuery =
|
||||
"MATCH (startNode:`Entity1`:`MultipleLabel`) WHERE startNode.id = $fromId MATCH (endNode)"
|
||||
@@ -92,7 +92,7 @@ class CypherGeneratorTest {
|
||||
when(persistentEntity.isUsingDeprecatedInternalId()).thenReturn(true);
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareSaveOfRelationship(persistentEntity,
|
||||
relationshipDescription, "REL");
|
||||
relationshipDescription, "REL", true);
|
||||
|
||||
String expectedQuery = "MATCH (startNode) WHERE id(startNode) = $fromId MATCH (endNode)"
|
||||
+ " WHERE elementId(endNode) = $toId MERGE (startNode)<-[relProps:`REL`]-(endNode) RETURN elementId(relProps) AS __elementId__";
|
||||
@@ -106,7 +106,7 @@ class CypherGeneratorTest {
|
||||
RelationshipDescription relationshipDescription = Mockito.mock(RelationshipDescription.class);
|
||||
doReturn(relatedEntity).when(relationshipDescription).getTarget();
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription);
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true);
|
||||
|
||||
String expectedQuery = "MATCH (startNode:`Entity1`)<-[rel]-(:`Entity2`) WHERE (startNode.id = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel";
|
||||
Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement));
|
||||
@@ -121,7 +121,7 @@ class CypherGeneratorTest {
|
||||
RelationshipDescription relationshipDescription = Mockito.mock(RelationshipDescription.class);
|
||||
doReturn(relatedEntity).when(relationshipDescription).getTarget();
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription);
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true);
|
||||
|
||||
String expectedQuery = "MATCH (startNode:`Entity1`:`MultipleLabel`)<-[rel]-(:`Entity2`:`MultipleLabel`) WHERE (startNode.id = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel";
|
||||
Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement));
|
||||
@@ -143,7 +143,7 @@ class CypherGeneratorTest {
|
||||
when(persistentEntity.getRequiredIdProperty()).thenReturn(persistentProperty);
|
||||
when(persistentEntity.isUsingDeprecatedInternalId()).thenReturn(true);
|
||||
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription);
|
||||
Statement statement = CypherGenerator.INSTANCE.prepareDeleteOf(persistentEntity, relationshipDescription, true);
|
||||
|
||||
String expectedQuery = "MATCH (startNode)<-[rel]-(:`Entity2`) WHERE (id(startNode) = $fromId AND NOT (elementId(rel) IN $__knownRelationShipIds__)) DELETE rel";
|
||||
Assertions.assertEquals(expectedQuery, Renderer.getRenderer(Configuration.newConfig().withDialect(Dialect.NEO4J_5).build()).render(statement));
|
||||
|
||||
Reference in New Issue
Block a user