fix: Use contextual Cypher-DSL configuration for rendering statements in various Cypher-DSL based query extension.

Fixes #2927.

# Conflicts:
#	src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java
This commit is contained in:
Michael Simons
2024-07-19 12:40:51 +02:00
parent eca86bc40f
commit 31bb6d7ead
17 changed files with 176 additions and 28 deletions

View File

@@ -136,10 +136,7 @@ public interface FluentFindOperation {
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if statement is {@literal null}.
*/
default TerminatingFind<T> matching(Statement statement, @Nullable Map<String, Object> parameter) {
return matching(statement.getCypher(), TemplateSupport.mergeParameters(statement, parameter));
}
TerminatingFind<T> matching(Statement statement, @Nullable Map<String, Object> parameter);
/**
* Set the filter {@link Statement statement} to be used.

View File

@@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.neo4j.cypherdsl.core.Statement;
import org.springframework.data.neo4j.repository.query.QueryFragmentsAndParameters;
import org.springframework.util.Assert;
@@ -87,7 +88,6 @@ final class FluentOperationSupport implements FluentFindOperation, FluentSaveOpe
public TerminatingFind<T> matching(String query, Map<String, Object> parameters) {
Assert.notNull(query, "Query must not be null");
return new ExecutableFindSupport<>(template, domainType, returnType, query, parameters);
}
@@ -100,6 +100,12 @@ final class FluentOperationSupport implements FluentFindOperation, FluentSaveOpe
return new ExecutableFindSupport<>(template, domainType, returnType, queryFragmentsAndParameters);
}
@Override
public TerminatingFind<T> matching(Statement statement, Map<String, Object> parameter) {
return matching(template.render(statement), TemplateSupport.mergeParameters(statement, parameter));
}
@Override
public T oneValue() {

View File

@@ -1099,6 +1099,10 @@ public final class Neo4jTemplate implements
return results;
}
String render(Statement statement) {
return renderer.render(statement);
}
final class DefaultExecutableQuery<T> implements ExecutableQuery<T> {
private final PreparedQuery<T> preparedQuery;

View File

@@ -126,9 +126,7 @@ public interface ReactiveFluentFindOperation {
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if statement is {@literal null}.
*/
default TerminatingFind<T> matching(Statement statement, @Nullable Map<String, Object> parameter) {
return matching(statement.getCypher(), TemplateSupport.mergeParameters(statement, parameter));
}
TerminatingFind<T> matching(Statement statement, @Nullable Map<String, Object> parameter);
/**
* Set the filter {@link Statement statement} to be used.

View File

@@ -21,6 +21,7 @@ import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.Map;
import org.neo4j.cypherdsl.core.Statement;
import org.springframework.data.neo4j.repository.query.QueryFragmentsAndParameters;
import org.springframework.util.Assert;
@@ -101,6 +102,12 @@ final class ReactiveFluentOperationSupport implements ReactiveFluentFindOperatio
return new ExecutableFindSupport<>(template, domainType, returnType, queryFragmentsAndParameters);
}
@Override
public TerminatingFind<T> matching(Statement statement, Map<String, Object> parameter) {
return matching(template.render(statement), TemplateSupport.mergeParameters(statement, parameter));
}
@Override
public Mono<T> one() {
return doFind(TemplateSupport.FetchType.ONE).single();

View File

@@ -1172,7 +1172,11 @@ public final class ReactiveNeo4jTemplate implements
return new ReactiveFluentOperationSupport(this).save(domainType);
}
static final class DefaultReactiveExecutableQuery<T> implements ExecutableQuery<T> {
String render(Statement statement) {
return this.renderer.render(statement);
}
final class DefaultReactiveExecutableQuery<T> implements ExecutableQuery<T> {
private final PreparedQuery<T> preparedQuery;
private final ReactiveNeo4jClient.RecordFetchSpec<T> fetchSpec;

View File

@@ -19,6 +19,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
@@ -44,15 +45,20 @@ import org.springframework.util.Assert;
final class CypherdslBasedQuery extends AbstractNeo4jQuery {
static CypherdslBasedQuery create(Neo4jOperations neo4jOperations, Neo4jMappingContext mappingContext,
Neo4jQueryMethod queryMethod, ProjectionFactory projectionFactory) {
Neo4jQueryMethod queryMethod, ProjectionFactory projectionFactory,
Function<Statement, String> renderer) {
return new CypherdslBasedQuery(neo4jOperations, mappingContext, queryMethod, Neo4jQueryType.DEFAULT, projectionFactory);
return new CypherdslBasedQuery(neo4jOperations, mappingContext, queryMethod, Neo4jQueryType.DEFAULT, projectionFactory, renderer);
}
private final Function<Statement, String> renderer;
private CypherdslBasedQuery(Neo4jOperations neo4jOperations,
Neo4jMappingContext mappingContext,
Neo4jQueryMethod queryMethod, Neo4jQueryType queryType, ProjectionFactory projectionFactory) {
Neo4jQueryMethod queryMethod, Neo4jQueryType queryType, ProjectionFactory projectionFactory,
Function<Statement, String> renderer) {
super(neo4jOperations, mappingContext, queryMethod, queryType, projectionFactory);
this.renderer = renderer;
}
@Override
@@ -86,7 +92,7 @@ final class CypherdslBasedQuery extends AbstractNeo4jQuery {
Map<String, Object> boundParameters = statement.getCatalog().getParameters();
return PreparedQuery.queryFor(returnedType)
.withCypherQuery(statement.getCypher())
.withCypherQuery(renderer.apply(statement))
.withParameters(boundParameters)
.usingMappingFunction(mappingFunction)
.build();
@@ -98,7 +104,7 @@ final class CypherdslBasedQuery extends AbstractNeo4jQuery {
// We verified this above
Statement countStatement = (Statement) parameterAccessor.getValues()[1];
return Optional.of(PreparedQuery.queryFor(Long.class)
.withCypherQuery(countStatement.getCypher())
.withCypherQuery(renderer.apply(countStatement))
.withParameters(countStatement.getCatalog().getParameters()).build());
}
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.neo4j.repository.query;
import java.lang.reflect.Method;
import org.apiguardian.api.API;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.neo4j.cypherdsl.core.renderer.Renderer;
import org.springframework.data.neo4j.core.Neo4jOperations;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.data.projection.ProjectionFactory;
@@ -40,12 +42,14 @@ public final class Neo4jQueryLookupStrategy implements QueryLookupStrategy {
private final Neo4jMappingContext mappingContext;
private final Neo4jOperations neo4jOperations;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final Configuration configuration;
public Neo4jQueryLookupStrategy(Neo4jOperations neo4jOperations, Neo4jMappingContext mappingContext,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
QueryMethodEvaluationContextProvider evaluationContextProvider, Configuration configuration) {
this.neo4jOperations = neo4jOperations;
this.mappingContext = mappingContext;
this.evaluationContextProvider = evaluationContextProvider;
this.configuration = configuration;
}
/* (non-Javadoc)
@@ -65,7 +69,7 @@ public final class Neo4jQueryLookupStrategy implements QueryLookupStrategy {
return StringBasedNeo4jQuery.create(neo4jOperations, mappingContext, evaluationContextProvider, queryMethod,
factory);
} else if (queryMethod.isCypherBasedProjection()) {
return CypherdslBasedQuery.create(neo4jOperations, mappingContext, queryMethod, factory);
return CypherdslBasedQuery.create(neo4jOperations, mappingContext, queryMethod, factory, Renderer.getRenderer(configuration)::render);
} else {
return PartTreeNeo4jQuery.create(neo4jOperations, mappingContext, queryMethod, factory);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.repository.query;
import java.util.Collection;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
@@ -42,15 +43,19 @@ import org.springframework.util.Assert;
final class ReactiveCypherdslBasedQuery extends AbstractReactiveNeo4jQuery {
static ReactiveCypherdslBasedQuery create(ReactiveNeo4jOperations neo4jOperations, Neo4jMappingContext mappingContext,
Neo4jQueryMethod queryMethod, ProjectionFactory projectionFactory) {
Neo4jQueryMethod queryMethod, ProjectionFactory projectionFactory, Function<Statement, String> renderer) {
return new ReactiveCypherdslBasedQuery(neo4jOperations, mappingContext, queryMethod, Neo4jQueryType.DEFAULT, projectionFactory);
return new ReactiveCypherdslBasedQuery(neo4jOperations, mappingContext, queryMethod, Neo4jQueryType.DEFAULT, projectionFactory, renderer);
}
private final Function<Statement, String> renderer;
private ReactiveCypherdslBasedQuery(ReactiveNeo4jOperations neo4jOperations,
Neo4jMappingContext mappingContext,
Neo4jQueryMethod queryMethod, Neo4jQueryType queryType, ProjectionFactory projectionFactory) {
Neo4jQueryMethod queryMethod, Neo4jQueryType queryType, ProjectionFactory projectionFactory,
Function<Statement, String> renderer) {
super(neo4jOperations, mappingContext, queryMethod, queryType, projectionFactory);
this.renderer = renderer;
}
@Override
@@ -68,7 +73,7 @@ final class ReactiveCypherdslBasedQuery extends AbstractReactiveNeo4jQuery {
Map<String, Object> boundParameters = statement.getCatalog().getParameters();
return PreparedQuery.queryFor(returnedType)
.withCypherQuery(statement.getCypher())
.withCypherQuery(renderer.apply(statement))
.withParameters(boundParameters)
.usingMappingFunction(mappingFunction)
.build();

View File

@@ -18,6 +18,8 @@ package org.springframework.data.neo4j.repository.query;
import java.lang.reflect.Method;
import org.apiguardian.api.API;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.neo4j.cypherdsl.core.renderer.Renderer;
import org.springframework.data.neo4j.core.ReactiveNeo4jOperations;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.data.projection.ProjectionFactory;
@@ -40,12 +42,14 @@ public final class ReactiveNeo4jQueryLookupStrategy implements QueryLookupStrate
private final ReactiveNeo4jOperations neo4jOperations;
private final Neo4jMappingContext mappingContext;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final Configuration configuration;
public ReactiveNeo4jQueryLookupStrategy(ReactiveNeo4jOperations neo4jOperations, Neo4jMappingContext mappingContext,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
QueryMethodEvaluationContextProvider evaluationContextProvider, Configuration configuration) {
this.neo4jOperations = neo4jOperations;
this.mappingContext = mappingContext;
this.evaluationContextProvider = evaluationContextProvider;
this.configuration = configuration;
}
/* (non-Javadoc)
@@ -65,7 +69,7 @@ public final class ReactiveNeo4jQueryLookupStrategy implements QueryLookupStrate
return ReactiveStringBasedNeo4jQuery.create(neo4jOperations, mappingContext, evaluationContextProvider,
queryMethod, projectionFactory);
} else if (queryMethod.isCypherBasedProjection()) {
return ReactiveCypherdslBasedQuery.create(neo4jOperations, mappingContext, queryMethod, projectionFactory);
return ReactiveCypherdslBasedQuery.create(neo4jOperations, mappingContext, queryMethod, projectionFactory, Renderer.getRenderer(configuration)::render);
} else {
return ReactivePartTreeNeo4jQuery.create(neo4jOperations, mappingContext, queryMethod, projectionFactory);
}

View File

@@ -17,6 +17,9 @@ package org.springframework.data.neo4j.repository.support;
import java.util.Optional;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.neo4j.core.Neo4jOperations;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity;
@@ -51,6 +54,8 @@ final class Neo4jRepositoryFactory extends RepositoryFactorySupport {
private final Neo4jMappingContext mappingContext;
private Configuration cypherDSLConfiguration = Configuration.defaultConfig();
Neo4jRepositoryFactory(Neo4jOperations neo4jOperations, Neo4jMappingContext mappingContext) {
this.neo4jOperations = neo4jOperations;
@@ -122,6 +127,14 @@ final class Neo4jRepositoryFactory extends RepositoryFactorySupport {
return SimpleNeo4jRepository.class;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
super.setBeanFactory(beanFactory);
this.cypherDSLConfiguration = beanFactory
.getBeanProvider(Configuration.class)
.getIfAvailable(Configuration::defaultConfig);
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
@@ -130,7 +143,7 @@ final class Neo4jRepositoryFactory extends RepositoryFactorySupport {
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(new Neo4jQueryLookupStrategy(neo4jOperations, mappingContext, evaluationContextProvider));
return Optional.of(new Neo4jQueryLookupStrategy(neo4jOperations, mappingContext, evaluationContextProvider, cypherDSLConfiguration));
}
@Override

View File

@@ -17,6 +17,7 @@ package org.springframework.data.neo4j.repository.support;
import java.util.Optional;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
@@ -55,6 +56,8 @@ final class ReactiveNeo4jRepositoryFactory extends ReactiveRepositoryFactorySupp
private final Neo4jMappingContext mappingContext;
private Configuration cypherDSLConfiguration = Configuration.defaultConfig();
ReactiveNeo4jRepositoryFactory(ReactiveNeo4jOperations neo4jOperations, Neo4jMappingContext mappingContext) {
this.neo4jOperations = neo4jOperations;
@@ -133,7 +136,7 @@ final class ReactiveNeo4jRepositoryFactory extends ReactiveRepositoryFactorySupp
QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional
.of(new ReactiveNeo4jQueryLookupStrategy(neo4jOperations, mappingContext, evaluationContextProvider));
.of(new ReactiveNeo4jQueryLookupStrategy(neo4jOperations, mappingContext, evaluationContextProvider, cypherDSLConfiguration));
}
@Override
@@ -148,6 +151,10 @@ final class ReactiveNeo4jRepositoryFactory extends ReactiveRepositoryFactorySupp
factory.addAdvice(advice);
});
}
this.cypherDSLConfiguration = beanFactory
.getBeanProvider(Configuration.class)
.getIfAvailable(Configuration::defaultConfig);
}
@Override

View File

@@ -41,6 +41,7 @@ abstract class AbstractElementIdTestBase {
void setupData(LogbackCapture logbackCapture, @Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
logbackCapture.addLogger("org.springframework.data.neo4j.cypher.deprecation", Level.WARN);
logbackCapture.addLogger("org.springframework.data.neo4j.cypher", Level.DEBUG);
try (Session session = driver.session()) {
session.run("MATCH (n) DETACH DELETE n").consume();
bookmarkCapture.seedWith(session.lastBookmarks());
@@ -78,6 +79,7 @@ abstract class AbstractElementIdTestBase {
List<String> formattedMessages = logbackCapture.getFormattedMessages();
assertThat(formattedMessages)
.noneMatch(s -> s.contains("Neo.ClientNotification.Statement.FeatureDeprecationWarning") ||
s.contains("The query used a deprecated function. ('id' is no longer supported)"));
s.contains("The query used a deprecated function. ('id' is no longer supported)") ||
s.matches("(?s).*toString\\(id\\(.*")); // No deprecations are logged when deprecated function call is nested. Anzeige ist raus.
}
}

View File

@@ -20,13 +20,18 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.neo4j.cypherdsl.core.Cypher;
import org.neo4j.cypherdsl.core.Functions;
import org.neo4j.cypherdsl.core.Statement;
import org.neo4j.driver.Driver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.repository.Neo4jRepository;
@@ -351,6 +356,28 @@ public class ImperativeElementIdIT extends AbstractElementIdTestBase {
}
}
@Test
@Tag("GH-2927")
void fluentOpsMustUseCypherDSLConfig(
LogbackCapture logbackCapture,
@Autowired Driver driver,
@Autowired BookmarkCapture bookmarkCapture,
@Autowired Neo4jTemplate neo4jTemplate) {
try (var session = driver.session(bookmarkCapture.createSessionConfig())) {
session.run("MERGE (n:" + Thing.THING_LABEL + "{foo: 'bar'})").consume();
}
var thingNode = Cypher.node(Thing.THING_LABEL);
var cypherStatement = Statement.builder()
.match(thingNode)
.where(Functions.elementId(thingNode).eq(Cypher.literalOf("test")))
.returning(thingNode)
.build();
neo4jTemplate.find(Thing.class).matching(cypherStatement).one();
assertThatLogMessageDoNotIndicateIDUsage(logbackCapture);
}
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(considerNestedRepositories = true)

View File

@@ -19,13 +19,18 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.neo4j.cypherdsl.core.Cypher;
import org.neo4j.cypherdsl.core.Functions;
import org.neo4j.cypherdsl.core.Statement;
import org.neo4j.driver.Driver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider;
import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate;
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository;
@@ -39,6 +44,7 @@ import org.springframework.lang.NonNull;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.assertj.core.api.Assertions.assertThat;
@@ -363,6 +369,29 @@ public class ReactiveElementIdIT extends AbstractElementIdTestBase {
}
}
@Test
@Tag("GH-2927")
void fluentOpsMustUseCypherDSLConfig(
LogbackCapture logbackCapture,
@Autowired Driver driver,
@Autowired BookmarkCapture bookmarkCapture,
@Autowired ReactiveNeo4jTemplate neo4jTemplate) {
try (var session = driver.session(bookmarkCapture.createSessionConfig())) {
session.run("MERGE (n:" + Thing.THING_LABEL + "{foo: 'bar'})").consume();
}
var thingNode = Cypher.node(Thing.THING_LABEL);
var cypherStatement = Statement.builder()
.match(thingNode)
.where(Functions.elementId(thingNode).eq(Cypher.literalOf("test")))
.returning(thingNode)
.build();
neo4jTemplate.find(Thing.class).matching(cypherStatement).all().as(StepVerifier::create)
.verifyComplete();
assertThatLogMessageDoNotIndicateIDUsage(logbackCapture);
}
@Configuration
@EnableTransactionManagement
@EnableReactiveNeo4jRepositories(considerNestedRepositories = true)

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2011-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.integration.issues.pure_element_id;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
/**
* Just a random annotated entity.
*/
@Node(primaryLabel = Thing.THING_LABEL)
public class Thing {
public static final String THING_LABEL = "THING";
@Id
@GeneratedValue
String id;
String name;
}

View File

@@ -45,6 +45,7 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.neo4j.cypherdsl.core.renderer.Configuration;
import org.neo4j.driver.Values;
import org.neo4j.driver.types.Point;
import org.springframework.context.ConfigurableApplicationContext;
@@ -199,7 +200,7 @@ final class RepositoryQueryTest {
final Neo4jQueryLookupStrategy lookupStrategy = new Neo4jQueryLookupStrategy(neo4jOperations,
neo4jMappingContext,
QueryMethodEvaluationContextProvider.DEFAULT);
QueryMethodEvaluationContextProvider.DEFAULT, Configuration.defaultConfig());
RepositoryQuery query = lookupStrategy.resolveQuery(queryMethod("findById", Object.class),
TEST_REPOSITORY_METADATA, PROJECTION_FACTORY, namedQueries);
@@ -210,7 +211,7 @@ final class RepositoryQueryTest {
void shouldSelectStringBasedNeo4jQuery() {
final Neo4jQueryLookupStrategy lookupStrategy = new Neo4jQueryLookupStrategy(neo4jOperations,
neo4jMappingContext, QueryMethodEvaluationContextProvider.DEFAULT);
neo4jMappingContext, QueryMethodEvaluationContextProvider.DEFAULT, Configuration.defaultConfig());
RepositoryQuery query = lookupStrategy.resolveQuery(queryMethod("annotatedQueryWithValidTemplate"),
TEST_REPOSITORY_METADATA, PROJECTION_FACTORY, namedQueries);
@@ -225,7 +226,7 @@ final class RepositoryQueryTest {
when(namedQueries.getQuery(namedQueryName)).thenReturn("MATCH (n) RETURN n");
final Neo4jQueryLookupStrategy lookupStrategy = new Neo4jQueryLookupStrategy(neo4jOperations,
neo4jMappingContext, QueryMethodEvaluationContextProvider.DEFAULT);
neo4jMappingContext, QueryMethodEvaluationContextProvider.DEFAULT, Configuration.defaultConfig());
RepositoryQuery query = lookupStrategy
.resolveQuery(queryMethod("findAllByANamedQuery"), TEST_REPOSITORY_METADATA,