DATAMONGO-445 - Allow to skip unnecessary elements in NearQuery.
Added support for skipping elements for NearQuery in MongoTemplate. As mongodb currently (2.4.4) doesn't support he skipping of elements in geoNear-Queries we skip the unnecessary elements ourselves. We use the limit & skip information from the given query or an explicitly passed Pageable. Original pull request: #64.
This commit is contained in:
committed by
Oliver Gierke
parent
5d3cc3fa04
commit
8cb92de1ee
@@ -566,8 +566,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware {
|
||||
mongoConverter, entityClass), near.getMetric());
|
||||
List<GeoResult<T>> result = new ArrayList<GeoResult<T>>(results.size());
|
||||
|
||||
int index = 0;
|
||||
int elementsToSkip = near.getSkip() != null ? near.getSkip() : 0;
|
||||
|
||||
for (Object element : results) {
|
||||
result.add(callback.doWith((DBObject) element));
|
||||
|
||||
/*
|
||||
* As MongoDB currently (2.4.4) doesn't support the skipping of elements in near queries
|
||||
* we skip the elements ourselves to avoid at least the document 2 object mapping overhead.
|
||||
*
|
||||
* @see https://jira.mongodb.org/browse/SERVER-3925
|
||||
*/
|
||||
if (index >= elementsToSkip) {
|
||||
result.add(callback.doWith((DBObject) element));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if (elementsToSkip > 0) {
|
||||
// as we skipped some elements we have to calculate the averageDistance ourselves:
|
||||
return new GeoResults<T>(result, near.getMetric());
|
||||
}
|
||||
|
||||
DBObject stats = (DBObject) commandResult.get("stats");
|
||||
|
||||
@@ -131,7 +131,7 @@ public class GeoResults<T> implements Iterable<GeoResult<T>> {
|
||||
private static Distance calculateAverageDistance(List<? extends GeoResult<?>> results, Metric metric) {
|
||||
|
||||
if (results.isEmpty()) {
|
||||
return new Distance(0, null);
|
||||
return new Distance(0, metric);
|
||||
}
|
||||
|
||||
double averageDistance = 0;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mongodb.core.geo.CustomMetric;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Metric;
|
||||
@@ -29,6 +30,7 @@ import com.mongodb.DBObject;
|
||||
* Builder class to build near-queries.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class NearQuery {
|
||||
|
||||
@@ -38,6 +40,7 @@ public class NearQuery {
|
||||
private Metric metric;
|
||||
private boolean spherical;
|
||||
private Integer num;
|
||||
private Integer skip;
|
||||
|
||||
/**
|
||||
* Creates a new {@link NearQuery}.
|
||||
@@ -116,7 +119,7 @@ public class NearQuery {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the number of results to return.
|
||||
* Configures the maximum number of results to return.
|
||||
*
|
||||
* @param num
|
||||
* @return
|
||||
@@ -126,6 +129,29 @@ public class NearQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the number of results to skip.
|
||||
*
|
||||
* @param skip
|
||||
* @return
|
||||
*/
|
||||
public NearQuery skip(int skip) {
|
||||
this.skip = skip;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link Pageable} to use.
|
||||
*
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
public NearQuery with(Pageable pageable) {
|
||||
this.num = pageable.getOffset() + pageable.getPageSize();
|
||||
this.skip = pageable.getOffset();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max distance results shall have from the configured origin. If a {@link Metric} was set before the given
|
||||
* value will be interpreted as being a value in that metric. E.g.
|
||||
@@ -290,9 +316,18 @@ public class NearQuery {
|
||||
*/
|
||||
public NearQuery query(Query query) {
|
||||
this.query = query;
|
||||
this.skip = query.getSkip();
|
||||
this.num = query.getLimit();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of elements to skip.
|
||||
*/
|
||||
public Integer getSkip() {
|
||||
return skip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link DBObject} built by the {@link NearQuery}.
|
||||
*
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.util.Assert;
|
||||
* Base class for {@link RepositoryQuery} implementations for Mongo.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
|
||||
@@ -287,6 +288,11 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
|
||||
nearQuery.maxDistance(maxDistance).in(maxDistance.getMetric());
|
||||
}
|
||||
|
||||
Pageable pageable = accessor.getPageable();
|
||||
if (pageable != null) {
|
||||
nearQuery.with(pageable);
|
||||
}
|
||||
|
||||
MongoEntityMetadata<?> metadata = method.getEntityInformation();
|
||||
return (GeoResults<Object>) operations.geoNear(nearQuery, metadata.getJavaType(), metadata.getCollectionName());
|
||||
}
|
||||
|
||||
@@ -19,14 +19,18 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.mongodb.core.geo.Distance;
|
||||
import org.springframework.data.mongodb.core.geo.Metric;
|
||||
import org.springframework.data.mongodb.core.geo.Metrics;
|
||||
import org.springframework.data.mongodb.core.geo.Point;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link NearQuery}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class NearQueryUnitTests {
|
||||
|
||||
@@ -75,4 +79,48 @@ public class NearQueryUnitTests {
|
||||
query = query.maxDistance(new Distance(200, Metrics.KILOMETERS));
|
||||
assertThat(query.getMetric(), is((Metric) Metrics.MILES));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void shouldTakeSkipAndLimitSettingsFromGivenPageable() {
|
||||
|
||||
Pageable pageable = new PageRequest(3, 5);
|
||||
NearQuery query = NearQuery.near(new Point(1, 1)).with(pageable);
|
||||
|
||||
assertThat(query.getSkip(), is(pageable.getPageNumber() * pageable.getPageSize()));
|
||||
assertThat((Integer) query.toDBObject().get("num"), is((pageable.getPageNumber() + 1) * pageable.getPageSize()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void shouldTakeSkipAndLimitSettingsFromGivenQuery() {
|
||||
|
||||
int limit = 10;
|
||||
int skip = 5;
|
||||
NearQuery query = NearQuery.near(new Point(1, 1)).query(
|
||||
Query.query(Criteria.where("foo").is("bar")).limit(limit).skip(skip));
|
||||
|
||||
assertThat(query.getSkip(), is(skip));
|
||||
assertThat((Integer) query.toDBObject().get("num"), is(limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void shouldTakeSkipAndLimitSettingsFromPageableEvenIfItWasSpecifiedOnQuery() {
|
||||
|
||||
int limit = 10;
|
||||
int skip = 5;
|
||||
Pageable pageable = new PageRequest(3, 5);
|
||||
NearQuery query = NearQuery.near(new Point(1, 1))
|
||||
.query(Query.query(Criteria.where("foo").is("bar")).limit(limit).skip(skip)).with(pageable);
|
||||
|
||||
assertThat(query.getSkip(), is(pageable.getPageNumber() * pageable.getPageSize()));
|
||||
assertThat((Integer) query.toDBObject().get("num"), is((pageable.getPageNumber() + 1) * pageable.getPageSize()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,4 +590,94 @@ public abstract class AbstractPersonRepositoryIntegrationTests {
|
||||
assertThat(result, hasSize(2));
|
||||
assertThat(result, hasItems(dave, oliver));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void executesGeoPageQueryForWithPageRequestForPageInBetween() {
|
||||
|
||||
Point farAway = new Point(-73.9, 40.7);
|
||||
Point here = new Point(-73.99, 40.73);
|
||||
|
||||
dave.setLocation(farAway);
|
||||
oliver.setLocation(here);
|
||||
carter.setLocation(here);
|
||||
boyd.setLocation(here);
|
||||
leroi.setLocation(here);
|
||||
|
||||
repository.save(Arrays.asList(dave, oliver, carter, boyd, leroi));
|
||||
|
||||
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
|
||||
Metrics.KILOMETERS), new PageRequest(1, 2));
|
||||
|
||||
assertThat(results.getContent().isEmpty(), is(false));
|
||||
assertThat(results.getNumberOfElements(), is(2));
|
||||
assertThat(results.isFirstPage(), is(false));
|
||||
assertThat(results.isLastPage(), is(false));
|
||||
assertThat(results.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
|
||||
assertThat(results.getAverageDistance().getNormalizedValue(), is(0.0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void executesGeoPageQueryForWithPageRequestForPageAtTheEnd() {
|
||||
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
|
||||
dave.setLocation(point);
|
||||
oliver.setLocation(point);
|
||||
carter.setLocation(point);
|
||||
|
||||
repository.save(Arrays.asList(dave, oliver, carter));
|
||||
|
||||
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
|
||||
Metrics.KILOMETERS), new PageRequest(1, 2));
|
||||
assertThat(results.getContent().isEmpty(), is(false));
|
||||
assertThat(results.getNumberOfElements(), is(1));
|
||||
assertThat(results.isFirstPage(), is(false));
|
||||
assertThat(results.isLastPage(), is(true));
|
||||
assertThat(results.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void executesGeoPageQueryForWithPageRequestForJustOneElement() {
|
||||
|
||||
Point point = new Point(-73.99171, 40.738868);
|
||||
dave.setLocation(point);
|
||||
repository.save(dave);
|
||||
|
||||
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
|
||||
Metrics.KILOMETERS), new PageRequest(0, 2));
|
||||
|
||||
assertThat(results.getContent().isEmpty(), is(false));
|
||||
assertThat(results.getNumberOfElements(), is(1));
|
||||
assertThat(results.isFirstPage(), is(true));
|
||||
assertThat(results.isLastPage(), is(true));
|
||||
assertThat(results.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-445
|
||||
*/
|
||||
@Test
|
||||
public void executesGeoPageQueryForWithPageRequestForJustOneElementEmptyPage() {
|
||||
|
||||
dave.setLocation(new Point(-73.99171, 40.738868));
|
||||
repository.save(dave);
|
||||
|
||||
GeoPage<Person> results = repository.findByLocationNear(new Point(-73.99, 40.73), new Distance(2000,
|
||||
Metrics.KILOMETERS), new PageRequest(1, 2));
|
||||
|
||||
assertThat(results.getContent().isEmpty(), is(true));
|
||||
assertThat(results.getNumberOfElements(), is(0));
|
||||
assertThat(results.isFirstPage(), is(false));
|
||||
assertThat(results.isLastPage(), is(true));
|
||||
assertThat(results.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user