Skip to content

Commit 09835d8

Browse files
Add Collation to generated query if present.
1 parent 52be56d commit 09835d8

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/MongoAotRepositoryFragmentSupport.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.ArrayList;
1919
import java.util.List;
20+
import java.util.Locale;
2021
import java.util.Map;
2122

2223
import org.bson.Document;
@@ -31,6 +32,7 @@
3132
import org.springframework.data.mongodb.core.convert.MongoConverter;
3233
import org.springframework.data.mongodb.core.mapping.FieldName;
3334
import org.springframework.data.mongodb.core.query.BasicQuery;
35+
import org.springframework.data.mongodb.core.query.Collation;
3436
import org.springframework.data.mongodb.repository.query.MongoParameters;
3537
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
3638
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
@@ -106,6 +108,39 @@ protected Document bindParameters(String source, Map<String, Object> parameters)
106108
return new ParameterBindingDocumentCodec().decode(source, bindingContext);
107109
}
108110

111+
protected Object evaluate(String source, Map<String, Object> parameters) {
112+
113+
ValueEvaluationContext valueEvaluationContext = this.valueExpressionDelegate.getEvaluationContextAccessor()
114+
.create(new NoMongoParameters()).getEvaluationContext(parameters.values());
115+
116+
EvaluationContext evaluationContext = valueEvaluationContext.getEvaluationContext();
117+
parameters.forEach(evaluationContext::setVariable);
118+
119+
ValueExpression parse = valueExpressionDelegate.getValueExpressionParser().parse(source);
120+
return parse.evaluate(valueEvaluationContext);
121+
}
122+
123+
protected Collation collationOf(@Nullable Object source) {
124+
125+
if(source == null) {
126+
return Collation.simple();
127+
}
128+
if (source instanceof String) {
129+
return Collation.parse(source.toString());
130+
}
131+
if (source instanceof Locale locale) {
132+
return Collation.of(locale);
133+
}
134+
if (source instanceof Document document) {
135+
return Collation.from(document);
136+
}
137+
if (source instanceof Collation collation) {
138+
return collation;
139+
}
140+
throw new IllegalArgumentException(
141+
"Unsupported collation source [%s]".formatted(ObjectUtils.nullSafeClassName(source)));
142+
}
143+
109144
protected BasicQuery createQuery(String queryString, Object[] parameters) {
110145

111146
Document queryDocument = bindParameters(queryString, parameters);

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/aot/QueryBlocks.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.geo.Polygon;
3030
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
3131
import org.springframework.data.mongodb.core.MongoOperations;
32+
import org.springframework.data.mongodb.core.annotation.Collation;
3233
import org.springframework.data.mongodb.core.geo.GeoJson;
3334
import org.springframework.data.mongodb.core.geo.Sphere;
3435
import org.springframework.data.mongodb.core.query.BasicQuery;
@@ -264,12 +265,26 @@ CodeBlock build() {
264265
}
265266

266267
String comment = metaAnnotation.getString("comment");
267-
if (StringUtils.hasText("comment")) {
268+
if (StringUtils.hasText(comment)) {
268269
builder.addStatement("$L.comment($S)", queryVariableName, comment);
269270
}
270271
}
271272

272-
// TODO: Meta annotation: Disk usage
273+
MergedAnnotation<Collation> collationAnnotation = context.getAnnotation(Collation.class);
274+
if (collationAnnotation.isPresent()) {
275+
276+
String collationString = collationAnnotation.getString("value");
277+
if(StringUtils.hasText(collationString)) {
278+
if (!MongoCodeBlocks.containsPlaceholder(collationString)) {
279+
builder.addStatement("$L.collation($T.parse($S))", queryVariableName,
280+
org.springframework.data.mongodb.core.query.Collation.class, collationString);
281+
} else {
282+
builder.add("$L.collation(collationOf(evaluate($S, ", queryVariableName, collationString);
283+
builder.add(MongoCodeBlocks.renderArgumentMap(arguments));
284+
builder.add(")));\n");
285+
}
286+
}
287+
}
273288

274289
return builder.build();
275290
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/aot/QueryMethodContributionUnitTests.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.lang.reflect.Method;
2424
import java.util.Arrays;
25+
import java.util.List;
2526
import java.util.regex.Pattern;
2627

2728
import javax.lang.model.element.Modifier;
@@ -37,8 +38,10 @@
3738
import org.springframework.data.geo.Point;
3839
import org.springframework.data.geo.Polygon;
3940
import org.springframework.data.mongodb.core.MongoOperations;
41+
import org.springframework.data.mongodb.core.annotation.Collation;
4042
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
4143
import org.springframework.data.mongodb.core.geo.Sphere;
44+
import org.springframework.data.mongodb.repository.Hint;
4245
import org.springframework.data.mongodb.repository.ReadPreference;
4346
import org.springframework.data.repository.Repository;
4447
import org.springframework.data.repository.aot.generate.AotQueryMethodGenerationContext;
@@ -211,8 +214,36 @@ void rendersRegexCriteria() throws NoSuchMethodException {
211214
MethodSpec methodSpec = codeOf(UserRepository.class, "findByFirstnameRegex", Pattern.class);
212215

213216
assertThat(methodSpec.toString()) //
214-
.contains("createQuery(\"{'firstname':{'$regex':?0}}\"") //
215-
.contains("Object[]{ pattern }");
217+
.contains("createQuery(\"{'firstname':{'$regex':?0}}\"") //
218+
.contains("Object[]{ pattern }");
219+
}
220+
221+
@Test // GH-4939
222+
void rendersHint() throws NoSuchMethodException {
223+
224+
MethodSpec methodSpec = codeOf(UserRepoWithMeta.class, "findByFirstname", String.class);
225+
226+
assertThat(methodSpec.toString()) //
227+
.contains(".withHint(\"fn-idx\")");
228+
}
229+
230+
@Test // GH-4939
231+
void rendersCollation() throws NoSuchMethodException {
232+
233+
MethodSpec methodSpec = codeOf(UserRepoWithMeta.class, "findByFirstname", String.class);
234+
235+
assertThat(methodSpec.toString()) //
236+
.containsPattern(".*\\.collation\\(.*Collation\\.parse\\(\"en_US\"\\)\\)");
237+
}
238+
239+
@Test // GH-4939
240+
void rendersCollationFromExpression() throws NoSuchMethodException {
241+
242+
MethodSpec methodSpec = codeOf(UserRepoWithMeta.class, "findWithCollationByFirstname", String.class, String.class);
243+
244+
assertThat(methodSpec.toString()) //
245+
.containsIgnoringWhitespaces(
246+
"collationOf(evaluate(\"?#{[1]}\", java.util.Map.of(\"firstname\", firstname, \"locale\", locale)))");
216247
}
217248

218249
private static MethodSpec codeOf(Class<?> repository, String methodName, Class<?>... args)
@@ -228,7 +259,7 @@ private static MethodSpec codeOf(Class<?> repository, String methodName, Class<?
228259
Assertions.fail("No contribution for method %s.%s(%s)".formatted(repository.getSimpleName(), methodName,
229260
Arrays.stream(args).map(Class::getSimpleName).toList()));
230261
}
231-
AotRepositoryFragmentMetadata metadata = new AotRepositoryFragmentMetadata(ClassName.get(UserRepository.class));
262+
AotRepositoryFragmentMetadata metadata = new AotRepositoryFragmentMetadata(ClassName.get(repository));
232263
metadata.addField(
233264
FieldSpec.builder(MongoOperations.class, "mongoOperations", Modifier.PRIVATE, Modifier.FINAL).build());
234265

@@ -247,6 +278,13 @@ protected TestQueryMethodGenerationContext(RepositoryInformation repositoryInfor
247278

248279
interface UserRepoWithMeta extends Repository<User, String> {
249280

281+
@Hint("fn-idx")
282+
@Collation("en_US")
283+
List<User> findByFirstname(String firstname);
284+
285+
@Collation("?#{[1]}")
286+
List<User> findWithCollationByFirstname(String firstname, String locale);
287+
250288
@ReadPreference("NEAREST")
251289
GeoResults<User> findByLocationCoordinatesNear(Point point, Distance maxDistance);
252290
}

0 commit comments

Comments
 (0)