Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>6.2.0-SNAPSHOT</version>
<version>6.2.0-NODE-NAMES-SNAPSHOT</version>

<name>Spring Data Neo4j</name>
<description>Next generation Object-Graph-Mapping for Spring Data.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.neo4j.cypherdsl.core.Node;
import org.neo4j.cypherdsl.core.StatementBuilder.OngoingMatchAndUpdate;
import org.springframework.data.neo4j.core.mapping.Constants;
import org.springframework.data.neo4j.core.mapping.NodeDescription;
import org.springframework.lang.Nullable;

/**
* Decorator for an ongoing update statement that removes obsolete dynamic labels and adds new ones.
Expand All @@ -33,16 +35,17 @@
*/
final class DynamicLabels implements UnaryOperator<OngoingMatchAndUpdate> {

public static final DynamicLabels EMPTY = new DynamicLabels(Collections.emptyList(), Collections.emptyList());
public static final DynamicLabels EMPTY = new DynamicLabels(null, Collections.emptyList(), Collections.emptyList());

private static final Node rootNode = Cypher.anyNode(Constants.NAME_OF_ROOT_NODE);
private final Node rootNode;

private final List<String> oldLabels;
private final List<String> newLabels;

DynamicLabels(Collection<String> oldLabels, Collection<String> newLabels) {
DynamicLabels(@Nullable NodeDescription<?> nodeDescription, Collection<String> oldLabels, Collection<String> newLabels) {
this.oldLabels = new ArrayList<>(oldLabels);
this.newLabels = new ArrayList<>(newLabels);
this.rootNode = Cypher.anyNode(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ private <T> DynamicLabels determineDynamicLabels(T entityToBeSaved, Neo4jPersist
}

Optional<Map<String, Object>> optionalResult = runnableQuery.fetch().one();
return new DynamicLabels(optionalResult.map(r -> (Collection<String>) r.get(Constants.NAME_OF_LABELS))
return new DynamicLabels(entityMetaData, optionalResult.map(r -> (Collection<String>) r.get(Constants.NAME_OF_LABELS))
.orElseGet(Collections::emptyList), (Collection<String>) propertyAccessor.getProperty(p));
}).orElse(DynamicLabels.EMPTY);
}
Expand Down Expand Up @@ -545,12 +545,12 @@ public <T> void deleteByIdWithVersion(Object id, Class<T> domainType, Neo4jPersi

String nameOfParameter = "id";
Condition condition = entityMetaData.getIdExpression().isEqualTo(parameter(nameOfParameter))
.and(Cypher.property(Constants.NAME_OF_ROOT_NODE, versionProperty.getPropertyName())
.and(Cypher.property(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData), versionProperty.getPropertyName())
.isEqualTo(parameter(Constants.NAME_OF_VERSION_PARAM))
.or(Cypher.property(Constants.NAME_OF_ROOT_NODE, versionProperty.getPropertyName()).isNull()));
.or(Cypher.property(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData), versionProperty.getPropertyName()).isNull()));

Statement statement = cypherGenerator.prepareMatchOf(entityMetaData, condition)
.returning(Constants.NAME_OF_ROOT_NODE).build();
.returning(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData)).build();

Map<String, Object> parameters = new HashMap<>();
parameters.put(nameOfParameter, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
Expand Down Expand Up @@ -971,7 +971,7 @@ private Optional<Neo4jClient.RecordFetchSpec<T>> createFetchSpec() {
if (nodesAndRelationshipsById.hasRootNodeIds()) {
return Optional.empty();
}
cypherQuery = renderer.render(nodesAndRelationshipsById.toStatement());
cypherQuery = renderer.render(nodesAndRelationshipsById.toStatement(entityMetaData));
finalParameters = nodesAndRelationshipsById.getParameters();
} else {
Statement statement = queryFragments.toStatement();
Expand Down Expand Up @@ -1036,7 +1036,7 @@ private void iterateNextLevel(Collection<Long> nodeIds, Neo4jPersistentEntity<?>
Collection<RelationshipDescription> relationships = target.getRelationshipsInHierarchy(preparedQuery.getQueryFragmentsAndParameters().getQueryFragments()::includeField);
for (RelationshipDescription relationshipDescription : relationships) {

Node node = anyNode(Constants.NAME_OF_ROOT_NODE);
Node node = anyNode(Constants.NAME_OF_TYPED_ROOT_NODE.apply(target));

Statement statement = cypherGenerator
.prepareMatchOf(target, relationshipDescription, null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ private <T> Mono<Tuple2<T, DynamicLabels>> determineDynamicLabels(T entityToBeSa
return runnableQuery.fetch().one().map(m -> (Collection<String>) m.get(Constants.NAME_OF_LABELS))
.switchIfEmpty(Mono.just(Collections.emptyList()))
.zipWith(Mono.just((Collection<String>) propertyAccessor.getProperty(p)))
.map(t -> Tuples.of(entityToBeSaved, new DynamicLabels(t.getT1(), t.getT2())));
.map(t -> Tuples.of(entityToBeSaved, new DynamicLabels(entityMetaData, t.getT1(), t.getT2())));
}).orElse(Mono.just(Tuples.of(entityToBeSaved, DynamicLabels.EMPTY)));
}

Expand Down Expand Up @@ -548,12 +548,12 @@ public <T> Mono<Void> deleteByIdWithVersion(Object id, Class<T> domainType, Neo4
String nameOfParameter = "id";
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getPersistentEntity(domainType);
Condition condition = entityMetaData.getIdExpression().isEqualTo(parameter(nameOfParameter))
.and(Cypher.property(Constants.NAME_OF_ROOT_NODE, versionProperty.getPropertyName())
.and(Cypher.property(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData), versionProperty.getPropertyName())
.isEqualTo(parameter(Constants.NAME_OF_VERSION_PARAM))
.or(Cypher.property(Constants.NAME_OF_ROOT_NODE, versionProperty.getPropertyName()).isNull()));
.or(Cypher.property(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData), versionProperty.getPropertyName()).isNull()));

Statement statement = cypherGenerator.prepareMatchOf(entityMetaData, condition)
.returning(Constants.NAME_OF_ROOT_NODE).build();
.returning(Constants.NAME_OF_TYPED_ROOT_NODE.apply(entityMetaData)).build();

Map<String, Object> parameters = new HashMap<>();
parameters.put(nameOfParameter, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
Expand Down Expand Up @@ -612,7 +612,7 @@ private <T> Mono<ExecutableQuery<T>> createExecutableQuery(Class<T> domainType,
if (containsPossibleCircles && !queryFragments.isScalarValueReturn()) {
return createNodesAndRelationshipsByIdStatementProvider(entityMetaData, queryFragments, queryFragmentsAndParameters.getParameters())
.flatMap(finalQueryAndParameters ->
createExecutableQuery(domainType, resultType, renderer.render(finalQueryAndParameters.toStatement()),
createExecutableQuery(domainType, resultType, renderer.render(finalQueryAndParameters.toStatement(entityMetaData)),
finalQueryAndParameters.getParameters()));
}

Expand Down Expand Up @@ -665,7 +665,7 @@ private Flux<Tuple2<Collection<Long>, Collection<Long>>> iterateNextLevel(Collec

return Flux.fromIterable(target.getRelationshipsInHierarchy(queryFragments::includeField))
.flatMap(relDe -> {
Node node = anyNode(Constants.NAME_OF_ROOT_NODE);
Node node = anyNode(Constants.NAME_OF_TYPED_ROOT_NODE.apply(target));

Statement statement = cypherGenerator
.prepareMatchOf(target, relDe, null,
Expand Down Expand Up @@ -995,7 +995,7 @@ public <T> Mono<ExecutableQuery<T>> toExecutableQuery(PreparedQuery<T> preparedQ
return createNodesAndRelationshipsByIdStatementProvider(entityMetaData, queryFragments, finalParameters)
.map(nodesAndRelationshipsById -> {
ReactiveNeo4jClient.MappingSpec<T> mappingSpec = this.neo4jClient.query(renderer.render(
nodesAndRelationshipsById.toStatement()))
nodesAndRelationshipsById.toStatement(entityMetaData)))
.bindAll(nodesAndRelationshipsById.getParameters()).fetchAs(resultType);

ReactiveNeo4jClient.RecordFetchSpec<T> fetchSpec = preparedQuery.getOptionalMappingFunction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ boolean hasRootNodeIds() {
return parameters.get(ROOT_NODE_IDS).isEmpty();
}

Statement toStatement() {
Statement toStatement(NodeDescription<?> nodeDescription) {

String rootNodeIds = "rootNodeIds";
String relationshipIds = "relationshipIds";
Expand All @@ -195,12 +195,12 @@ Statement toStatement() {
.optionalMatch(relatedNodes)
.where(Functions.id(relatedNodes).in(Cypher.parameter(relatedNodeIds)))
.with(
rootNodes.as(Constants.NAME_OF_ROOT_NODE.getValue()),
rootNodes.as(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription).getValue()),
Functions.collectDistinct(relationships).as(Constants.NAME_OF_SYNTHESIZED_RELATIONS),
Functions.collectDistinct(relatedNodes).as(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES))
.orderBy(queryFragments.getOrderBy())
.returning(
Constants.NAME_OF_ROOT_NODE.as(Constants.NAME_OF_SYNTHESIZED_ROOT_NODE),
Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription).as(Constants.NAME_OF_SYNTHESIZED_ROOT_NODE),
Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATIONS),
Cypher.name(Constants.NAME_OF_SYNTHESIZED_RELATED_NODES)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import org.apiguardian.api.API.Status;
import org.neo4j.cypherdsl.core.Cypher;
import org.neo4j.cypherdsl.core.SymbolicName;
import org.springframework.util.StringUtils;

import java.util.function.Function;

/**
* A pool of constants used in our Cypher generation. These constants may change without further notice and are meant
Expand All @@ -31,7 +34,12 @@
@API(status = Status.EXPERIMENTAL, since = "6.0")
public final class Constants {

public static final SymbolicName NAME_OF_ROOT_NODE = Cypher.name("n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do a NAME_OF_TYPED_ROOT_NODE.apply(null) and assigned that, so that it is somewhat compatible in case anyone used those constants.

public static final Function<NodeDescription<?>, SymbolicName> NAME_OF_TYPED_ROOT_NODE =
(nodeDescription) -> nodeDescription != null
? Cypher.name(StringUtils.uncapitalize(nodeDescription.getUnderlyingClass().getSimpleName()))
: Cypher.name("n");

public static final SymbolicName NAME_OF_ROOT_NODE = NAME_OF_TYPED_ROOT_NODE.apply(null);

public static final String NAME_OF_INTERNAL_ID = "__internalNeo4jId__";
/**
Expand All @@ -54,7 +62,6 @@ public final class Constants {
public static final String NAME_OF_ENTITY_LIST_PARAM = "__entities__";
public static final String NAME_OF_KNOWN_RELATIONSHIP_PARAM = "__knownRelationShipId__";
public static final String NAME_OF_KNOWN_RELATIONSHIPS_PARAM = "__knownRelationShipIds__";
public static final String NAME_OF_PATHS = "__paths__";
public static final String NAME_OF_ALL_PROPERTIES = "__allProperties__";

public static final String NAME_OF_SYNTHESIZED_ROOT_NODE = "__sn__";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public StatementBuilder.OrderableOngoingReadingAndWith prepareMatchOf(NodeDescri
Node rootNode = createRootNode(nodeDescription);

List<Expression> expressions = new ArrayList<>();
expressions.add(Constants.NAME_OF_ROOT_NODE);
expressions.add(rootNode.getRequiredSymbolicName());
expressions.add(Functions.id(rootNode).as(Constants.NAME_OF_INTERNAL_ID));

return match(rootNode).where(conditionOrNoCondition(condition)).with(expressions.toArray(new Expression[] {}));
Expand All @@ -121,7 +121,7 @@ public StatementBuilder.OngoingReading prepareMatchOf(NodeDescription<?> nodeDes
String primaryLabel = nodeDescription.getPrimaryLabel();
List<String> additionalLabels = nodeDescription.getAdditionalLabels();

Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_ROOT_NODE);
Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));

StatementBuilder.OngoingReadingWithoutWhere match = null;
if (initialMatchOn == null || initialMatchOn.isEmpty()) {
Expand Down Expand Up @@ -150,7 +150,7 @@ public StatementBuilder.OngoingReading prepareMatchOf(NodeDescription<?> nodeDes
String primaryLabel = nodeDescription.getPrimaryLabel();
List<String> additionalLabels = nodeDescription.getAdditionalLabels();

Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_ROOT_NODE);
Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));

StatementBuilder.OngoingReadingWithoutWhere match = null;
if (initialMatchOn == null || initialMatchOn.isEmpty()) {
Expand Down Expand Up @@ -210,7 +210,7 @@ public Node createRootNode(NodeDescription<?> nodeDescription) {
String primaryLabel = nodeDescription.getPrimaryLabel();
List<String> additionalLabels = nodeDescription.getAdditionalLabels();

return node(primaryLabel, additionalLabels).named(Constants.NAME_OF_ROOT_NODE);
return node(primaryLabel, additionalLabels).named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));
}

/**
Expand All @@ -223,7 +223,7 @@ public Node createRootNode(NodeDescription<?> nodeDescription) {
*/
public Statement createStatementReturningDynamicLabels(NodeDescription<?> nodeDescription) {

final Node rootNode = Cypher.anyNode(Constants.NAME_OF_ROOT_NODE);
final Node rootNode = Cypher.anyNode(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));

Condition versionCondition;
if (((Neo4jPersistentEntity) nodeDescription).hasVersionProperty()) {
Expand Down Expand Up @@ -254,7 +254,7 @@ public Statement prepareDeleteOf(NodeDescription<?> nodeDescription, @Nullable C
public Statement prepareDeleteOf(NodeDescription<?> nodeDescription, @Nullable Condition condition, boolean count) {

Node rootNode = node(nodeDescription.getPrimaryLabel(), nodeDescription.getAdditionalLabels())
.named(Constants.NAME_OF_ROOT_NODE);
.named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));
OngoingUpdate ongoingUpdate = match(rootNode).where(conditionOrNoCondition(condition)).detachDelete(rootNode);
if (count) {
return ongoingUpdate.returning(Functions.count(rootNode)).build();
Expand All @@ -268,7 +268,7 @@ public Statement prepareSaveOf(NodeDescription<?> nodeDescription,
String primaryLabel = nodeDescription.getPrimaryLabel();
List<String> additionalLabels = nodeDescription.getAdditionalLabels();

Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_ROOT_NODE);
Node rootNode = node(primaryLabel, additionalLabels).named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));
IdDescription idDescription = nodeDescription.getIdDescription();
Parameter idParameter = parameter(Constants.NAME_OF_ID);

Expand Down Expand Up @@ -356,7 +356,7 @@ public Statement prepareSaveOfMultipleInstancesOf(NodeDescription<?> nodeDescrip
"Only entities that use external IDs can be saved in a batch.");

Node rootNode = node(nodeDescription.getPrimaryLabel(), nodeDescription.getAdditionalLabels())
.named(Constants.NAME_OF_ROOT_NODE);
.named(Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription));
IdDescription idDescription = nodeDescription.getIdDescription();

String nameOfIdProperty = idDescription.getOptionalGraphPropertyName()
Expand Down Expand Up @@ -519,7 +519,13 @@ public Collection<Expression> createReturnStatementForMatch(Neo4jPersistentEntit
if (nodeDescription.containsPossibleCircles(includeField)) {
return createGenericReturnStatement();
} else {
return Collections.singleton(projectPropertiesAndRelationships(PropertyFilter.RelaxedPropertyPath.withRootType(nodeDescription.getUnderlyingClass()), nodeDescription, Constants.NAME_OF_ROOT_NODE, includeField, null, processedRelationships));
return Collections.singleton(projectPropertiesAndRelationships(
PropertyFilter.RelaxedPropertyPath.withRootType(nodeDescription.getUnderlyingClass()),
nodeDescription,
Constants.NAME_OF_TYPED_ROOT_NODE.apply(nodeDescription),
includeField,
null,
processedRelationships));
}
}

Expand Down Expand Up @@ -612,7 +618,7 @@ private List<Object> generateListsFor(PropertyFilter.RelaxedPropertyPath parentP

// if we already processed the other way before, do not try to jump in the infinite loop
// unless it is a root node relationship
if (!nodeName.equals(Constants.NAME_OF_ROOT_NODE) && relationshipDescription.hasRelationshipObverse()
if (relationshipDescription.hasRelationshipObverse()
&& processedRelationships.contains(relationshipDescription.getRelationshipObverse())) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ private IdDescription computeIdDescription() {

// Assigned ids
if (generatedValueAnnotation == null) {
return IdDescription.forAssignedIds(propertyName);
return IdDescription.forAssignedIds(Constants.NAME_OF_TYPED_ROOT_NODE.apply(this), propertyName);
}

Class<? extends IdGenerator<?>> idGeneratorClass = generatedValueAnnotation.generatorClass();
Expand All @@ -443,11 +443,11 @@ private IdDescription computeIdDescription() {
"Internally generated ids can only be assigned to one of " + VALID_GENERATED_ID_TYPES);
}

return IdDescription.forInternallyGeneratedIds();
return IdDescription.forInternallyGeneratedIds(Constants.NAME_OF_TYPED_ROOT_NODE.apply(this));
}

// Externally generated ids.
return IdDescription.forExternallyGeneratedIds(idGeneratorClass, idGeneratorRef, propertyName);
return IdDescription.forExternallyGeneratedIds(Constants.NAME_OF_TYPED_ROOT_NODE.apply(this), idGeneratorClass, idGeneratorRef, propertyName);
}

@Override
Expand Down
Loading