Skip to content

Commit 6f84a1c

Browse files
authored
Support collection parameters in @query methods.
Original Pull Request #1856 Closes #1858
1 parent 112ca59 commit 6f84a1c

File tree

2 files changed

+87
-5
lines changed

2 files changed

+87
-5
lines changed

Diff for: src/main/java/org/springframework/data/elasticsearch/repository/support/StringQueryUtil.java

+28-4
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
*/
1616
package org.springframework.data.elasticsearch.repository.support;
1717

18+
import java.util.Collection;
1819
import java.util.regex.Matcher;
1920
import java.util.regex.Pattern;
21+
import java.util.stream.Collectors;
2022

2123
import org.springframework.core.convert.support.GenericConversionService;
2224
import org.springframework.data.repository.query.ParameterAccessor;
2325
import org.springframework.util.NumberUtils;
2426

2527
/**
2628
* @author Peter-Josef Meisch
29+
* @author Niklas Herder
2730
*/
2831
final public class StringQueryUtil {
2932

@@ -53,6 +56,28 @@ private static String getParameterWithIndex(ParameterAccessor accessor, int inde
5356
// noinspection ConstantConditions
5457
if (parameter != null) {
5558

59+
parameterValue = convert(parameter);
60+
}
61+
62+
return parameterValue;
63+
64+
}
65+
66+
private static String convert(Object parameter) {
67+
if (Collection.class.isAssignableFrom(parameter.getClass())) {
68+
Collection<?> collectionParam = (Collection<?>) parameter;
69+
StringBuilder sb = new StringBuilder("[");
70+
sb.append(collectionParam.stream().map(o -> {
71+
if (o instanceof String) {
72+
return "\"" + convert(o) + "\"";
73+
} else {
74+
return convert(o);
75+
}
76+
}).collect(Collectors.joining(",")));
77+
sb.append("]");
78+
return sb.toString();
79+
} else {
80+
String parameterValue = "null";
5681
if (conversionService.canConvert(parameter.getClass(), String.class)) {
5782
String converted = conversionService.convert(parameter, String.class);
5883

@@ -62,11 +87,10 @@ private static String getParameterWithIndex(ParameterAccessor accessor, int inde
6287
} else {
6388
parameterValue = parameter.toString();
6489
}
65-
}
66-
67-
parameterValue = parameterValue.replaceAll("\"", Matcher.quoteReplacement("\\\""));
68-
return parameterValue;
6990

91+
parameterValue = parameterValue.replaceAll("\"", Matcher.quoteReplacement("\\\""));
92+
return parameterValue;
93+
}
7094
}
7195

7296
}

Diff for: src/test/java/org/springframework/data/elasticsearch/repository/query/ElasticsearchStringQueryUnitTests.java

+59-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import java.lang.reflect.Method;
21+
import java.util.ArrayList;
2122
import java.util.Arrays;
2223
import java.util.Collection;
2324
import java.util.HashMap;
@@ -51,6 +52,7 @@
5152
/**
5253
* @author Christoph Strobl
5354
* @author Peter-Josef Meisch
55+
* @author Niklas Herder
5456
*/
5557
@ExtendWith(MockitoExtension.class)
5658
public class ElasticsearchStringQueryUnitTests {
@@ -95,14 +97,50 @@ void shouldEscapeStringsInQueryParameters() throws Exception {
9597
.isEqualTo("{\"bool\":{\"must\": [{\"match\": {\"prefix\": {\"name\" : \"hello \\\"Stranger\\\"\"}}]}}");
9698
}
9799

98-
private org.springframework.data.elasticsearch.core.query.Query createQuery(String methodName, String... args)
100+
@Test // #1858
101+
@DisplayName("should only quote String query parameters")
102+
void shouldOnlyEscapeStringQueryParameters() throws Exception {
103+
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByAge", Integer.valueOf(30));
104+
105+
assertThat(query).isInstanceOf(StringQuery.class);
106+
assertThat(((StringQuery) query).getSource()).isEqualTo("{ 'bool' : { 'must' : { 'term' : { 'age' : 30 } } } }");
107+
108+
}
109+
110+
@Test // #1858
111+
@DisplayName("should only quote String collection query parameters")
112+
void shouldOnlyEscapeStringCollectionQueryParameters() throws Exception {
113+
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByAgeIn",
114+
new ArrayList<>(Arrays.asList(30, 35, 40)));
115+
116+
assertThat(query).isInstanceOf(StringQuery.class);
117+
assertThat(((StringQuery) query).getSource())
118+
.isEqualTo("{ 'bool' : { 'must' : { 'term' : { 'age' : [30,35,40] } } } }");
119+
120+
}
121+
122+
@Test // #1858
123+
@DisplayName("should escape Strings in collection query parameters")
124+
void shouldEscapeStringsInCollectionsQueryParameters() throws Exception {
125+
126+
final List<String> another_string = Arrays.asList("hello \"Stranger\"", "Another string");
127+
List<String> params = new ArrayList<>(another_string);
128+
org.springframework.data.elasticsearch.core.query.Query query = createQuery("findByNameIn", params);
129+
130+
assertThat(query).isInstanceOf(StringQuery.class);
131+
assertThat(((StringQuery) query).getSource()).isEqualTo(
132+
"{ 'bool' : { 'must' : { 'terms' : { 'name' : [\"hello \\\"Stranger\\\"\",\"Another string\"] } } } }");
133+
}
134+
135+
private org.springframework.data.elasticsearch.core.query.Query createQuery(String methodName, Object... args)
99136
throws NoSuchMethodException {
100137

101138
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
102139
ElasticsearchQueryMethod queryMethod = getQueryMethod(methodName, argTypes);
103140
ElasticsearchStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
104141
return elasticsearchStringQuery.createQuery(new ElasticsearchParametersParameterAccessor(queryMethod, args));
105142
}
143+
106144
private ElasticsearchStringQuery queryForMethod(ElasticsearchQueryMethod queryMethod) {
107145
return new ElasticsearchStringQuery(queryMethod, operations, queryMethod.getAnnotatedQuery());
108146
}
@@ -116,9 +154,18 @@ private ElasticsearchQueryMethod getQueryMethod(String name, Class<?>... paramet
116154

117155
private interface SampleRepository extends Repository<Person, String> {
118156

157+
@Query("{ 'bool' : { 'must' : { 'term' : { 'age' : ?0 } } } }")
158+
List<Person> findByAge(Integer age);
159+
160+
@Query("{ 'bool' : { 'must' : { 'term' : { 'age' : ?0 } } } }")
161+
List<Person> findByAgeIn(ArrayList<Integer> age);
162+
119163
@Query("{ 'bool' : { 'must' : { 'term' : { 'name' : '?0' } } } }")
120164
Person findByName(String name);
121165

166+
@Query("{ 'bool' : { 'must' : { 'terms' : { 'name' : ?0 } } } }")
167+
Person findByNameIn(ArrayList<String> names);
168+
122169
@Query(value = "name:(?0, ?11, ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?0, ?1)")
123170
Person findWithRepeatedPlaceholder(String arg0, String arg1, String arg2, String arg3, String arg4, String arg5,
124171
String arg6, String arg7, String arg8, String arg9, String arg10, String arg11);
@@ -131,16 +178,27 @@ Person findWithRepeatedPlaceholder(String arg0, String arg1, String arg2, String
131178
* @author Rizwan Idrees
132179
* @author Mohsin Husen
133180
* @author Artur Konczak
181+
* @author Niklas Herder
134182
*/
135183

136184
@Document(indexName = "test-index-person-query-unittest")
137185
static class Person {
138186

187+
@Nullable public int age;
139188
@Nullable @Id private String id;
140189
@Nullable private String name;
141190
@Nullable @Field(type = FieldType.Nested) private List<Car> car;
142191
@Nullable @Field(type = FieldType.Nested, includeInParent = true) private List<Book> books;
143192

193+
@Nullable
194+
public int getAge() {
195+
return age;
196+
}
197+
198+
public void setAge(int age) {
199+
this.age = age;
200+
}
201+
144202
@Nullable
145203
public String getId() {
146204
return id;

0 commit comments

Comments
 (0)