Skip to content

Commit 871bea6

Browse files
GeoNearExecution
1 parent 62c86e3 commit 871bea6

File tree

5 files changed

+158
-1
lines changed

5 files changed

+158
-1
lines changed

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

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.data.domain.Sort.Order;
3232
import org.springframework.data.geo.Box;
3333
import org.springframework.data.geo.Circle;
34+
import org.springframework.data.geo.Distance;
3435
import org.springframework.data.geo.Polygon;
3536
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
3637
import org.springframework.data.mongodb.core.ExecutableRemoveOperation.ExecutableRemove;
@@ -46,6 +47,7 @@
4647
import org.springframework.data.mongodb.core.query.BasicQuery;
4748
import org.springframework.data.mongodb.core.query.BasicUpdate;
4849
import org.springframework.data.mongodb.core.query.Collation;
50+
import org.springframework.data.mongodb.core.query.NearQuery;
4951
import org.springframework.data.mongodb.repository.Hint;
5052
import org.springframework.data.mongodb.repository.Meta;
5153
import org.springframework.data.mongodb.repository.ReadPreference;
@@ -151,6 +153,12 @@ static AggregationCodeBlockBuilder aggregationBlockBuilder(AotQueryMethodGenerat
151153
return new AggregationCodeBlockBuilder(context, queryMethod);
152154
}
153155

156+
static GeoNearCodeBlockBuilder geoNearBlockBuilder(AotQueryMethodGenerationContext context,
157+
MongoQueryMethod queryMethod) {
158+
159+
return new GeoNearCodeBlockBuilder(context, queryMethod);
160+
}
161+
154162
/**
155163
* Builder for generating aggregation execution {@link CodeBlock}.
156164
*
@@ -467,14 +475,77 @@ CodeBlock build() {
467475
}
468476
}
469477

478+
static class GeoNearCodeBlockBuilder {
479+
480+
private final AotQueryMethodGenerationContext context;
481+
private final MongoQueryMethod queryMethod;
482+
private final List<CodeBlock> arguments;
483+
484+
private String variableName;
485+
486+
GeoNearCodeBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) {
487+
488+
this.context = context;
489+
this.arguments = context.getBindableParameterNames().stream().map(CodeBlock::of).collect(Collectors.toList());
490+
this.queryMethod = queryMethod;
491+
}
492+
493+
CodeBlock build() {
494+
495+
CodeBlock.Builder builder = CodeBlock.builder();
496+
builder.add("\n");
497+
498+
String locationParameterName = context.getParameterName(queryMethod.getParameters().getNearIndex());
499+
500+
builder.addStatement("$1T $2L = $1T.near($3L)", NearQuery.class, variableName, locationParameterName);
501+
502+
if (queryMethod.getParameters().getRangeIndex() != -1) {
503+
504+
String rangeParametername = context.getParameterName(queryMethod.getParameters().getRangeIndex());
505+
String minVarName = context.localVariable("min");
506+
String maxVarName = context.localVariable("max");
507+
508+
builder.beginControlFlow("if($L.getLowerBound().isPresent())", rangeParametername);
509+
builder.addStatement("$1T $2L = $3L.getLowerBound().get()", Distance.class, minVarName, rangeParametername);
510+
builder.addStatement("$1L.minDistance($2L.getValue()).in($2L.getMetric())", variableName, minVarName);
511+
builder.endControlFlow();
512+
513+
builder.beginControlFlow("if($L.getUpperBound().isPresent())", rangeParametername);
514+
builder.addStatement("$1T $2L = $3L.getUpperBound().get()", Distance.class, maxVarName, rangeParametername);
515+
builder.addStatement("$1L.maxDistance($2L.getValue()).in($2L.getMetric())", variableName, maxVarName);
516+
builder.endControlFlow();
517+
} else {
518+
519+
String distanceParametername = context.getParameterName(queryMethod.getParameters().getMaxDistanceIndex());
520+
builder.addStatement("$1L.maxDistance($2L.getValue()).in($2L.getMetric())", variableName,
521+
distanceParametername);
522+
}
523+
524+
if (context.getPageableParameterName() != null) {
525+
builder.addStatement("$L.with($L)", variableName, context.getPageableParameterName());
526+
}
527+
528+
builder.add("\n");
529+
builder.addStatement("return $L.query($T.class).near($L).all()", context.fieldNameOf(MongoOperations.class),
530+
context.getRepositoryInformation().getDomainType(), variableName);
531+
return builder.build();
532+
}
533+
534+
public GeoNearCodeBlockBuilder usingQueryVariableName(String variableName) {
535+
this.variableName = variableName;
536+
return this;
537+
}
538+
}
539+
470540
@NullUnmarked
471541
static class AggregationCodeBlockBuilder {
472542

473543
private final AotQueryMethodGenerationContext context;
474544
private final MongoQueryMethod queryMethod;
545+
private final List<CodeBlock> arguments;
475546

476547
private AggregationInteraction source;
477-
private final List<CodeBlock> arguments;
548+
478549
private String aggregationVariableName;
479550
private boolean pipelineOnly;
480551

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
9595
return aggregationMethodContributor(queryMethod, aggregation);
9696
}
9797

98+
if(queryMethod.isGeoNearQuery()) {
99+
NearQueryInteraction near = new NearQueryInteraction();
100+
return nearQueryMethodContributor(queryMethod, near);
101+
}
102+
98103
QueryInteraction query = createStringQuery(getRepositoryInformation(), queryMethod,
99104
AnnotatedElementUtils.findMergedAnnotation(method, Query.class), method.getParameterCount());
100105

@@ -181,6 +186,20 @@ private static boolean backoff(MongoQueryMethod method) {
181186
return skip;
182187
}
183188

189+
private static MethodContributor<MongoQueryMethod> nearQueryMethodContributor(MongoQueryMethod queryMethod,
190+
NearQueryInteraction interaction) {
191+
192+
return MethodContributor.forQueryMethod(queryMethod).withMetadata(interaction).contribute(context -> {
193+
194+
CodeBlock.Builder builder = CodeBlock.builder();
195+
196+
builder.add(geoNearBlockBuilder(context, queryMethod).usingQueryVariableName("nearQuery").build());
197+
// builder.add(aggregationExecutionBlockBuilder(context, queryMethod).referencing("aggregation").build());
198+
199+
return builder.build();
200+
});
201+
}
202+
184203
private static MethodContributor<MongoQueryMethod> aggregationMethodContributor(MongoQueryMethod queryMethod,
185204
AggregationInteraction aggregation) {
186205

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.repository.aot;
17+
18+
import java.util.LinkedHashMap;
19+
import java.util.Map;
20+
21+
import org.springframework.data.repository.aot.generate.QueryMetadata;
22+
import org.springframework.util.StringUtils;
23+
24+
/**
25+
* An {@link MongoInteraction} to execute a query.
26+
*
27+
* @author Christoph Strobl
28+
* @since 5.0
29+
*/
30+
class NearQueryInteraction extends MongoInteraction implements QueryMetadata {
31+
32+
private final InteractionType interactionType;
33+
34+
NearQueryInteraction() {
35+
interactionType = InteractionType.QUERY;
36+
}
37+
38+
@Override
39+
InteractionType getExecutionType() {
40+
return interactionType;
41+
}
42+
43+
@Override
44+
public Map<String, Object> serialize() {
45+
46+
Map<String, Object> serialized = new LinkedHashMap<>();
47+
48+
49+
50+
return serialized;
51+
}
52+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@
3434
import org.springframework.data.domain.Window;
3535
import org.springframework.data.geo.Box;
3636
import org.springframework.data.geo.Circle;
37+
import org.springframework.data.geo.Distance;
38+
import org.springframework.data.geo.GeoResults;
3739
import org.springframework.data.geo.Point;
3840
import org.springframework.data.geo.Polygon;
3941
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
4042
import org.springframework.data.mongodb.repository.Aggregation;
4143
import org.springframework.data.mongodb.repository.Hint;
44+
import org.springframework.data.mongodb.repository.Person;
4245
import org.springframework.data.mongodb.repository.Query;
4346
import org.springframework.data.mongodb.repository.ReadPreference;
4447
import org.springframework.data.mongodb.repository.Update;
@@ -115,6 +118,8 @@ public interface UserRepository extends CrudRepository<User, String> {
115118

116119
List<User> findByLocationCoordinatesWithin(Polygon polygon);
117120

121+
GeoResults<User> findByLocationCoordinatesNear(Point point, Distance maxDistance);
122+
118123
// TODO: GeoQueries
119124
// TODO: TextSearch
120125

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
import org.springframework.data.domain.Window;
4848
import org.springframework.data.geo.Box;
4949
import org.springframework.data.geo.Circle;
50+
import org.springframework.data.geo.Distance;
51+
import org.springframework.data.geo.GeoResults;
52+
import org.springframework.data.geo.Metrics;
5053
import org.springframework.data.geo.Point;
5154
import org.springframework.data.geo.Polygon;
5255
import org.springframework.data.mongodb.core.MongoOperations;
@@ -649,6 +652,13 @@ void findsPeopleByLocationWithinPolygon() {
649652
}
650653

651654

655+
@Test
656+
void testNearWithGeoResult() {
657+
658+
GeoResults<User> users = fragment.findByLocationCoordinatesNear(new Point(-73.99, 40.73), Distance.of(2000, Metrics.KILOMETERS));
659+
System.out.println("users: " + users);
660+
assertThat(users).isNotEmpty();
661+
}
652662

653663
private static void initUsers() {
654664

0 commit comments

Comments
 (0)