Skip to content

Commit d73807d

Browse files
Support ReadConcern and ReadPreference via NearQuery.
Implement ReadConcernAware and ReadPreferenceAware for NearQuery and make sure those get applied when working with the template API. Original Pull Request: #4288
1 parent e56f6ce commit d73807d

File tree

6 files changed

+188
-13
lines changed

6 files changed

+188
-13
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -948,10 +948,13 @@ public <T> GeoResults<T> geoNear(NearQuery near, Class<?> domainType, String col
948948
String distanceField = operations.nearQueryDistanceFieldName(domainType);
949949

950950
Builder optionsBuilder = AggregationOptions.builder().collation(near.getCollation());
951-
Query query = near.getQuery();
952951

953-
if (query != null && query.hasReadPreference()) {
954-
optionsBuilder.readPreference(query.getReadPreference());
952+
if (near.hasReadPreference()) {
953+
optionsBuilder.readPreference(near.getReadPreference());
954+
}
955+
956+
if(near.hasReadConcern()) {
957+
optionsBuilder.readConcern(near.getReadConcern());
955958
}
956959

957960
Aggregation $geoNear = TypedAggregation.newAggregation(domainType, Aggregation.geoNear(near, distanceField))

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import org.springframework.data.mongodb.core.aggregation.Aggregation;
8282
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
8383
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
84+
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.Builder;
8485
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
8586
import org.springframework.data.mongodb.core.aggregation.PrefixingDelegatingAggregationOperationContext;
8687
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
@@ -998,8 +999,19 @@ protected <T> Flux<GeoResult<T>> geoNear(NearQuery near, Class<?> entityClass, S
998999
GeoNearResultDocumentCallback<T> callback = new GeoNearResultDocumentCallback<>(distanceField,
9991000
new ProjectingReadCallback<>(mongoConverter, projection, collection), near.getMetric());
10001001

1002+
Builder optionsBuilder = AggregationOptions.builder();
1003+
if (near.hasReadPreference()) {
1004+
optionsBuilder.readPreference(near.getReadPreference());
1005+
}
1006+
1007+
if(near.hasReadConcern()) {
1008+
optionsBuilder.readConcern(near.getReadConcern());
1009+
}
1010+
1011+
optionsBuilder.collation(near.getCollation());
1012+
10011013
Aggregation $geoNear = TypedAggregation.newAggregation(entityClass, Aggregation.geoNear(near, distanceField))
1002-
.withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
1014+
.withOptions(optionsBuilder.build());
10031015

10041016
return aggregate($geoNear, collection, Document.class) //
10051017
.concatMap(callback::doWith);

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java

+76-6
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@
2424
import org.springframework.data.geo.Metric;
2525
import org.springframework.data.geo.Metrics;
2626
import org.springframework.data.geo.Point;
27+
import org.springframework.data.mongodb.core.ReadConcernAware;
28+
import org.springframework.data.mongodb.core.ReadPreferenceAware;
2729
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
2830
import org.springframework.lang.Nullable;
2931
import org.springframework.util.Assert;
3032
import org.springframework.util.ObjectUtils;
3133

34+
import com.mongodb.ReadConcern;
35+
import com.mongodb.ReadPreference;
36+
3237
/**
3338
* Builder class to build near-queries. <br />
3439
* MongoDB {@code $geoNear} operator allows usage of a {@literal GeoJSON Point} or legacy coordinate pair. Though
@@ -171,7 +176,7 @@
171176
* @author Christoph Strobl
172177
* @author Mark Paluch
173178
*/
174-
public final class NearQuery {
179+
public final class NearQuery implements ReadConcernAware, ReadPreferenceAware {
175180

176181
private final Point point;
177182
private @Nullable Query query;
@@ -181,6 +186,8 @@ public final class NearQuery {
181186
private boolean spherical;
182187
private @Nullable Long limit;
183188
private @Nullable Long skip;
189+
private @Nullable ReadConcern readConcern;
190+
private @Nullable ReadPreference readPreference;
184191

185192
/**
186193
* Creates a new {@link NearQuery}.
@@ -536,11 +543,6 @@ public NearQuery query(Query query) {
536543
return this;
537544
}
538545

539-
@Nullable
540-
public Query getQuery() {
541-
return query;
542-
}
543-
544546
/**
545547
* @return the number of elements to skip.
546548
*/
@@ -560,6 +562,74 @@ public Collation getCollation() {
560562
return query != null ? query.getCollation().orElse(null) : null;
561563
}
562564

565+
/**
566+
* Configures the query to use the given {@link ReadConcern} unless the underlying {@link #query(Query)}
567+
* {@link Query#hasReadConcern() specifies} another one.
568+
*
569+
* @param readConcern must not be {@literal null}.
570+
* @return this.
571+
* @since 4.1
572+
*/
573+
public NearQuery withReadConcern(ReadConcern readConcern) {
574+
575+
Assert.notNull(readConcern, "ReadConcern must not be null");
576+
this.readConcern = readConcern;
577+
return this;
578+
}
579+
580+
/**
581+
* Configures the query to use the given {@link ReadPreference} unless the underlying {@link #query(Query)}
582+
* {@link Query#hasReadPreference() specifies} another one.
583+
*
584+
* @param readPreference must not be {@literal null}.
585+
* @return this.
586+
* @since 4.1
587+
*/
588+
public NearQuery withReadPreference(ReadPreference readPreference) {
589+
590+
Assert.notNull(readPreference, "ReadPreference must not be null");
591+
this.readPreference = readPreference;
592+
return this;
593+
}
594+
595+
/**
596+
* Get the {@link ReadConcern} to use. Will return the underlying {@link #query(Query) queries}
597+
* {@link Query#getReadConcern() ReadConcern} if present or the one defined on the {@link NearQuery#readConcern}
598+
* itself.
599+
*
600+
* @return can be {@literal null} if none set.
601+
* @since 4.1
602+
* @see ReadConcernAware
603+
*/
604+
@Nullable
605+
@Override
606+
public ReadConcern getReadConcern() {
607+
608+
if (query != null && query.hasReadConcern()) {
609+
return query.getReadConcern();
610+
}
611+
return readConcern;
612+
}
613+
614+
/**
615+
* Get the {@link ReadPreference} to use. Will return the underlying {@link #query(Query) queries}
616+
* {@link Query#getReadPreference() ReadPreference} if present or the one defined on the
617+
* {@link NearQuery#readPreference} itself.
618+
*
619+
* @return can be {@literal null} if none set.
620+
* @since 4.1
621+
* @see ReadPreferenceAware
622+
*/
623+
@Nullable
624+
@Override
625+
public ReadPreference getReadPreference() {
626+
627+
if (query != null && query.hasReadPreference()) {
628+
return query.getReadPreference();
629+
}
630+
return readPreference;
631+
}
632+
563633
/**
564634
* Returns the {@link Document} built by the {@link NearQuery}.
565635
*

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -583,15 +583,24 @@ void geoNearShouldHonorReadPreferenceWhenSet() {
583583
void geoNearShouldHonorReadPreferenceFromQuery() {
584584

585585
NearQuery query = NearQuery.near(new Point(1, 1));
586+
query.withReadPreference(ReadPreference.secondary());
586587

587-
Query inner = new Query();
588-
inner.withReadPreference(ReadPreference.secondary());
589-
query.query(inner);
590588
template.geoNear(query, Wrapper.class);
591589

592590
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
593591
}
594592

593+
@Test // GH-4277
594+
void geoNearShouldHonorReadConcernFromQuery() {
595+
596+
NearQuery query = NearQuery.near(new Point(1, 1));
597+
query.withReadConcern(ReadConcern.SNAPSHOT);
598+
599+
template.geoNear(query, Wrapper.class);
600+
601+
verify(collection).withReadConcern(eq(ReadConcern.SNAPSHOT));
602+
}
603+
595604
@Test // DATAMONGO-1166, DATAMONGO-2264
596605
void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
597606

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java

+23
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.context.ApplicationListener;
6060
import org.springframework.context.support.StaticApplicationContext;
6161
import org.springframework.data.annotation.Id;
62+
import org.springframework.data.geo.Point;
6263
import org.springframework.data.mapping.MappingException;
6364
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
6465
import org.springframework.data.mapping.context.MappingContext;
@@ -384,6 +385,28 @@ void geoNearShouldUseCollationWhenPresent() {
384385
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("fr").build()));
385386
}
386387

388+
@Test // GH-4277
389+
void geoNearShouldHonorReadPreferenceFromQuery() {
390+
391+
NearQuery query = NearQuery.near(new Point(1, 1));
392+
query.withReadPreference(ReadPreference.secondary());
393+
394+
template.geoNear(query, Wrapper.class).subscribe();
395+
396+
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
397+
}
398+
399+
@Test // GH-4277
400+
void geoNearShouldHonorReadConcernFromQuery() {
401+
402+
NearQuery query = NearQuery.near(new Point(1, 1));
403+
query.withReadConcern(ReadConcern.SNAPSHOT);
404+
405+
template.geoNear(query, Wrapper.class).subscribe();
406+
407+
verify(collection).withReadConcern(eq(ReadConcern.SNAPSHOT));
408+
}
409+
387410
@Test // DATAMONGO-1719
388411
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
389412

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java

+58
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
import org.springframework.data.geo.Point;
3030
import org.springframework.data.mongodb.core.DocumentTestUtils;
3131
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
32+
import org.springframework.test.util.ReflectionTestUtils;
33+
34+
import com.mongodb.ReadConcern;
35+
import com.mongodb.ReadPreference;
3236

3337
/**
3438
* Unit tests for {@link NearQuery}.
@@ -229,4 +233,58 @@ public void shouldUseMilesForDistanceWhenMaxDistanceInKilometers() {
229233

230234
assertThat(query.toDocument()).containsEntry("maxDistance", 1000D).containsEntry("distanceMultiplier", 0.00062137D);
231235
}
236+
237+
@Test // GH-4277
238+
void fetchesReadPreferenceFromUnderlyingQueryObject() {
239+
240+
NearQuery nearQuery = NearQuery.near(new Point(0, 0))
241+
.query(new Query().withReadPreference(ReadPreference.nearest()));
242+
243+
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.nearest());
244+
}
245+
246+
@Test // GH-4277
247+
void fetchesReadConcernFromUnderlyingQueryObject() {
248+
249+
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).query(new Query().withReadConcern(ReadConcern.SNAPSHOT));
250+
251+
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.SNAPSHOT);
252+
}
253+
254+
@Test // GH-4277
255+
void usesReadPreferenceFromNearQueryIfUnderlyingQueryDoesNotDefineAny() {
256+
257+
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadPreference(ReadPreference.nearest())
258+
.query(new Query());
259+
260+
assertThat(((Query) ReflectionTestUtils.getField(nearQuery, "query")).getReadPreference()).isNull();
261+
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.nearest());
262+
}
263+
264+
@Test // GH-4277
265+
void usesReadConcernFromNearQueryIfUnderlyingQueryDoesNotDefineAny() {
266+
267+
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadConcern(ReadConcern.SNAPSHOT).query(new Query());
268+
269+
assertThat(((Query) ReflectionTestUtils.getField(nearQuery, "query")).getReadConcern()).isNull();
270+
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.SNAPSHOT);
271+
}
272+
273+
@Test // GH-4277
274+
void readPreferenceFromUnderlyingQueryOverridesNearQueryOne() {
275+
276+
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadPreference(ReadPreference.nearest())
277+
.query(new Query().withReadPreference(ReadPreference.primary()));
278+
279+
assertThat(nearQuery.getReadPreference()).isEqualTo(ReadPreference.primary());
280+
}
281+
282+
@Test // GH-4277
283+
void readConcernFromUnderlyingQueryOverridesNearQueryOne() {
284+
285+
NearQuery nearQuery = NearQuery.near(new Point(0, 0)).withReadConcern(ReadConcern.SNAPSHOT)
286+
.query(new Query().withReadConcern(ReadConcern.MAJORITY));
287+
288+
assertThat(nearQuery.getReadConcern()).isEqualTo(ReadConcern.MAJORITY);
289+
}
232290
}

0 commit comments

Comments
 (0)