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;
import org.springframework.data.geo.GeoResult; import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric; import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.PropertyPath; import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.data.mapping.PropertyReferenceException;
import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.mapping.callback.EntityCallbacks;
@ -1191,11 +1192,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
LOGGER.debug("Executing count: {} in collection: {}", serializeToJsonSafely(filter), collectionName); LOGGER.debug("Executing count: {} in collection: {}", serializeToJsonSafely(filter), collectionName);
} }
if (MongoDatabaseUtils.isTransactionActive(getMongoDbFactory())) { return execute(collectionName, collection -> collection.countDocuments(QueryMapper.processCountFilter(filter), options));
return execute(collectionName, collection -> collection.countDocuments(filter, options));
}
return execute(collectionName, collection -> collection.count(filter, options));
} }
/* /*
@ -3523,19 +3520,5 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
// native MongoDB objects that offer methods with ClientSession must not be proxied. // native MongoDB objects that offer methods with ClientSession must not be proxied.
return delegate.getDb(); 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
*/ */
protected Mono<Long> doCount(String collectionName, Document filter, CountOptions options) { protected Mono<Long> doCount(String collectionName, Document filter, CountOptions options) {
return ReactiveMongoDatabaseUtils.isTransactionActive(mongoDatabaseFactory) // return createMono(collectionName,
.flatMap(txActive -> createMono(collectionName, collection -> collection.countDocuments(QueryMapper.processCountFilter(filter), options));
collection -> txActive ? collection.countDocuments(filter, options) : collection.count(filter, options)));
} }
/* /*
@ -3323,20 +3322,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
// native MongoDB objects that offer methods with ClientSession must not be proxied. // native MongoDB objects that offer methods with ClientSession must not be proxied.
return delegate.getMongoDatabase(); 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 @RequiredArgsConstructor

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

@ -15,17 +15,8 @@
*/ */
package org.springframework.data.mongodb.core.convert; package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList; import java.util.*;
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.Map.Entry; import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -36,6 +27,7 @@ import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Example; import org.springframework.data.domain.Example;
import org.springframework.data.geo.Point;
import org.springframework.data.mapping.Association; import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntity;
@ -1290,4 +1282,97 @@ public class QueryMapper {
public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() { public MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
return mappingContext; 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 {
public void usesCacheForMethodLookup() { public void usesCacheForMethodLookup() {
MethodCache cache = (MethodCache) ReflectionTestUtils.getField(SessionAwareMethodInterceptor.class, "METHOD_CACHE"); 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(); assertThat(cache.contains(countMethod, MongoCollection.class)).isFalse();
collection.count(); collection.countDocuments();
assertThat(cache.contains(countMethod, MongoCollection.class)).isTrue(); 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 {
when(db.runCommand(any(), any(Class.class))).thenReturn(commandResultDocument); when(db.runCommand(any(), any(Class.class))).thenReturn(commandResultDocument);
when(collection.find(any(org.bson.Document.class), any(Class.class))).thenReturn(findIterable); 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.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.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable); when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
when(collection.withReadPreference(any())).thenReturn(collection); when(collection.withReadPreference(any())).thenReturn(collection);
@ -735,7 +735,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); template.exists(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class); ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture()); verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation()) assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build());
@ -926,7 +926,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class); template.count(new BasicQuery("{}").collation(Collation.of("fr")), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class); ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture()); verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation()) assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build()); .isEqualTo(com.mongodb.client.model.Collation.builder().locale("fr").build());
@ -939,7 +939,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").withHint(queryHint), AutogenerateableId.class); template.count(new BasicQuery("{}").withHint(queryHint), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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); assertThat(options.getValue().getHint()).isEqualTo(queryHint);
} }
@ -1068,7 +1068,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").skip(100), AutogenerateableId.class); template.count(new BasicQuery("{}").skip(100), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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); assertThat(options.getValue().getSkip()).isEqualTo(100);
} }
@ -1079,7 +1079,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.count(new BasicQuery("{}").limit(10), AutogenerateableId.class); template.count(new BasicQuery("{}").limit(10), AutogenerateableId.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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); assertThat(options.getValue().getLimit()).isEqualTo(10);
} }
@ -1150,7 +1150,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
template.exists(new BasicQuery("{}"), Sith.class); template.exists(new BasicQuery("{}"), Sith.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class); ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).count(any(), options.capture()); verify(collection).countDocuments(any(), options.capture());
assertThat(options.getValue().getCollation()) assertThat(options.getValue().getCollation())
.isEqualTo(com.mongodb.client.model.Collation.builder().locale("de_AT").build()); .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 {
when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher); when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher);
when(collection.aggregate(anyList())).thenReturn(aggregatePublisher); when(collection.aggregate(anyList())).thenReturn(aggregatePublisher);
when(collection.aggregate(anyList(), any(Class.class))).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.updateOne(any(), any(Bson.class), any(UpdateOptions.class))).thenReturn(updateResultPublisher);
when(collection.updateMany(any(Bson.class), any(Bson.class), any())).thenReturn(updateResultPublisher); when(collection.updateMany(any(Bson.class), any(Bson.class), any())).thenReturn(updateResultPublisher);
when(collection.findOneAndUpdate(any(), any(Bson.class), any(FindOneAndUpdateOptions.class))) when(collection.findOneAndUpdate(any(), any(Bson.class), any(FindOneAndUpdateOptions.class)))
@ -391,7 +391,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().skip(10), Person.class, "star-wars").subscribe(); template.count(new Query().skip(10), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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(10); assertThat(options.getValue().getSkip()).isEqualTo(10);
} }
@ -402,7 +402,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().limit(100), Person.class, "star-wars").subscribe(); template.count(new Query().limit(100), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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(100); assertThat(options.getValue().getLimit()).isEqualTo(100);
} }
@ -414,7 +414,7 @@ public class ReactiveMongoTemplateUnitTests {
template.count(new Query().withHint(queryHint), Person.class, "star-wars").subscribe(); template.count(new Query().withHint(queryHint), Person.class, "star-wars").subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.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); 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 {
when(collection.deleteMany(any(ClientSession.class), any(), any())).thenReturn(resultPublisher); when(collection.deleteMany(any(ClientSession.class), any(), any())).thenReturn(resultPublisher);
when(collection.insertOne(any(ClientSession.class), any(Document.class))).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.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.drop(any(ClientSession.class))).thenReturn(resultPublisher);
when(collection.findOneAndUpdate(any(ClientSession.class), any(), any(Bson.class), any())) when(collection.findOneAndUpdate(any(ClientSession.class), any(), any(Bson.class), any()))
.thenReturn(resultPublisher); .thenReturn(resultPublisher);
@ -224,7 +224,7 @@ public class ReactiveSessionBoundMongoTemplateUnitTests {
template.count(new Query(), Person.class).subscribe(); 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 @Test // DATAMONGO-1880

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

@ -284,7 +284,6 @@ public class SessionBoundMongoTemplateTests {
} }
@Test // DATAMONGO-2012 @Test // DATAMONGO-2012
@Ignore("error 2 (BadValue): $match does not support $geoNear, $near, and $nearSphere")
public void countWithGeoInTransaction() { public void countWithGeoInTransaction() {
if (!template.collectionExists(Person.class)) { 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 {
template.count(new Query(), Person.class); 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 @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 {
assertThat(target).isEqualTo(new org.bson.Document("_id", "id-1")); 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 @Document
public class Foo { public class Foo {
@Id private ObjectId id; @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 {
public void withinCenter() { public void withinCenter() {
Circle circle = new Circle(-73.99171, 40.738868, 0.01); 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(venues).hasSize(7);
assertThat(template.count(query, Venue.class)).isEqualTo(7);
} }
@Test @Test
public void withinCenterSphere() { public void withinCenterSphere() {
Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); 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(venues).hasSize(11);
assertThat(template.count(query, Venue.class)).isEqualTo(11);
} }
@Test @Test
public void withinBox() { public void withinBox() {
Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); 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(venues).hasSize(4);
assertThat(template.count(query, Venue.class)).isEqualTo(4);
} }
@Test @Test
@ -150,8 +159,10 @@ public abstract class AbstractGeoSpatialTests {
Polygon polygon = new Polygon(first, second, third, fourth); 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(venues).hasSize(4);
assertThat(template.count(query, Venue.class)).isEqualTo(4);
} }
@Test @Test
@ -159,8 +170,10 @@ public abstract class AbstractGeoSpatialTests {
Point point = new Point(-73.99171, 40.738868); Point point = new Point(-73.99171, 40.738868);
Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784)); Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784));
List<Venue> venues = template.find(query, Venue.class); List<Venue> venues = template.find(query, Venue.class);
assertThat(venues).hasSize(11); assertThat(venues).hasSize(11);
assertThat(template.count(query, Venue.class)).isEqualTo(11);
} }
@Test // DATAMONGO-1360 @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.*;
import java.util.List; import java.util.List;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
@ -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.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
@ -72,11 +74,23 @@ public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests {
@Test // DATAMONGO-1110 @Test // DATAMONGO-1110
public void nearSphereWithMinDistance() { public void nearSphereWithMinDistance() {
Point point = new Point(-73.99171, 40.738868); 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); 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 @Override
protected void createIndex() { protected void createIndex() {
template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); 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;
import org.springframework.data.mongodb.core.index.IndexField; import org.springframework.data.mongodb.core.index.IndexField;
import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations; 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 * Modified from https://github.com/deftlabs/mongo-java-geospatial-example
@ -45,9 +46,13 @@ public class GeoSpatial2DTests extends AbstractGeoSpatialTests {
@Test @Test
public void nearPoint() { public void nearPoint() {
Point point = new Point(-73.99171, 40.738868); 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(venues.size()).isEqualTo(7);
assertThat(template.count(query, Venue.class)).isEqualTo(7);
} }
@Test // DATAMONGO-360 @Test // DATAMONGO-360

Loading…
Cancel
Save