Browse Source

DATAMONGO-2059 - Replace usage of deprecated collection.count() with collection.countDocuments().

This commit switches from simple collection.count(), operating on potentially false collection statistic,  to countDocuments() using an aggregation for accurate results.
The transition required query modifications at some points because $match does not support $near and $nearSphere but require $geoWithin along with $center or $centerSphere which does not support $minDistance (see https://jira.mongodb.org/browse/SERVER-37043).
$geoWithin further more does not sort results by distance, but this fact can be ignored when just counting matches.

Examples:

{ location : { $near : [-73.99171, 40.738868], $maxDistance : 1.1 } }
{ location : { $geoWithin : { $center: [ [-73.99171, 40.738868], 1.1] } } }

{ location : { $near : [-73.99171, 40.738868], $minDistance : 0.1, $maxDistance : 1.1 } }
{$and :[ { $nor :[ { location :{ $geoWithin :{ $center :[ [-73.99171, 40.738868 ], 0.01] } } } ]}, { location :{ $geoWithin :{ $center :[ [-73.99171, 40.738868 ], 1.1] } } } ] }

Original pull request: #604.
pull/802/head
Christoph Strobl 6 years ago committed by Mark Paluch
parent
commit
909c51d00a
No known key found for this signature in database
GPG Key ID: 51A00FA751B91849
  1. 21
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 19
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
  3. 105
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  4. 4
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SessionAwareMethodInterceptorUnitTests.java
  5. 14
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
  6. 8
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java
  7. 4
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java
  8. 1
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java
  9. 2
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java
  10. 90
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
  11. 21
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java
  12. 16
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java
  13. 7
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java

21
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

@ -53,6 +53,7 @@ import org.springframework.data.geo.Distance; @@ -53,6 +53,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.callback.EntityCallbacks;
@ -1191,11 +1192,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -1191,11 +1192,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
LOGGER.debug("Executing count: {} in collection: {}", serializeToJsonSafely(filter), collectionName);
}
if (MongoDatabaseUtils.isTransactionActive(getMongoDbFactory())) {
return execute(collectionName, collection -> collection.countDocuments(filter, options));
}
return execute(collectionName, collection -> collection.count(filter, options));
return execute(collectionName, collection -> collection.countDocuments(QueryMapper.processCountFilter(filter), options));
}
/*
@ -3523,19 +3520,5 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, @@ -3523,19 +3520,5 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
// native MongoDB objects that offer methods with ClientSession must not be proxied.
return delegate.getDb();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.MongoTemplate#doCount(java.lang.String, org.bson.Document, com.mongodb.client.model.CountOptions)
*/
@Override
protected long doCount(String collectionName, Document filter, CountOptions options) {
if (!session.hasActiveTransaction()) {
return super.doCount(collectionName, filter, options);
}
return execute(collectionName, collection -> collection.countDocuments(filter, options));
}
}
}

19
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

@ -1300,9 +1300,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @@ -1300,9 +1300,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
*/
protected Mono<Long> doCount(String collectionName, Document filter, CountOptions options) {
return ReactiveMongoDatabaseUtils.isTransactionActive(mongoDatabaseFactory) //
.flatMap(txActive -> createMono(collectionName,
collection -> txActive ? collection.countDocuments(filter, options) : collection.count(filter, options)));
return createMono(collectionName,
collection -> collection.countDocuments(QueryMapper.processCountFilter(filter), options));
}
/*
@ -3323,20 +3322,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati @@ -3323,20 +3322,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
// native MongoDB objects that offer methods with ClientSession must not be proxied.
return delegate.getMongoDatabase();
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ReactiveMongoTemplate#count(java.lang.String, org.bson.Document, com.mongodb.client.model.CountOptions)
*/
@Override
public Mono<Long> doCount(String collectionName, Document filter, CountOptions options) {
if (!session.hasActiveTransaction()) {
return super.doCount(collectionName, filter, options);
}
return createMono(collectionName, collection -> collection.countDocuments(filter, options));
}
}
@RequiredArgsConstructor

105
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

@ -15,17 +15,8 @@ @@ -15,17 +15,8 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -36,6 +27,7 @@ import org.bson.types.ObjectId; @@ -36,6 +27,7 @@ import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Example;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity;
@ -1290,4 +1282,97 @@ public class QueryMapper { @@ -1290,4 +1282,97 @@ public class QueryMapper {
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
return mappingContext;
}
public static Document processCountFilter(Document source) {
Document target = new Document();
for (Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Document) {
Document theValue = (Document) entry.getValue();
if (containsNear(theValue)) {
target.putAll(createGeoWithin(entry.getKey(), theValue));
} else {
target.put(entry.getKey(), entry.getValue());
}
} else if (entry.getValue() instanceof Collection) {
Collection<Object> tmp = new ArrayList<>();
for (Object val : (Collection) entry.getValue()) {
if (val instanceof Document) {
tmp.add(processCountFilter((Document) val));
} else {
tmp.add(val);
}
}
target.put(entry.getKey(), tmp);
} else {
target.put(entry.getKey(), entry.getValue());
}
}
return target;
}
private static Document createGeoWithin(String key, Document source) {
boolean spheric = source.containsKey("$nearSphere");
Object $near = spheric ? source.get("$nearSphere") : source.get("$near");
Number maxDistance = source.containsKey("$maxDistance") ? (Number) source.get("$maxDistance") : Double.MAX_VALUE;
List<Object> $centerMax = Arrays.asList(toCenterCoordinates($near), maxDistance);
Document $geoWithinMax = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMax));
if (!containsNearWithMinDistance(source)) {
return new Document(key, $geoWithinMax);
}
Number minDistance = (Number) source.get("$minDistance");
List<Object> $centerMin = Arrays.asList(toCenterCoordinates($near), minDistance);
Document $geoWithinMin = new Document("$geoWithin",
new Document(spheric ? "$centerSphere" : "$center", $centerMin));
List<Document> criteria = new ArrayList<>();
criteria.add(new Document("$nor", Arrays.asList(new Document(key, $geoWithinMin))));
criteria.add(new Document(key, $geoWithinMax));
return new Document("$and", criteria);
}
private static boolean containsNear(Document source) {
if (source.containsKey("$near") || source.containsKey("$nearSphere")) {
return true;
}
return false;
}
private static boolean containsNearWithMinDistance(Document source) {
if (!containsNear(source)) {
return false;
}
return source.containsKey("$minDistance");
}
private static Object toCenterCoordinates(Object value) {
if (ObjectUtils.isArray(value)) {
return value;
}
if (value instanceof Point) {
return Arrays.asList(((Point) value).getX(), ((Point) value).getY());
}
if (value instanceof Document && ((Document) value).containsKey("x")) {
Document point = (Document) value;
return Arrays.asList(point.get("x"), point.get("y"));
}
return value;
}
}

4
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/SessionAwareMethodInterceptorUnitTests.java

@ -107,11 +107,11 @@ public class SessionAwareMethodInterceptorUnitTests { @@ -107,11 +107,11 @@ public class SessionAwareMethodInterceptorUnitTests {
public void usesCacheForMethodLookup() {
MethodCache cache = (MethodCache) ReflectionTestUtils.getField(SessionAwareMethodInterceptor.class, "METHOD_CACHE");
Method countMethod = ClassUtils.getMethod(MongoCollection.class, "count");
Method countMethod = ClassUtils.getMethod(MongoCollection.class, "countDocuments");
assertThat(cache.contains(countMethod, MongoCollection.class)).isFalse();
collection.count();
collection.countDocuments();
assertThat(cache.contains(countMethod, MongoCollection.class)).isTrue();
}

14
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java

@ -154,7 +154,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -154,7 +154,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
when(db.runCommand(any(), any(Class.class))).thenReturn(commandResultDocument);
when(collection.find(any(org.bson.Document.class), any(Class.class))).thenReturn(findIterable);
when(collection.mapReduce(any(), any(), eq(Document.class))).thenReturn(mapReduceIterable);
when(collection.count(any(Bson.class), any(CountOptions.class))).thenReturn(1L); // TODO: MongoDB 4 - fix me
when(collection.countDocuments(any(Bson.class), any(CountOptions.class))).thenReturn(1L); // TODO: MongoDB 4 - fix me
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
when(collection.withReadPreference(any())).thenReturn(collection);
@ -735,7 +735,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -735,7 +735,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build());
@ -926,7 +926,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -926,7 +926,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build());
@ -939,7 +939,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -939,7 +939,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").withHint(queryHint), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getHint()).isEqualTo(queryHint);
}
@ -1068,7 +1068,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -1068,7 +1068,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").skip(100), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getSkip()).isEqualTo(100);
}
@ -1079,7 +1079,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -1079,7 +1079,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").limit(10), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getLimit()).isEqualTo(10);
}
@ -1150,7 +1150,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @@ -1150,7 +1150,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.exists(new BasicQuery("{}"), Sith.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build());

8
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java

@ -126,7 +126,7 @@ public class ReactiveMongoTemplateUnitTests { @@ -126,7 +126,7 @@ public class ReactiveMongoTemplateUnitTests {
when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher);
when(collection.aggregate(anyList())).thenReturn(aggregatePublisher);
when(collection.aggregate(anyList(), any(Class.class))).thenReturn(aggregatePublisher);
when(collection.count(any(), any(CountOptions.class))).thenReturn(Mono.just(0L));
when(collection.countDocuments(any(), any(CountOptions.class))).thenReturn(Mono.just(0L));
when(collection.updateOne(any(), any(Bson.class), any(UpdateOptions.class))).thenReturn(updateResultPublisher);
when(collection.updateMany(any(Bson.class), any(Bson.class), any())).thenReturn(updateResultPublisher);
when(collection.findOneAndUpdate(any(), any(Bson.class), any(FindOneAndUpdateOptions.class)))
@ -391,7 +391,7 @@ public class ReactiveMongoTemplateUnitTests { @@ -391,7 +391,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().skip(10), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getSkip()).isEqualTo(10);
}
@ -402,7 +402,7 @@ public class ReactiveMongoTemplateUnitTests { @@ -402,7 +402,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().limit(100), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getLimit()).isEqualTo(100);
}
@ -414,7 +414,7 @@ public class ReactiveMongoTemplateUnitTests { @@ -414,7 +414,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().withHint(queryHint), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture());
verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getHint()).isEqualTo(queryHint);
}

4
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveSessionBoundMongoTemplateUnitTests.java

@ -111,7 +111,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests { @@ -111,7 +111,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests {
when(collection.deleteMany(any(ClientSession.class), any(), any())).thenReturn(resultPublisher);
when(collection.insertOne(any(ClientSession.class), any(Document.class))).thenReturn(resultPublisher);
when(collection.aggregate(any(ClientSession.class), anyList(), any(Class.class))).thenReturn(aggregatePublisher);
when(collection.count(any(ClientSession.class), any(), any(CountOptions.class))).thenReturn(resultPublisher);
when(collection.countDocuments(any(ClientSession.class), any(), any(CountOptions.class))).thenReturn(resultPublisher);
when(collection.drop(any(ClientSession.class))).thenReturn(resultPublisher);
when(collection.findOneAndUpdate(any(ClientSession.class), any(), any(Bson.class), any()))
.thenReturn(resultPublisher);
@ -224,7 +224,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests { @@ -224,7 +224,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests {
template.count(new Query(), Person.class).subscribe();
verify(collection).count(eq(clientSession), any(), any(CountOptions.class));
verify(collection).countDocuments(eq(clientSession), any(), any(CountOptions.class));
}
@Test // DATAMONGO-1880

1
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateTests.java

@ -284,7 +284,6 @@ public class SessionBoundMongoTemplateTests { @@ -284,7 +284,6 @@ public class SessionBoundMongoTemplateTests {
}
@Test // DATAMONGO-2012
@Ignore("error 2 (BadValue): $match does not support $geoNear, $near, and $nearSphere")
public void countWithGeoInTransaction() {
if (!template.collectionExists(Person.class)) {

2
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SessionBoundMongoTemplateUnitTests.java

@ -226,7 +226,7 @@ public class SessionBoundMongoTemplateUnitTests { @@ -226,7 +226,7 @@ public class SessionBoundMongoTemplateUnitTests {
template.count(new Query(), Person.class);
verify(collection).count(eq(clientSession), any(), any(CountOptions.class));
verify(collection).countDocuments(eq(clientSession), any(), any(CountOptions.class));
}
@Test // DATAMONGO-1880

90
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

@ -890,6 +890,96 @@ public class QueryMapperUnitTests { @@ -890,6 +890,96 @@ public class QueryMapperUnitTests {
assertThat(target).isEqualTo(new org.bson.Document("_id", "id-1"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithoutDistance() {
Query source = query(where("location").near(new Point(-73.99171, 40.738868)));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document
.parse("{\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 1.7976931348623157E308]}}}"));
}
@Test // DATAMONGO-2059
public void nearSphereToGeoWithinWithoutDistance() {
Query source = query(where("location").nearSphere(new Point(-73.99171, 40.738868)));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document.parse(
"{\"location\": {\"$geoWithin\": {\"$centerSphere\": [[-73.99171, 40.738868], 1.7976931348623157E308]}}}"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithMaxDistance() {
Query source = query(where("location").near(new Point(-73.99171, 40.738868)).maxDistance(10));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(
org.bson.Document.parse("{\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}}"));
}
@Test // DATAMONGO-2059
public void nearSphereToGeoWithinWithMaxDistance() {
Query source = query(where("location").nearSphere(new Point(-73.99171, 40.738868)).maxDistance(10));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document
.parse("{\"location\": {\"$geoWithin\": {\"$centerSphere\": [[-73.99171, 40.738868], 10.0]}}}"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithMinDistance() {
Query source = query(where("location").near(new Point(-73.99171, 40.738868)).minDistance(0.01));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document.parse(
"{\"$and\":[{\"$nor\":[{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 0.01]}}}]},"
+ " {\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 1.7976931348623157E308]}}}]}"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithMaxDistanceAndCombinedWithOtherCriteria() {
Query source = query(
where("name").is("food").and("location").near(new Point(-73.99171, 40.738868)).maxDistance(10));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document
.parse("{\"name\": \"food\", \"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}}"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithMinDistanceOrCombinedWithOtherCriteria() {
Query source = query(new Criteria().orOperator(where("name").is("food"),
where("location").near(new Point(-73.99171, 40.738868)).minDistance(0.01)));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document.parse(
"{\"$or\" : [ { \"name\": \"food\" }, {\"$and\":[{\"$nor\":[{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 0.01]}}}]},{\"location\":{\"$geoWithin\":{\"$center\":[ [ -73.99171, 40.738868 ], 1.7976931348623157E308]}}}]} ]}"));
}
@Test // DATAMONGO-2059
public void nearToGeoWithinWithMaxDistanceOrCombinedWithOtherCriteria() {
Query source = query(new Criteria().orOperator(where("name").is("food"),
where("location").near(new Point(-73.99171, 40.738868)).maxDistance(10)));
org.bson.Document target = postProcessQueryForCount(source);
assertThat(target).isEqualTo(org.bson.Document.parse(
"{\"$or\" : [ { \"name\": \"food\" }, {\"location\": {\"$geoWithin\": {\"$center\": [[-73.99171, 40.738868], 10.0]}}} ]}"));
}
private org.bson.Document postProcessQueryForCount(Query source) {
org.bson.Document intermediate = mapper.getMappedObject(source.getQueryObject(), (MongoPersistentEntity<?>) null);
return QueryMapper.processCountFilter(intermediate);
}
@Document
public class Foo {
@Id private ObjectId id;

21
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java

@ -120,24 +120,33 @@ public abstract class AbstractGeoSpatialTests { @@ -120,24 +120,33 @@ public abstract class AbstractGeoSpatialTests {
public void withinCenter() {
Circle circle = new Circle(-73.99171, 40.738868, 0.01);
List<Venue> venues = template.find(query(where("location").within(circle)), Venue.class);
Query query = query(where("location").within(circle));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(7);
assertThat(template.count(query, Venue.class)).isEqualTo(7);
}
@Test
public void withinCenterSphere() {
Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784);
List<Venue> venues = template.find(query(where("location").withinSphere(circle)), Venue.class);
Query query = query(where("location").withinSphere(circle));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(11);
assertThat(template.count(query, Venue.class)).isEqualTo(11);
}
@Test
public void withinBox() {
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404));
List<Venue> venues = template.find(query(where("location").within(box)), Venue.class);
Query query = query(where("location").within(box));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(4);
assertThat(template.count(query, Venue.class)).isEqualTo(4);
}
@Test
@ -150,8 +159,10 @@ public abstract class AbstractGeoSpatialTests { @@ -150,8 +159,10 @@ public abstract class AbstractGeoSpatialTests {
Polygon polygon = new Polygon(first, second, third, fourth);
List<Venue> venues = template.find(query(where("location").within(polygon)), Venue.class);
Query query = query(where("location").within(polygon));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(4);
assertThat(template.count(query, Venue.class)).isEqualTo(4);
}
@Test
@ -159,8 +170,10 @@ public abstract class AbstractGeoSpatialTests { @@ -159,8 +170,10 @@ public abstract class AbstractGeoSpatialTests {
Point point = new Point(-73.99171, 40.738868);
Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(11);
assertThat(template.count(query, Venue.class)).isEqualTo(11);
}
@Test // DATAMONGO-1360

16
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java

@ -22,6 +22,7 @@ import static org.springframework.data.mongodb.core.query.Query.*; @@ -22,6 +22,7 @@ import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.domain.Sort.Direction;
@ -36,6 +37,7 @@ import org.springframework.data.mongodb.core.index.IndexField; @@ -36,6 +37,7 @@ import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
/**
* @author Christoph Strobl
@ -72,11 +74,23 @@ public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests { @@ -72,11 +74,23 @@ public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests {
@Test // DATAMONGO-1110
public void nearSphereWithMinDistance() {
Point point = new Point(-73.99171, 40.738868);
List<Venue> venues = template.find(query(where("location").nearSphere(point).minDistance(0.01)), Venue.class);
Query query = query(where("location").nearSphere(point).minDistance(0.01));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues.size()).isEqualTo(1);
}
@Test
public void countNearSphereWithMinDistance() {
Point point = new Point(-73.99171, 40.738868);
Query query = query(where("location").nearSphere(point).minDistance(0.01));
assertThat(template.count(query, Venue.class)).isEqualTo(1);
}
@Override
protected void createIndex() {
template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));

7
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java

@ -32,6 +32,7 @@ import org.springframework.data.mongodb.core.index.GeospatialIndex; @@ -32,6 +32,7 @@ import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.query.Query;
/**
* Modified from https://github.com/deftlabs/mongo-java-geospatial-example
@ -45,9 +46,13 @@ public class GeoSpatial2DTests extends AbstractGeoSpatialTests { @@ -45,9 +46,13 @@ public class GeoSpatial2DTests extends AbstractGeoSpatialTests {
@Test
public void nearPoint() {
Point point = new Point(-73.99171, 40.738868);
List<Venue> venues = template.find(query(where("location").near(point).maxDistance(0.01)), Venue.class);
Query query = query(where("location").near(point).maxDistance(0.01));
List<Venue> venues = template.find(query, Venue.class);
assertThat(venues.size()).isEqualTo(7);
assertThat(template.count(query, Venue.class)).isEqualTo(7);
}
@Test // DATAMONGO-360

Loading…
Cancel
Save