diff --git a/pom.xml b/pom.xml
index 0fed49575..21e6b92b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,7 +75,7 @@
1.11.0
2.0.SP1
8.40
- 2022.4.0
+ 2022.8.5
spring-data-neo4j
SDNEO4J
1.2.5
diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java b/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java
index 2a5c5e281..57a088507 100644
--- a/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java
+++ b/src/main/java/org/springframework/data/neo4j/core/mapping/CypherGenerator.java
@@ -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()) {
diff --git a/src/main/java/org/springframework/data/neo4j/repository/query/Neo4jSpelSupport.java b/src/main/java/org/springframework/data/neo4j/repository/query/Neo4jSpelSupport.java
index c45b5c829..847c725ad 100644
--- a/src/main/java/org/springframework/data/neo4j/repository/query/Neo4jSpelSupport.java
+++ b/src/main/java/org/springframework/data/neo4j/repository/query/Neo4jSpelSupport.java
@@ -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