From 259c43af19db269476d31bfe5df5554807b7b2f9 Mon Sep 17 00:00:00 2001 From: diamondT Date: Mon, 27 Jun 2022 21:35:06 +0300 Subject: [PATCH] Fix handling of array-of-strings parameters for @Query-annotated queries. Original Pull Request #2182 Closes #2135 --- ...tReactiveElasticsearchRepositoryQuery.java | 3 +- .../query/ConvertingParameterAccessor.java | 95 ------------------- ...tiveElasticsearchStringQueryUnitTests.java | 46 ++++++++- ...asticsearchRepositoryIntegrationTests.java | 56 +++++++++++ 4 files changed, 102 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/org/springframework/data/elasticsearch/repository/query/ConvertingParameterAccessor.java diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractReactiveElasticsearchRepositoryQuery.java b/src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractReactiveElasticsearchRepositoryQuery.java index 37e9f1291..6e49eb651 100644 --- a/src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractReactiveElasticsearchRepositoryQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/repository/query/AbstractReactiveElasticsearchRepositoryQuery.java @@ -82,8 +82,7 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor ResultProcessor processor = queryMethod.getResultProcessor().withDynamicProjection(parameterAccessor); - Query query = createQuery( - new ConvertingParameterAccessor(elasticsearchOperations.getElasticsearchConverter(), parameterAccessor)); + Query query = createQuery(parameterAccessor); if (queryMethod.hasAnnotatedHighlight()) { query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery()); diff --git a/src/main/java/org/springframework/data/elasticsearch/repository/query/ConvertingParameterAccessor.java b/src/main/java/org/springframework/data/elasticsearch/repository/query/ConvertingParameterAccessor.java deleted file mode 100644 index 119a8f6a8..000000000 --- a/src/main/java/org/springframework/data/elasticsearch/repository/query/ConvertingParameterAccessor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019-2022 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.elasticsearch.repository.query; - -import java.util.Iterator; -import java.util.Optional; - -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter; -import org.springframework.lang.Nullable; - -/** - * @author Christoph Strobl - * @author Peter-Josef Meisch - * @since 3.2 - */ -public class ConvertingParameterAccessor implements ElasticsearchParameterAccessor { - - private final ElasticsearchConverter converter; - private final ElasticsearchParameterAccessor delegate; - - public ConvertingParameterAccessor(ElasticsearchConverter converter, ElasticsearchParameterAccessor delegate) { - - this.converter = converter; - this.delegate = delegate; - } - - @Override - public Object[] getValues() { - return delegate.getValues(); - } - - @Override - public Pageable getPageable() { - return delegate.getPageable(); - } - - @Override - public Sort getSort() { - return delegate.getSort(); - } - - // @Override - // public Optional> getDynamicProjection() { - // return delegate.getDynamicProjection(); - // } - - @Override - public Class findDynamicProjection() { - return delegate.findDynamicProjection(); - } - - @Override - public Object getBindableValue(int index) { - return getConvertedValue(delegate.getBindableValue(index)); - } - - @Override - public boolean hasBindableNullValue() { - return delegate.hasBindableNullValue(); - } - - @Override - public Iterator iterator() { - return delegate.iterator(); - } - - @Nullable - private Object getConvertedValue(Object value) { - - if (value == null) { - return "null"; - } - - if (converter.getConversionService().canConvert(value.getClass(), String.class)) { - return converter.getConversionService().convert(value, String.class); - } - - return value.toString(); - } -} diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/query/ReactiveElasticsearchStringQueryUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/query/ReactiveElasticsearchStringQueryUnitTests.java index e9a362df5..047de67e9 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/query/ReactiveElasticsearchStringQueryUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/query/ReactiveElasticsearchStringQueryUnitTests.java @@ -144,10 +144,37 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri .isEqualTo("{ 'bool' : { 'must' : { 'term' : { 'car' : 'Toyota-Prius' } } } }"); } + @Test // #2135 + @DisplayName("should handle array-of-strings parameters correctly") + void shouldHandleArrayOfStringsParametersCorrectly() throws Exception { + + List otherNames = List.of("Wesley", "Emmett"); + + org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByOtherNames", otherNames); + + assertThat(query).isInstanceOf(StringQuery.class); + assertThat(((StringQuery) query).getSource()) + .isEqualTo("{ 'bool' : { 'must' : { 'terms' : { 'otherNames' : [\"Wesley\",\"Emmett\"] } } } }"); + } + + @Test // #2135 + @DisplayName("should handle array-of-Integers parameters correctly") + void shouldHandleArrayOfIntegerParametersCorrectly() throws Exception { + + List ages = List.of(42, 57); + + org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByAges", ages); + + assertThat(query).isInstanceOf(StringQuery.class); + assertThat(((StringQuery) query).getSource()) + .isEqualTo("{ 'bool' : { 'must' : { 'terms' : { 'ages' : [42,57] } } } }"); + } + private org.springframework.data.elasticsearch.core.query.Query createQuery(String methodName, Object... args) throws NoSuchMethodException { - Class[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new); + Class[] argTypes = Arrays.stream(args).map(Object::getClass) + .map(clazz -> Collection.class.isAssignableFrom(clazz) ? List.class : clazz).toArray(Class[]::new); ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(methodName, argTypes); ReactiveElasticsearchStringQuery elasticsearchStringQuery = queryForMethod(queryMethod); @@ -195,6 +222,12 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri @Query("{ 'bool' : { 'must' : { 'term' : { 'car' : '?0' } } } }") Mono findByCar(Car car); + @Query("{ 'bool' : { 'must' : { 'terms' : { 'otherNames' : ?0 } } } }") + Flux findByOtherNames(List otherNames); + + @Query("{ 'bool' : { 'must' : { 'terms' : { 'ages' : ?0 } } } }") + Flux findByAges(List ages); + } /** @@ -211,6 +244,8 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri @Nullable private String name; + @Nullable private List otherNames; + @Nullable @Field(type = FieldType.Nested) private List car; @@ -235,6 +270,15 @@ public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStri this.name = name; } + @Nullable + public List getOtherNames() { + return otherNames; + } + + public void setOtherNames(List otherNames) { + this.otherNames = otherNames; + } + @Nullable public List getCar() { return car; diff --git a/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryIntegrationTests.java index 2f50033b8..3368732f0 100644 --- a/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/repository/support/SimpleReactiveElasticsearchRepositoryIntegrationTests.java @@ -588,6 +588,52 @@ abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests { assertThat(documentWithIdExistsInIndex("id-three").block()).isTrue(); } + @Test // #2135 + void FluxOfSearchHitForArrayQuery() { + bulkIndex(new SampleEntity("id-one", "message1"), // + new SampleEntity("id-two", "message2"), // + new SampleEntity("id-three", "message3")) // + .block(); + + repository.findAllViaAnnotatedQueryByMessageIn(List.of("message1", "message3")) // + .as(StepVerifier::create) // + .consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-one")) // + .consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) // + .verifyComplete(); + + } + + @Test // #2135 + void FluxOfSearchHitForIntegerArrayQuery() { + bulkIndex(new SampleEntity("id-one", "test", 3), // + new SampleEntity("id-two", "test test", 1), // + new SampleEntity("id-three", "test test", 2)) // + .block(); + + repository.findAllViaAnnotatedQueryByRatesIn(List.of(2, 3)) // + .as(StepVerifier::create) // + .consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-one")) // + .consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) // + .verifyComplete(); + + } + + @Test // #2135 + void FluxOfSearchHitForStringAndIntegerArrayQuery() { + bulkIndex(new SampleEntity("id-one", "message1", 1), // + new SampleEntity("id-two", "message2", 2), // + new SampleEntity("id-three", "message3", 3), // + new SampleEntity("id-four", "message4", 4), // + new SampleEntity("id-five", "message5", 5)) // + .block(); + + repository.findAllViaAnnotatedQueryByMessageInAndRatesIn(List.of("message5", "message3"), List.of(2, 3)) // + .as(StepVerifier::create) // + .consumeNextWith(it -> assertThat(it.getId()).isEqualTo("id-three")) // + .verifyComplete(); + + } + Mono bulkIndex(SampleEntity... entities) { return operations.saveAll(Arrays.asList(entities), IndexCoordinates.of(indexNameProvider.indexName())).then(); } @@ -627,6 +673,16 @@ abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests { @CountQuery(value = "{\"bool\": {\"must\": [{\"term\": {\"message\": \"?0\"}}]}}") Mono retrieveCountByText(String message); + + @Query("{ \"terms\": { \"message\": ?0 } }") + Flux findAllViaAnnotatedQueryByMessageIn(List messages); + + @Query("{ \"terms\": { \"rate\": ?0 } }") + Flux findAllViaAnnotatedQueryByRatesIn(List rates); + + @Query("{\"bool\": {\"must\": [{ \"terms\": { \"message\": ?0 } }, { \"terms\": { \"rate\": ?1 } }] } }") + Flux findAllViaAnnotatedQueryByMessageInAndRatesIn(List messages, List rates); + } @Document(indexName = "#{@indexNameProvider.indexName()}")