Browse Source

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.
pull/66/head
Thomas Darimont 13 years ago committed by Oliver Gierke
parent
commit
8cb92de1ee
  1. 20
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
  2. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResults.java
  3. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java
  4. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  5. 48
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java
  6. 90
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

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

@ -566,8 +566,26 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -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");

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoResults.java

@ -131,7 +131,7 @@ public class GeoResults<T> implements Iterable<GeoResult<T>> { @@ -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;

37
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/NearQuery.java

@ -15,6 +15,7 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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}.
*

6
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

@ -38,6 +38,7 @@ import org.springframework.util.Assert; @@ -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 { @@ -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());
}

48
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/NearQueryUnitTests.java

@ -19,14 +19,18 @@ import static org.hamcrest.CoreMatchers.*; @@ -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 { @@ -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()));
}
}

90
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -590,4 +590,94 @@ public abstract class AbstractPersonRepositoryIntegrationTests { @@ -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));
}
}

Loading…
Cancel
Save