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.
This commit is contained in:
committed by
Mark Paluch
parent
4a04e82e86
commit
909c51d00a
@@ -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,
|
||||
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,
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 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
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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());
|
||||
|
||||
@@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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 {
|
||||
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
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 {
|
||||
|
||||
@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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@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
|
||||
|
||||
Reference in New Issue
Block a user