Skip to content

Commit fbceb94

Browse files
GeoNear query with additional filter
1 parent 435ceab commit fbceb94

File tree

9 files changed

+99
-19
lines changed

9 files changed

+99
-19
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor;
5050
import org.springframework.data.mongodb.repository.query.MongoParameterAccessor;
5151
import org.springframework.data.mongodb.repository.query.MongoQueryCreator;
52+
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
5253
import org.springframework.data.repository.query.Parameter;
5354
import org.springframework.data.repository.query.Parameters;
5455
import org.springframework.data.repository.query.QueryMethod;
@@ -80,8 +81,11 @@ public AotQueryCreator() {
8081
@SuppressWarnings("NullAway")
8182
StringQuery createQuery(PartTree partTree, QueryMethod queryMethod) {
8283

84+
85+
boolean geoNear = queryMethod instanceof MongoQueryMethod mqm ? mqm.isGeoNearQuery() : false;
86+
8387
Query query = new MongoQueryCreator(partTree,
84-
new PlaceholderConvertingParameterAccessor(new PlaceholderParameterAccessor(queryMethod)), mappingContext)
88+
new PlaceholderConvertingParameterAccessor(new PlaceholderParameterAccessor(queryMethod)), mappingContext, geoNear, queryMethod.isSearchQuery())
8589
.createQuery();
8690

8791
if (partTree.isLimiting()) {

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,15 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
110110
return aggregationMethodContributor(queryMethod, aggregation);
111111
}
112112

113+
QueryInteraction query = createStringQuery(getRepositoryInformation(), queryMethod,
114+
AnnotatedElementUtils.findMergedAnnotation(method, Query.class), method.getParameterCount());
115+
113116
if (queryMethod.isGeoNearQuery() || (queryMethod.getParameters().getMaxDistanceIndex() != -1
114117
&& queryMethod.getReturnType().isCollectionLike())) {
115-
NearQueryInteraction near = new NearQueryInteraction();
118+
NearQueryInteraction near = new NearQueryInteraction(query);
116119
return nearQueryMethodContributor(queryMethod, near);
117120
}
118121

119-
QueryInteraction query = createStringQuery(getRepositoryInformation(), queryMethod,
120-
AnnotatedElementUtils.findMergedAnnotation(method, Query.class), method.getParameterCount());
121-
122122
if (queryMethod.hasAnnotatedQuery()) {
123123
if (StringUtils.hasText(queryMethod.getAnnotatedQuery())
124124
&& Pattern.compile("[\\?:][#$]\\{.*\\}").matcher(queryMethod.getAnnotatedQuery()).find()) {
@@ -206,8 +206,16 @@ private static MethodContributor<MongoQueryMethod> nearQueryMethodContributor(Mo
206206

207207
CodeBlock.Builder builder = CodeBlock.builder();
208208

209-
String variableName = "nearQuery";
209+
String variableName = context.localVariable("nearQuery");
210210
builder.add(geoNearBlockBuilder(context, queryMethod).usingQueryVariableName(variableName).build());
211+
212+
if (!context.getBindableParameterNames().isEmpty()) {
213+
String filterQueryVariableName = context.localVariable("filterQuery");
214+
builder.add(queryBlockBuilder(context, queryMethod).usingQueryVariableName(filterQueryVariableName)
215+
.filter(interaction.getQuery()).build());
216+
builder.addStatement("$L.query($L)", variableName, filterQueryVariableName);
217+
}
218+
211219
builder.add(geoNearExecutionBlockBuilder(context, queryMethod).referencing(variableName).build());
212220

213221
return builder.build();

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.LinkedHashMap;
1919
import java.util.Map;
2020

21+
import org.springframework.data.mongodb.core.query.Query;
2122
import org.springframework.data.repository.aot.generate.QueryMetadata;
2223
import org.springframework.util.StringUtils;
2324

@@ -30,16 +31,22 @@
3031
class NearQueryInteraction extends MongoInteraction implements QueryMetadata {
3132

3233
private final InteractionType interactionType;
34+
private final QueryInteraction query;
3335

34-
NearQueryInteraction() {
36+
NearQueryInteraction(QueryInteraction query) {
3537
interactionType = InteractionType.QUERY;
38+
this.query = query;
3639
}
3740

3841
@Override
3942
InteractionType getExecutionType() {
4043
return interactionType;
4144
}
4245

46+
public QueryInteraction getQuery() {
47+
return query;
48+
}
49+
4350
@Override
4451
public Map<String, Object> serialize() {
4552

spring-data-mongodb/src/test/java/example/aot/UserRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public interface UserRepository extends CrudRepository<User, String> {
129129

130130
GeoResults<User> findByLocationCoordinatesNear(Point point, Distance maxDistance);
131131

132+
GeoResults<User> findByLocationCoordinatesNearAndLastname(Point point, Distance maxDistance, String lastname);
133+
132134
List<GeoResult<User>> findUserAsListByLocationCoordinatesNear(Point point, Distance maxDistance);
133135

134136
GeoResults<User> findByLocationCoordinatesNear(Point point, Range<Distance> distance);

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

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
*/
1616
package org.springframework.data.mongodb.repository;
1717

18-
import static java.util.Arrays.*;
19-
import static org.assertj.core.api.Assertions.*;
20-
import static org.assertj.core.api.Assumptions.*;
21-
import static org.springframework.data.geo.Metrics.*;
18+
import static java.util.Arrays.asList;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
21+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
22+
import static org.assertj.core.api.Assumptions.assumeThat;
23+
import static org.springframework.data.geo.Metrics.KILOMETERS;
2224

2325
import java.util.ArrayList;
2426
import java.util.Arrays;
@@ -38,13 +40,22 @@
3840
import org.junit.jupiter.api.Test;
3941
import org.junit.jupiter.api.TestInstance;
4042
import org.junit.jupiter.api.extension.ExtendWith;
41-
4243
import org.springframework.beans.factory.annotation.Autowired;
4344
import org.springframework.dao.DuplicateKeyException;
4445
import org.springframework.dao.IncorrectResultSizeDataAccessException;
45-
import org.springframework.data.domain.*;
46+
import org.springframework.data.domain.Example;
47+
import org.springframework.data.domain.ExampleMatcher;
4648
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
49+
import org.springframework.data.domain.Limit;
50+
import org.springframework.data.domain.Page;
51+
import org.springframework.data.domain.PageRequest;
52+
import org.springframework.data.domain.Pageable;
53+
import org.springframework.data.domain.Range;
54+
import org.springframework.data.domain.ScrollPosition;
55+
import org.springframework.data.domain.Slice;
56+
import org.springframework.data.domain.Sort;
4757
import org.springframework.data.domain.Sort.Direction;
58+
import org.springframework.data.domain.Window;
4859
import org.springframework.data.geo.Box;
4960
import org.springframework.data.geo.Circle;
5061
import org.springframework.data.geo.Distance;
@@ -216,8 +227,8 @@ void appliesScrollPositionCorrectly() {
216227
@Test // GH-4397
217228
void appliesLimitToScrollingCorrectly() {
218229

219-
Window<Person> page = repository.findByLastnameLikeOrderByLastnameAscFirstnameAsc("*a*",
220-
ScrollPosition.keyset(), Limit.of(2));
230+
Window<Person> page = repository.findByLastnameLikeOrderByLastnameAscFirstnameAsc("*a*", ScrollPosition.keyset(),
231+
Limit.of(2));
221232

222233
assertThat(page.isLast()).isFalse();
223234
assertThat(page.size()).isEqualTo(2);
@@ -250,7 +261,8 @@ void executesPagedFinderCorrectly() {
250261
@Test // GH-4397
251262
void executesFinderCorrectlyWithSortAndLimit() {
252263

253-
List<Person> page = repository.findByLastnameLike("*a*", Sort.by(Direction.ASC, "lastname", "firstname"), Limit.of(2));
264+
List<Person> page = repository.findByLastnameLike("*a*", Sort.by(Direction.ASC, "lastname", "firstname"),
265+
Limit.of(2));
254266

255267
assertThat(page).containsExactly(carter, stefan);
256268
}
@@ -462,6 +474,22 @@ void executesGeoNearQueryForResultsCorrectly() {
462474
assertThat(results.getContent()).isNotEmpty();
463475
}
464476

477+
@Test
478+
void executesGeoNearQueryWithAdditionalFilterCorrectly() {
479+
480+
Point point = new Point(-73.99171, 40.738868);
481+
dave.setLocation(point);
482+
repository.save(dave);
483+
484+
Person p2 = new Person("fn", "ln", 42, Sex.MALE);
485+
p2.setLocation(point);
486+
repository.save(p2);
487+
488+
GeoResults<Person> results = repository.findByLocationNearAndLastname(new Point(-73.99, 40.73),
489+
Distance.of(2000, Metrics.KILOMETERS), "ln");
490+
assertThat(results.getContent()).hasSize(1);
491+
}
492+
465493
@Test
466494
void executesGeoPageQueryForResultsCorrectly() {
467495

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ Window<Person> findByLastnameLikeOrderByLastnameAscFirstnameAsc(String lastname,
221221
List<Person> findByNamedQuery(String firstname);
222222

223223
GeoResults<Person> findByLocationNear(Point point, Distance maxDistance);
224+
GeoResults<Person> findByLocationNearAndLastname(Point point, Distance maxDistance, String Lastname);
224225

225226
// DATAMONGO-1110
226227
GeoResults<Person> findPersonByLocationNear(Point point, Range<Distance> distance);

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ private Object getFragmentFacadeProxy(Object fragment) {
9393
Method target = ReflectionUtils.findMethod(fragment.getClass(), method.getName(), method.getParameterTypes());
9494

9595
if (target == null) {
96-
throw new NoSuchMethodException("Method [%s] is not implemented by [%s]".formatted(method, target));
96+
throw new MethodNotImplementedException("Method [%s] is not implemented by [%s]".formatted(method, target));
9797
}
9898

9999
try {
@@ -127,4 +127,11 @@ public ProjectionFactory getProjectionFactory() {
127127
}
128128
};
129129
}
130+
131+
public static class MethodNotImplementedException extends RuntimeException {
132+
133+
public MethodNotImplementedException(String message) {
134+
super(message);
135+
}
136+
}
130137
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
6060
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
6161
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
62+
import org.springframework.data.mongodb.repository.aot.AotFragmentTestConfigurationSupport.MethodNotImplementedException;
6263
import org.springframework.data.mongodb.test.util.Client;
6364
import org.springframework.data.mongodb.test.util.MongoClientExtension;
6465
import org.springframework.data.mongodb.test.util.MongoTestUtils;
@@ -675,6 +676,14 @@ void testNearWithGeoResult() {
675676
assertThat(users).extracting(GeoResult::getContent).extracting(User::getUsername).containsExactly("leia");
676677
}
677678

679+
@Test
680+
void testNearWithAdditionalFilterQueryAsGeoResult() {
681+
682+
GeoResults<User> users = fragment.findByLocationCoordinatesNearAndLastname(new Point(-73.99, 40.73),
683+
Distance.of(50, Metrics.KILOMETERS), "Organa");
684+
assertThat(users).extracting(GeoResult::getContent).extracting(User::getUsername).containsExactly("leia");
685+
}
686+
678687
@Test
679688
void testNearReturningListOfGeoResult() {
680689

@@ -695,7 +704,7 @@ void testNearWithRange() {
695704
@Test
696705
void testNearReturningGeoPage() {
697706

698-
assertThatExceptionOfType(NoSuchMethodException.class)
707+
assertThatExceptionOfType(MethodNotImplementedException.class)
699708
.isThrownBy(() -> fragment.findByLocationCoordinatesNear(new Point(-73.99, 40.73),
700709
Distance.of(2000, Metrics.KILOMETERS), PageRequest.of(0, 1)));
701710

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,21 @@ void rendersNearQueryForGeoResults() throws NoSuchMethodException {
118118
.contains("NearQuery.near(point)") //
119119
.contains("nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric())") //
120120
.contains(".withReadPreference(com.mongodb.ReadPreference.valueOf(\"NEAREST\")") //
121+
.doesNotContain(".query(") //
122+
.contains(".near(nearQuery).all()");
123+
}
124+
125+
@Test
126+
void rendersNearQueryWithFilterForGeoResults() throws NoSuchMethodException {
127+
128+
MethodSpec methodSpec = codeOf(UserRepository.class, "findByLocationCoordinatesNearAndLastname", Point.class,
129+
Distance.class, String.class);
130+
131+
assertThat(methodSpec.toString()) //
132+
.contains("NearQuery.near(point)") //
133+
.contains("nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric())") //
134+
.contains("filterQuery = createQuery(\"{'lastname':?0}\", new java.lang.Object[]{ lastname })") //
135+
.contains("nearQuery.query(filterQuery)") //
121136
.contains(".near(nearQuery).all()");
122137
}
123138

@@ -151,6 +166,5 @@ interface UserRepoWithMeta extends Repository<User, String> {
151166

152167
@ReadPreference("NEAREST")
153168
GeoResults<User> findByLocationCoordinatesNear(Point point, Distance maxDistance);
154-
155169
}
156170
}

0 commit comments

Comments
 (0)