Skip to content

Commit cf09e57

Browse files
authored
Add Kotlin extensions for several interfaces and CoroutineElasticsearchRepository.
Original Pull Request #2573 Closes #2545
1 parent f973236 commit cf09e57

15 files changed

+505
-12
lines changed

pom.xml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@
202202
<scope>test</scope>
203203
</dependency>
204204

205+
<!-- Kotlin extension -->
206+
<dependency>
207+
<groupId>org.jetbrains.kotlin</groupId>
208+
<artifactId>kotlin-stdlib</artifactId>
209+
<optional>true</optional>
210+
</dependency>
211+
212+
<dependency>
213+
<groupId>org.jetbrains.kotlin</groupId>
214+
<artifactId>kotlin-reflect</artifactId>
215+
<optional>true</optional>
216+
</dependency>
217+
218+
<dependency>
219+
<groupId>org.jetbrains.kotlinx</groupId>
220+
<artifactId>kotlinx-coroutines-core</artifactId>
221+
<optional>true</optional>
222+
</dependency>
223+
224+
<dependency>
225+
<groupId>org.jetbrains.kotlinx</groupId>
226+
<artifactId>kotlinx-coroutines-reactor</artifactId>
227+
<optional>true</optional>
228+
</dependency>
229+
205230
<!-- Test -->
206231
<dependency>
207232
<groupId>org.springframework</groupId>
@@ -215,6 +240,13 @@
215240
</exclusions>
216241
</dependency>
217242

243+
<dependency>
244+
<groupId>org.jetbrains.kotlinx</groupId>
245+
<artifactId>kotlinx-coroutines-test</artifactId>
246+
<scope>test</scope>
247+
<optional>true</optional>
248+
</dependency>
249+
218250
<dependency>
219251
<groupId>org.slf4j</groupId>
220252
<artifactId>log4j-over-slf4j</artifactId>

src/main/java/org/springframework/data/elasticsearch/repository/ReactiveElasticsearchRepository.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,4 @@
2727
*/
2828
@NoRepositoryBean
2929
public interface ReactiveElasticsearchRepository<T, ID>
30-
extends ReactiveSortingRepository<T, ID>, ReactiveCrudRepository<T, ID> {
31-
32-
}
30+
extends ReactiveSortingRepository<T, ID>, ReactiveCrudRepository<T, ID> {}

src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractElasticsearchRepositoryQuery.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.repository.query.ParametersParameterAccessor;
3030
import org.springframework.data.repository.query.QueryMethod;
3131
import org.springframework.data.repository.query.RepositoryQuery;
32+
import org.springframework.data.repository.query.ResultProcessor;
3233
import org.springframework.data.util.StreamUtils;
3334
import org.springframework.lang.Nullable;
3435
import org.springframework.util.Assert;
@@ -75,7 +76,9 @@ public QueryMethod getQueryMethod() {
7576
public Object execute(Object[] parameters) {
7677

7778
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
78-
Class<?> clazz = getResultClass();
79+
ResultProcessor resultProcessor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor);
80+
Class<?> clazz = resultProcessor.getReturnedType().getDomainType();
81+
7982
Query query = createQuery(parameters);
8083

8184
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
@@ -132,8 +135,10 @@ public Object execute(Object[] parameters) {
132135

133136
public Query createQuery(Object[] parameters) {
134137

135-
Class<?> clazz = getResultClass();
136138
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
139+
ResultProcessor resultProcessor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor);
140+
Class<?> returnedType = resultProcessor.getReturnedType().getDomainType();
141+
137142
Query query = createQuery(parameterAccessor);
138143

139144
Assert.notNull(query, "unsupported query");
@@ -151,10 +156,6 @@ public Query createQuery(Object[] parameters) {
151156
return query;
152157
}
153158

154-
private Class<?> getResultClass() {
155-
return queryMethod.getResultProcessor().getReturnedType().getDomainType();
156-
}
157-
158159
private ParametersParameterAccessor getParameterAccessor(Object[] parameters) {
159160
return new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
160161
}

src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractReactiveElasticsearchRepositoryQuery.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.reactivestreams.Publisher;
2222
import org.springframework.core.convert.converter.Converter;
2323
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
24+
import org.springframework.data.elasticsearch.core.SearchHit;
2425
import org.springframework.data.elasticsearch.core.SearchHitSupport;
2526
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
2627
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
@@ -81,6 +82,13 @@ private Object executeDeferred(Object[] parameters) {
8182
private Object execute(ElasticsearchParameterAccessor parameterAccessor) {
8283

8384
ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor);
85+
var returnedType = processor.getReturnedType();
86+
Class<?> domainType = returnedType.getDomainType();
87+
Class<?> typeToRead = returnedType.getTypeToRead();
88+
89+
if (SearchHit.class.isAssignableFrom(typeToRead)) {
90+
typeToRead = queryMethod.unwrappedReturnType;
91+
}
8492

8593
Query query = createQuery(parameterAccessor);
8694

@@ -100,8 +108,7 @@ private Object execute(ElasticsearchParameterAccessor parameterAccessor) {
100108
ReactiveElasticsearchQueryExecution execution = getExecution(parameterAccessor,
101109
new ResultProcessingConverter(processor));
102110

103-
var returnedType = processor.getReturnedType();
104-
return execution.execute(query, returnedType.getDomainType(), returnedType.getTypeToRead(), index);
111+
return execution.execute(query, domainType, typeToRead, index);
105112
}
106113

107114
private ReactiveElasticsearchQueryExecution getExecution(ElasticsearchParameterAccessor accessor,

src/main/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchQueryMethod.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import org.springframework.data.repository.core.RepositoryMetadata;
4444
import org.springframework.data.repository.query.ParameterAccessor;
4545
import org.springframework.data.repository.query.QueryMethod;
46+
import org.springframework.data.repository.util.QueryExecutionConverters;
47+
import org.springframework.data.repository.util.ReactiveWrapperConverters;
4648
import org.springframework.data.util.Lazy;
4749
import org.springframework.data.util.TypeInformation;
4850
import org.springframework.lang.Nullable;
@@ -62,9 +64,16 @@
6264
*/
6365
public class ElasticsearchQueryMethod extends QueryMethod {
6466

67+
// the following 2 variables exits in the base class, but are private. We need them for
68+
// correct handling of return types (SearchHits), so we have our own values here.
69+
// Alas this means that we have to copy code that initializes these variables and in the
70+
// base class uses them in order to use our variables
71+
protected final Method method;
72+
protected final Class<?> unwrappedReturnType;
73+
private Boolean unwrappedReturnTypeFromSearchHit = null;
74+
6575
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
6676
@Nullable private ElasticsearchEntityMetadata<?> metadata;
67-
protected final Method method; // private in base class, but needed here and in derived classes as well
6877
@Nullable private final Query queryAnnotation;
6978
@Nullable private final Highlight highlightAnnotation;
7079
private final Lazy<HighlightQuery> highlightQueryLazy = Lazy.of(this::createAnnotatedHighlightQuery);
@@ -83,6 +92,7 @@ public ElasticsearchQueryMethod(Method method, RepositoryMetadata repositoryMeta
8392
this.queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
8493
this.highlightAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Highlight.class);
8594
this.sourceFilters = AnnotatedElementUtils.findMergedAnnotation(method, SourceFilters.class);
95+
this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(repositoryMetadata, method);
8696

8797
verifyCountQueryTypes();
8898
}
@@ -188,6 +198,11 @@ protected MappingContext<? extends ElasticsearchPersistentEntity<?>, Elasticsear
188198
* @since 4.0
189199
*/
190200
public boolean isSearchHitMethod() {
201+
202+
if (unwrappedReturnTypeFromSearchHit != null && unwrappedReturnTypeFromSearchHit) {
203+
return true;
204+
}
205+
191206
Class<?> methodReturnType = method.getReturnType();
192207

193208
if (SearchHits.class.isAssignableFrom(methodReturnType)) {
@@ -322,4 +337,32 @@ private String[] mapParameters(String[] source, ParameterAccessor parameterAcces
322337

323338
return fieldNames.toArray(new String[0]);
324339
}
340+
341+
// region Copied from QueryMethod base class
342+
/*
343+
* Copied from the QueryMethod class adding support for collections of SearchHit instances. No static method here.
344+
*/
345+
private Class<? extends Object> potentiallyUnwrapReturnTypeFor(RepositoryMetadata metadata, Method method) {
346+
TypeInformation<?> returnType = metadata.getReturnType(method);
347+
if (!QueryExecutionConverters.supports(returnType.getType())
348+
&& !ReactiveWrapperConverters.supports(returnType.getType())) {
349+
return returnType.getType();
350+
} else {
351+
TypeInformation<?> componentType = returnType.getComponentType();
352+
if (componentType == null) {
353+
throw new IllegalStateException(
354+
String.format("Couldn't find component type for return value of method %s", method));
355+
} else {
356+
357+
if (SearchHit.class.isAssignableFrom(componentType.getType())) {
358+
unwrappedReturnTypeFromSearchHit = true;
359+
return componentType.getComponentType().getType();
360+
} else {
361+
return componentType.getType();
362+
}
363+
}
364+
}
365+
}
366+
// endregion
367+
325368
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.springframework.data.elasticsearch.core
2+
3+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
4+
import org.springframework.data.elasticsearch.core.query.BulkOptions
5+
import org.springframework.data.elasticsearch.core.query.ByQueryResponse
6+
import org.springframework.data.elasticsearch.core.query.IndexQuery
7+
import org.springframework.data.elasticsearch.core.query.Query
8+
import org.springframework.data.elasticsearch.core.query.UpdateQuery
9+
10+
/**
11+
* Extension functions for [DocumentOperations] methods that take a Class parameter leveraging reified type parameters.
12+
* @author Peter-Josef Meisch
13+
* @since 5.2
14+
*/
15+
16+
inline fun <reified T : Any> DocumentOperations.get(id: String): T? =
17+
get(id, T::class.java)
18+
19+
inline fun <reified T : Any> DocumentOperations.get(id: String, index: IndexCoordinates): T? =
20+
get(id, T::class.java, index)
21+
22+
inline fun <reified T : Any> DocumentOperations.multiGet(query: Query): List<MultiGetItem<T>> =
23+
multiGet(query, T::class.java)
24+
25+
inline fun <reified T : Any> DocumentOperations.multiGet(query: Query, index: IndexCoordinates): List<MultiGetItem<T>> =
26+
multiGet(query, T::class.java, index)
27+
28+
inline fun <reified T : Any> DocumentOperations.exists(id: String): Boolean = exists(id, T::class.java)
29+
30+
inline fun <reified T : Any> DocumentOperations.bulkIndex(queries: List<IndexQuery>): List<IndexedObjectInformation> =
31+
bulkIndex(queries, T::class.java)
32+
33+
inline fun <reified T : Any> DocumentOperations.bulkIndex(
34+
queries: List<IndexQuery>,
35+
bulkOptions: BulkOptions
36+
): List<IndexedObjectInformation> =
37+
bulkIndex(queries, bulkOptions, T::class.java)
38+
39+
inline fun <reified T : Any> DocumentOperations.bulkUpdate(queries: List<UpdateQuery>) =
40+
bulkUpdate(queries, T::class.java)
41+
42+
inline fun <reified T : Any> DocumentOperations.delete(id: String): String =
43+
delete(id, T::class.java)
44+
45+
inline fun <reified T : Any> DocumentOperations.delete(query: Query): ByQueryResponse =
46+
delete(query, T::class.java)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.springframework.data.elasticsearch.core
2+
3+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
4+
5+
/**
6+
* Extension functions for [DocumentOperations] methods that take a Class parameter leveraging reified type parameters.
7+
* @author Peter-Josef Meisch
8+
* @since 5.2
9+
*/
10+
11+
inline fun <reified T : Any> ElasticsearchOperations.indexOps(): IndexOperations =
12+
indexOps(T::class.java)
13+
14+
inline fun <reified T : Any> ElasticsearchOperations.getIndexCoordinatesFor(): IndexCoordinates =
15+
getIndexCoordinatesFor(T::class.java)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.springframework.data.elasticsearch.core
2+
3+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
4+
import org.springframework.data.elasticsearch.core.query.Query
5+
import reactor.core.publisher.Flux
6+
import reactor.core.publisher.Mono
7+
8+
/**
9+
* Extension functions for [ReactiveDocumentOperations] methods that take a Class parameter leveraging reified type parameters.
10+
* @author Peter-Josef Meisch
11+
* @since 5.2
12+
*/
13+
14+
inline fun <reified T : Any> ReactiveDocumentOperations.multiGet(query: Query): Flux<MultiGetItem<T>> = multiGet(query, T::class.java)
15+
inline fun <reified T : Any> ReactiveDocumentOperations.multiGet(query: Query, index: IndexCoordinates): Flux<MultiGetItem<T>> = multiGet(query, T::class.java, index)
16+
17+
inline fun <reified T : Any> ReactiveDocumentOperations.get(id: String): Mono<T> = get(id, T::class.java)
18+
inline fun <reified T : Any> ReactiveDocumentOperations.get(id: String, index: IndexCoordinates): Mono<T> = get(id, T::class.java, index)
19+
inline fun <reified T : Any> ReactiveDocumentOperations.exists(id: String): Mono<Boolean> = exists(id, T::class.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.springframework.data.elasticsearch.core
2+
3+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
4+
5+
/**
6+
* Extension functions for [ReacctiveElasticsearchOperations] methods that take a Class parameter leveraging reified type parameters.
7+
* @author Peter-Josef Meisch
8+
* @since 5.2
9+
*/
10+
11+
inline fun <reified T : Any> ReactiveElasticsearchOperations.indexOps(): ReactiveIndexOperations =
12+
indexOps(T::class.java)
13+
14+
inline fun <reified T : Any> ReactiveElasticsearchOperations.getIndexCoordinatesFor(): IndexCoordinates =
15+
getIndexCoordinatesFor(T::class.java)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.springframework.data.elasticsearch.core
2+
3+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates
4+
import org.springframework.data.elasticsearch.core.query.Query
5+
import org.springframework.data.elasticsearch.core.suggest.response.Suggest
6+
import reactor.core.publisher.Flux
7+
import reactor.core.publisher.Mono
8+
9+
/**
10+
* Extension functions for [SearchOperations] methods that take a Class parameter leveraging reified type parameters.
11+
* @author Peter-Josef Meisch
12+
* @since 5.2
13+
*/
14+
15+
inline fun <reified T : Any> ReactiveSearchOperations.count(): Mono<Long> = count(T::class.java)
16+
inline fun <reified T : Any> ReactiveSearchOperations.count(query: Query): Mono<Long> = count(query, T::class.java)
17+
inline fun <reified T : Any> ReactiveSearchOperations.count(query: Query, index: IndexCoordinates): Mono<Long> = count(query, T::class.java, index)
18+
inline fun <reified T : Any> ReactiveSearchOperations.search(query: Query): Flux<SearchHit<T>> = search(query, T::class.java)
19+
inline fun <reified T : Any> ReactiveSearchOperations.search(query: Query, index: IndexCoordinates): Flux<SearchHit<T>> = search(query, T::class.java, index)
20+
inline fun <reified T : Any> ReactiveSearchOperations.searchForPage(query: Query): Mono<SearchPage<T>> = searchForPage(query, T::class.java)
21+
inline fun <reified T : Any> ReactiveSearchOperations.searchForPage(query: Query, index: IndexCoordinates): Mono<SearchPage<T>> = searchForPage(query, T::class.java, index)
22+
inline fun <reified T : Any> ReactiveSearchOperations.searchForHits(query: Query): Mono<ReactiveSearchHits<T>> = searchForHits(query, T::class.java)
23+
inline fun <reified T : Any> ReactiveSearchOperations.aggregate(query: Query): Flux<out AggregationContainer<*>> = aggregate(query, T::class.java)
24+
inline fun <reified T : Any> ReactiveSearchOperations.aggregate(query: Query, index: IndexCoordinates): Flux<out AggregationContainer<*>> = aggregate(query, T::class.java, index)
25+
inline fun <reified T : Any> ReactiveSearchOperations.suggest(query: Query): Mono<Suggest> = suggest(query, T::class.java)
26+
inline fun <reified T : Any> ReactiveSearchOperations.suggest(query: Query, index: IndexCoordinates): Mono<Suggest> = suggest(query, T::class.java, index)

0 commit comments

Comments
 (0)