GH-2681 - Sanitize label replacements.

This requires an update of the CypherDSL to use the built-in
function instead of copying code.
This commit is contained in:
Gerrit Meier
2023-02-27 09:17:22 +01:00
parent af863985b8
commit eeb54cd23e
3 changed files with 28 additions and 16 deletions

View File

@@ -75,7 +75,7 @@
<byte-buddy.version>1.11.0</byte-buddy.version>
<cdi>2.0.SP1</cdi>
<checkstyle.version>8.40</checkstyle.version>
<cypher-dsl.version>2022.4.0</cypher-dsl.version>
<cypher-dsl.version>2022.8.5</cypher-dsl.version>
<dist.id>spring-data-neo4j</dist.id>
<dist.key>SDNEO4J</dist.key>
<flatten-maven-plugin.version>1.2.5</flatten-maven-plugin.version>

View File

@@ -39,6 +39,7 @@ import org.neo4j.cypherdsl.core.Condition;
import org.neo4j.cypherdsl.core.Conditions;
import org.neo4j.cypherdsl.core.Cypher;
import org.neo4j.cypherdsl.core.Expression;
import org.neo4j.cypherdsl.core.FunctionInvocation;
import org.neo4j.cypherdsl.core.Functions;
import org.neo4j.cypherdsl.core.MapProjection;
import org.neo4j.cypherdsl.core.Node;
@@ -54,6 +55,7 @@ import org.neo4j.cypherdsl.core.StatementBuilder.OngoingUpdate;
import org.neo4j.cypherdsl.core.SymbolicName;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.neo4j.cypherdsl.core.renderer.Renderer;
import org.neo4j.cypherdsl.core.utils.Assertions;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentProperty;
@@ -62,6 +64,8 @@ import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import javax.lang.model.SourceVersion;
/**
* A generator based on the schema defined by node and relationship descriptions. Most methods return renderable Cypher
* statements.
@@ -348,7 +352,7 @@ public enum CypherGenerator {
Property versionProperty = rootNode.property(((Neo4jPersistentEntity<?>) nodeDescription).getRequiredVersionProperty().getName());
createIfNew = updateDecorator.apply(optionalMatch(possibleExistingNode)
.where(possibleExistingNode.internalId().isEqualTo(idParameter))
.where(idFunction(possibleExistingNode).isEqualTo(idParameter))
.with(possibleExistingNode)
.where(possibleExistingNode.isNull())
.create(rootNode.withProperties(versionProperty, literalOf(0)))
@@ -358,7 +362,7 @@ public enum CypherGenerator {
.build();
updateIfExists = updateDecorator.apply(match(rootNode)
.where(rootNode.internalId().isEqualTo(idParameter))
.where(idFunction(rootNode).isEqualTo(idParameter))
.and(versionProperty.isEqualTo(parameter(Constants.NAME_OF_VERSION_PARAM))) // Initial check
.set(versionProperty.to(versionProperty.add(literalOf(1)))) // Acquire lock
.with(rootNode)
@@ -368,12 +372,12 @@ public enum CypherGenerator {
.returning(rootNode).build();
} else {
createIfNew = updateDecorator
.apply(optionalMatch(possibleExistingNode).where(possibleExistingNode.internalId().isEqualTo(idParameter))
.apply(optionalMatch(possibleExistingNode).where(idFunction(possibleExistingNode).isEqualTo(idParameter))
.with(possibleExistingNode).where(possibleExistingNode.isNull()).create(rootNode)
.set(rootNode, parameter(Constants.NAME_OF_PROPERTIES_PARAM)))
.returning(rootNode).build();
updateIfExists = updateDecorator.apply(match(rootNode).where(rootNode.internalId().isEqualTo(idParameter))
updateIfExists = updateDecorator.apply(match(rootNode).where(idFunction(rootNode).isEqualTo(idParameter))
.mutate(rootNode, parameter(Constants.NAME_OF_PROPERTIES_PARAM))).returning(rootNode).build();
}
@@ -381,6 +385,10 @@ public enum CypherGenerator {
}
}
private static FunctionInvocation idFunction(Node rootNode) {
return Functions.id(rootNode);
}
public Statement prepareSaveOfMultipleInstancesOf(NodeDescription<?> nodeDescription) {
Assert.isTrue(!nodeDescription.isUsingInternalIds(),
@@ -398,7 +406,7 @@ public enum CypherGenerator {
return Cypher.unwind(parameter(Constants.NAME_OF_ENTITY_LIST_PARAM)).as(row)
.merge(rootNode.withProperties(nameOfIdProperty, Cypher.property(row, Constants.NAME_OF_ID)))
.mutate(rootNode, Cypher.property(row, Constants.NAME_OF_PROPERTIES_PARAM))
.returning(rootNode.internalId().as(Constants.NAME_OF_INTERNAL_ID), rootNode.property(nameOfIdProperty).as(Constants.NAME_OF_ID))
.returning(idFunction(rootNode).as(Constants.NAME_OF_INTERNAL_ID), rootNode.property(nameOfIdProperty).as(Constants.NAME_OF_ID))
.build();
}
@@ -420,9 +428,9 @@ public enum CypherGenerator {
startNode.relationshipFrom(endNode, type)).named(RELATIONSHIP_NAME);
return match(startNode)
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
: startNode.property(idPropertyName).isEqualTo(idParameter))
.match(endNode).where(endNode.internalId().isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
.match(endNode).where(idFunction(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)))
.merge(relationshipFragment)
.returning(Functions.id(relationshipFragment))
.build();
@@ -450,9 +458,9 @@ public enum CypherGenerator {
.with(row)
.match(startNode)
.where(neo4jPersistentEntity.isUsingInternalIds()
? startNode.internalId().isEqualTo(idProperty)
? idFunction(startNode).isEqualTo(idProperty)
: startNode.property(idPropertyName).isEqualTo(idProperty))
.match(endNode).where(endNode.internalId().isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.match(endNode).where(idFunction(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.merge(relationshipFragment)
.returning(Functions.id(relationshipFragment))
.build();
@@ -482,9 +490,9 @@ public enum CypherGenerator {
.named(RELATIONSHIP_NAME);
StatementBuilder.OngoingReadingWithWhere startAndEndNodeMatch = match(startNode)
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
: startNode.property(idPropertyName).isEqualTo(idParameter))
.match(endNode).where(endNode.internalId().isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
.match(endNode).where(idFunction(endNode).isEqualTo(parameter(Constants.TO_ID_PARAMETER_NAME)));
StatementBuilder.ExposesSet createOrMatch = isNew
? startAndEndNodeMatch.create(relationshipFragment)
@@ -527,10 +535,10 @@ public enum CypherGenerator {
.match(startNode)
.where(
neo4jPersistentEntity.isUsingInternalIds()
? startNode.internalId().isEqualTo(idProperty)
? idFunction(startNode).isEqualTo(idProperty)
: startNode.property(idPropertyName).isEqualTo(idProperty)
)
.match(endNode).where(endNode.internalId().isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.match(endNode).where(idFunction(endNode).isEqualTo(Cypher.property(row, Constants.TO_ID_PARAMETER_NAME)))
.create(relationshipFragment)
.mutate(RELATIONSHIP_NAME, relationshipProperties)
.returning(Functions.id(relationshipFragment)).build();
@@ -566,7 +574,7 @@ public enum CypherGenerator {
Parameter<?> idParameter = parameter(Constants.FROM_ID_PARAMETER_NAME);
return match(relationship)
.where(neo4jPersistentEntity.isUsingInternalIds() ? startNode.internalId().isEqualTo(idParameter)
.where(neo4jPersistentEntity.isUsingInternalIds() ? idFunction(startNode).isEqualTo(idParameter)
: startNode.property(idPropertyName).isEqualTo(idParameter))
.and(Functions.id(relationship).in(Cypher.parameter(Constants.NAME_OF_KNOWN_RELATIONSHIPS_PARAM)).not())
.delete(relationship.getRequiredSymbolicName())
@@ -613,6 +621,7 @@ public enum CypherGenerator {
}
expression = Cypher.property(property.substring(0, firstDot), tail);
} else {
Assertions.isTrue(SourceVersion.isIdentifier(property), "Name must be a valid identifier.");
expression = Cypher.name(property);
}
if (order.isIgnoreCase()) {

View File

@@ -26,6 +26,7 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.LogFactory;
import org.apiguardian.api.API;
import org.neo4j.cypherdsl.core.internal.SchemaNames;
import org.springframework.core.log.LogAccessor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -111,7 +112,9 @@ public final class Neo4jSpelSupport {
private static String joinStrings(Object arg, String joinOn) {
if (arg instanceof Collection) {
return ((Collection<?>) arg).stream().map(Object::toString).collect(Collectors.joining(joinOn));
return ((Collection<?>) arg).stream()
.map(o -> SchemaNames.sanitize(o.toString()).get())
.collect(Collectors.joining(joinOn));
}
// we are so kind and also accept plain strings instead of collection<string>