feature: Allow Cypher LIST<ANY> to be used directly as repository methods returning collections.
Signed-off-by: Michael Simons <michael@simons.ac>
This commit is contained in:
@@ -24,6 +24,7 @@ import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.neo4j.driver.Bookmark;
|
||||
import org.neo4j.driver.Driver;
|
||||
@@ -472,7 +473,12 @@ final class DefaultNeo4jClient implements Neo4jClient, ApplicationContextAware {
|
||||
|
||||
try (QueryRunner statementRunner = getQueryRunner(this.databaseSelection, this.impersonatedUser)) {
|
||||
Result result = runnableStatement.runWith(statementRunner);
|
||||
Collection<T> values = result.stream().map(partialMappingFunction(TypeSystem.getDefault())).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
Collection<T> values = result.stream().flatMap(r -> {
|
||||
if (mappingFunction instanceof SingleValueMappingFunction && r.size() == 1 && r.get(0).hasType(TypeSystem.getDefault().LIST())) {
|
||||
return r.get(0).asList(v -> ((SingleValueMappingFunction<T>) mappingFunction).convertValue(v)).stream();
|
||||
}
|
||||
return Stream.of(partialMappingFunction(TypeSystem.getDefault()).apply(r));
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
ResultSummaries.process(result.consume());
|
||||
return values;
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
@@ -408,7 +408,13 @@ final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient, Applicati
|
||||
Flux<T> executeWith(Tuple2<String, Map<String, Object>> t, ReactiveQueryRunner runner) {
|
||||
|
||||
return Flux.usingWhen(Flux.from(runner.run(t.getT1(), t.getT2())),
|
||||
result -> Flux.from(result.records()).mapNotNull(r -> mappingFunction.apply(TypeSystem.getDefault(), r)),
|
||||
result -> Flux.from(result.records()).flatMap(r -> {
|
||||
if (mappingFunction instanceof SingleValueMappingFunction && r.size() == 1 && r.get(0).hasType(TypeSystem.getDefault().LIST())) {
|
||||
return Flux.fromStream(r.get(0).asList(v -> ((SingleValueMappingFunction<T>) mappingFunction).convertValue(v)).stream());
|
||||
}
|
||||
var item = mappingFunction.apply(TypeSystem.getDefault(), r);
|
||||
return item == null ? Flux.empty() : Flux.just(item);
|
||||
}),
|
||||
result -> Flux.from(result.consume()).doOnNext(ResultSummaries::process));
|
||||
}
|
||||
|
||||
|
||||
@@ -1268,7 +1268,7 @@ public final class Neo4jTemplate implements
|
||||
if (preparedQuery.resultsHaveBeenAggregated()) {
|
||||
return all.stream().flatMap(nested -> ((Collection<T>) nested).stream()).distinct().collect(Collectors.toList());
|
||||
}
|
||||
return all.stream().collect(Collectors.toList());
|
||||
return new ArrayList<>(all);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1328,8 +1328,8 @@ public final class Neo4jTemplate implements
|
||||
|
||||
Neo4jClient.MappingSpec<T> newMappingSpec = neo4jClient.query(cypherQuery)
|
||||
.bindAll(finalParameters).fetchAs(preparedQuery.getResultType());
|
||||
return Optional.of(preparedQuery.getOptionalMappingFunction()
|
||||
.map(newMappingSpec::mappedBy).orElse(newMappingSpec));
|
||||
return preparedQuery.getOptionalMappingFunction()
|
||||
.map(newMappingSpec::mappedBy).or(() -> Optional.of(newMappingSpec));
|
||||
}
|
||||
|
||||
private NodesAndRelationshipsByIdStatementProvider createNodesAndRelationshipsByIdStatementProvider(Neo4jPersistentEntity<?> entityMetaData,
|
||||
|
||||
@@ -54,7 +54,11 @@ final class SingleValueMappingFunction<T> implements BiFunction<TypeSystem, Reco
|
||||
throw new IllegalArgumentException("Records with more than one value cannot be converted without a mapper");
|
||||
}
|
||||
|
||||
Value source = record.get(0);
|
||||
return convertValue(record.get(0));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
T convertValue(@Nullable Value source) {
|
||||
if (targetClass == Void.class || targetClass == void.class) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -265,6 +265,18 @@ class RepositoryIT {
|
||||
RETURN *""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDomainType(@Autowired PersonRepository repository) {
|
||||
var strings = repository.noDomainType();
|
||||
assertThat(strings).containsExactly("a", "b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDomainTypeWithListInQueryShouldWork(@Autowired PersonRepository repository) {
|
||||
var strings = repository.noDomainTypeWithListInQuery();
|
||||
assertThat(strings).containsExactly("a", "b", "c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAll(@Autowired PersonRepository repository) {
|
||||
|
||||
|
||||
@@ -131,6 +131,12 @@ public interface PersonRepository extends Neo4jRepository<PersonWithAllConstruct
|
||||
countQuery = "MATCH (n:PersonWithAllConstructor) WHERE n.name = $aName OR n.name = $anotherName RETURN count(n)")
|
||||
Page<PersonWithAllConstructor> findPageByCustomQueryWithCount(@Param("aName") String aName, @Param("anotherName") String anotherName, Pageable pageable);
|
||||
|
||||
@Query("UNWIND ['a', 'b', 'c'] AS x RETURN x")
|
||||
List<String> noDomainType();
|
||||
|
||||
@Query("RETURN ['a', 'b', 'c']")
|
||||
List<String> noDomainTypeWithListInQuery();
|
||||
|
||||
Long countAllByNameOrName(String aName, String anotherName);
|
||||
|
||||
Optional<PersonWithAllConstructor> findOneByNameAndFirstNameAllIgnoreCase(String name, String firstName);
|
||||
|
||||
@@ -250,6 +250,18 @@ class ReactiveRepositoryIT {
|
||||
.expectNextMatches(personList::contains).verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDomainType(@Autowired ReactivePersonRepository repository) {
|
||||
var strings = repository.noDomainTypeAsFlux();
|
||||
StepVerifier.create(strings).expectNext("a", "b", "c").verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDomainTypeWithListInQueryShouldWork(@Autowired ReactivePersonRepository repository) {
|
||||
var strings = repository.noDomainTypeWithListInQuery();
|
||||
StepVerifier.create(strings).expectNext("a", "b", "c").verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findAllByIdsPublisher(@Autowired ReactivePersonRepository repository) {
|
||||
|
||||
|
||||
@@ -48,6 +48,12 @@ public interface ReactivePersonRepository extends ReactiveNeo4jRepository<Person
|
||||
@Query("MATCH (n:PersonWithAllConstructor{name:'Test'}) return n")
|
||||
Mono<PersonWithAllConstructor> getOnePersonViaQuery();
|
||||
|
||||
@Query("UNWIND ['a', 'b', 'c'] AS x RETURN x")
|
||||
Flux<String> noDomainTypeAsFlux();
|
||||
|
||||
@Query("RETURN ['a', 'b', 'c']")
|
||||
Flux<String> noDomainTypeWithListInQuery();
|
||||
|
||||
Mono<PersonWithAllConstructor> findOneByNameAndFirstName(String name, String firstName);
|
||||
|
||||
Mono<PersonWithAllConstructor> findOneByNameAndFirstNameAllIgnoreCase(String name, String firstName);
|
||||
|
||||
Reference in New Issue
Block a user