Skip to content

Commit

Permalink
[Geo API 644] API for $geoWithin queries, for querying within a Mult…
Browse files Browse the repository at this point in the history
…i-Polygon.
  • Loading branch information
trishagee committed Mar 15, 2015
1 parent ff57b35 commit 42cf5c2
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 3 deletions.
14 changes: 14 additions & 0 deletions morphia/src/main/java/org/mongodb/morphia/query/FieldEnd.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.mongodb.morphia.query;


import org.mongodb.morphia.geo.MultiPolygon;
import org.mongodb.morphia.geo.Point;
import org.mongodb.morphia.geo.Polygon;

Expand Down Expand Up @@ -104,4 +105,17 @@ public interface FieldEnd<T> {
* @return T
*/
T within(Polygon boundary);

/**
* This runs the $geoWithin query, returning documents with GeoJson fields
* whose area falls within the given boundaries. When determining
* inclusion, MongoDB considers the border of a shape to be part of the
* shape, subject to the precision of floating point numbers.
*
* These queries are only compatible with MongoDB 2.4 or greater.
*
* @param boundaries a multi-polygon describing the areas to search within.
* @return T
*/
T within(MultiPolygon boundaries);
}
14 changes: 11 additions & 3 deletions morphia/src/main/java/org/mongodb/morphia/query/FieldEndImpl.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.mongodb.morphia.query;


import org.mongodb.morphia.geo.MultiPolygon;
import org.mongodb.morphia.geo.Point;
import org.mongodb.morphia.geo.Polygon;
import org.mongodb.morphia.logging.Logger;
Expand All @@ -11,6 +12,8 @@
import java.util.Map;
import java.util.regex.Pattern;

import static org.mongodb.morphia.query.FilterOperator.GEO_WITHIN;


public class FieldEndImpl<T extends CriteriaContainerImpl> implements FieldEnd<T> {
private static final Logger LOG = MorphiaLoggerFactory.get(FieldEndImpl.class);
Expand Down Expand Up @@ -101,13 +104,18 @@ public T equal(final Object val) {

public T within(final Shape shape) {
Assert.parametersNotNull("shape", shape);
return addCriteria(FilterOperator.GEO_WITHIN, shape.toDBObject());
return addCriteria(GEO_WITHIN, shape.toDBObject());
}

@Override
public T within(final Polygon boundary) {
target.add(new StandardGeoFieldCriteria(query, field, FilterOperator
.GEO_WITHIN, boundary, null, validateName, false));
target.add(new StandardGeoFieldCriteria(query, field, GEO_WITHIN, boundary, null, validateName, false));
return target;
}

@Override
public T within(final MultiPolygon boundaries) {
target.add(new StandardGeoFieldCriteria(query, field, GEO_WITHIN, boundaries, null, validateName, false));
return target;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
package org.mongodb.morphia.query;

import org.junit.Before;
import org.junit.Test;
import org.mongodb.morphia.TestBase;
import org.mongodb.morphia.geo.AllTheThings;
import org.mongodb.morphia.geo.Area;
import org.mongodb.morphia.geo.City;
import org.mongodb.morphia.geo.MultiPolygon;
import org.mongodb.morphia.geo.Polygon;
import org.mongodb.morphia.geo.Regions;
import org.mongodb.morphia.geo.Route;

import java.util.List;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
import static org.mongodb.morphia.geo.GeoJson.geometryCollection;
import static org.mongodb.morphia.geo.GeoJson.lineString;
import static org.mongodb.morphia.geo.GeoJson.multiPoint;
import static org.mongodb.morphia.geo.GeoJson.multiPolygon;
import static org.mongodb.morphia.geo.GeoJson.point;
import static org.mongodb.morphia.geo.GeoJson.polygon;
import static org.mongodb.morphia.geo.PointBuilder.pointBuilder;

public class GeoWithinQueriesWithMultiPolygonTest extends TestBase {
private final Polygon uk = polygon(point(49.78, -10.5),
point(49.78, 1.78),
point(59, 1.78),
point(59, -10.5),
point(49.78, -10.5));
private final Polygon spain = polygon(point(43.40, -10.24),
point(43.40, 3.19),
point(35.45, 3.19),
point(35.45, -10.24),
point(43.40, -10.24));

@Before
public void setUp() {
// this whole test class is designed for "modern" geo queries
checkMinServerVersion(2.4);
super.setUp();
}

@Test
public void shouldFindCitiesInEurope() {
// given
MultiPolygon europeanCountries = multiPolygon(uk, spain);

City manchester = new City("Manchester", point(53.4722454, -2.2235922));
getDs().save(manchester);
City london = new City("London", point(51.5286416, -0.1015987));
getDs().save(london);
City sevilla = new City("Sevilla", point(37.3753708, -5.9550582));
getDs().save(sevilla);
City newYork = new City("New York", point(40.75981395319104, -73.98302106186748));
getDs().save(newYork);

getDs().ensureIndexes();

// when
List<City> citiesInTheUK;
citiesInTheUK = getDs().find(City.class)
.field("location")
.within(europeanCountries)
.asList();

// then
assertThat(citiesInTheUK.size(), is(3));
assertThat(citiesInTheUK, containsInAnyOrder(london, manchester,
sevilla));
}

@Test
public void shouldFindRoutesCompletelyWithinRequiredEuropeCountries() {
// given
MultiPolygon requiredEuropeanCountries = multiPolygon(uk, spain);
Route sevilla = new Route("Spain", lineString(pointBuilder().latitude(37.40759155713022).longitude(-5.964911067858338).build(),
pointBuilder().latitude(37.40341208875179).longitude(-5.9643941558897495).build(),
pointBuilder().latitude(37.40297396667302).longitude(-5.970452763140202).build()));
getDs().save(sevilla);
Route newYork = new Route("New York", lineString(pointBuilder().latitude(40.75981395319104).longitude(-73.98302106186748).build(),
pointBuilder().latitude(40.7636824529618).longitude(-73.98049869574606).build(),
pointBuilder().latitude(40.76962974853814).longitude(-73.97964206524193).build()));
getDs().save(newYork);
Route london = new Route("London", lineString(pointBuilder().latitude(51.507780365645885).longitude(-0.21786745637655258).build(),
pointBuilder().latitude(51.50802478194237).longitude(-0.21474729292094707).build(),
pointBuilder().latitude(51.5086863655597).longitude(-0.20895397290587425).build()));
getDs().save(london);
Route londonToParis = new Route("London To Paris", lineString(pointBuilder().latitude(51.5286416).longitude(-0.1015987).build(),
pointBuilder().latitude(48.858859).longitude(2.3470599).build()));
getDs().save(londonToParis);
getDs().ensureIndexes();

// when
List<Route> routesInTheUK = getDs().find(Route.class)
.field("route")
.within(requiredEuropeanCountries)
.asList();

// then
assertThat(routesInTheUK.size(), is(2));
assertThat(routesInTheUK, containsInAnyOrder(london, sevilla));
}

@Test
public void shouldFindAreasCompletelyWithinRequiredEuropeanCountries() {
// given
MultiPolygon requiredEuropeanCountries = multiPolygon(uk, spain);
Area sevilla = new Area("Spain", polygon(pointBuilder().latitude(37.40759155713022).longitude(-5.964911067858338).build(),
pointBuilder().latitude(37.40341208875179).longitude(-5.9643941558897495).build(),
pointBuilder().latitude(37.40297396667302).longitude(-5.970452763140202).build(),
pointBuilder().latitude(37.40759155713022).longitude(-5.964911067858338).build())
);
getDs().save(sevilla);
Area newYork = new Area("New York", polygon(pointBuilder().latitude(40.75981395319104).longitude(-73.98302106186748).build(),
pointBuilder().latitude(40.7636824529618).longitude(-73.98049869574606).build(),
pointBuilder().latitude(40.76962974853814).longitude(-73.97964206524193).build(),
pointBuilder().latitude(40.75981395319104).longitude(-73.98302106186748).build()));
getDs().save(newYork);
Area london = new Area("London", polygon(pointBuilder().latitude(51.507780365645885).longitude(-0.21786745637655258).build(),
pointBuilder().latitude(51.50802478194237).longitude(-0.21474729292094707).build(),
pointBuilder().latitude(51.5086863655597).longitude(-0.20895397290587425).build(),
pointBuilder().latitude(51.507780365645885).longitude(-0.21786745637655258).build()));
getDs().save(london);
Area ukAndSomeOfEurope = new Area("Europe", polygon(
pointBuilder().latitude(58.0).longitude(-10.0).build(),
pointBuilder().latitude(58.0).longitude(3).build(),
pointBuilder().latitude(48.858859).longitude(3).build(),
pointBuilder().latitude(48.858859).longitude(-10).build(),
pointBuilder().latitude(58.0).longitude(-10.0).build()));
getDs().save(ukAndSomeOfEurope);
getDs().ensureIndexes();

// when
List<Area> areasInTheUK = getDs().find(Area.class)
.field("area")
.within(requiredEuropeanCountries)
.asList();

// then
assertThat(areasInTheUK.size(), is(2));
assertThat(areasInTheUK, containsInAnyOrder(london, sevilla));
}

@Test
public void shouldFindRegionsWithinEurope() {
checkMinServerVersion(2.6);
// given
MultiPolygon europeanCountries = multiPolygon(uk, spain);
Regions sevilla = new Regions("Spain", multiPolygon(polygon(point(37.40759155713022, -5.964911067858338),
point(37.40341208875179, -5.9643941558897495),
point(37.40297396667302, -5.970452763140202),
point(37.40759155713022, -5.964911067858338)),
polygon(point(37.38744598813355, -6.001141928136349),
point(37.385990973562, -6.002588979899883),
point(37.386126928031445, -6.002463921904564),
point(37.38744598813355, -6.001141928136349))));
getDs().save(sevilla);

Regions usa = new Regions("US", multiPolygon(polygon(point(40.75981395319104, -73.98302106186748),
point(40.7636824529618, -73.98049869574606),
point(40.76962974853814, -73.97964206524193),
point(40.75981395319104, -73.98302106186748)),
polygon(point(28.326568258926272, -81.60542246885598),
point(28.327541397884488, -81.6022228449583),
point(28.32950334995985, -81.60564735531807),
point(28.326568258926272, -81.60542246885598))));
getDs().save(usa);

Regions london = new Regions("London", multiPolygon(polygon(point(51.507780365645885, -0.21786745637655258),
point(51.50802478194237, -0.21474729292094707),
point(51.5086863655597, -0.20895397290587425),
point(51.507780365645885, -0.21786745637655258)),
polygon(point(51.498216362670064, 0.0074849557131528854),
point(51.49176875129342, 0.01821178011596203),
point(51.492886897176504, 0.05523204803466797),
point(51.49393044412136, 0.06663135252892971),
point(51.498216362670064, 0.0074849557131528854))));
getDs().save(london);
getDs().ensureIndexes();

// when
List<Regions> regionsInTheUK = getDs().find(Regions.class)
.field("regions")
.within(europeanCountries)
.asList();

// then
assertThat(regionsInTheUK.size(), is(2));
assertThat(regionsInTheUK, containsInAnyOrder(sevilla, london));
}

@Test
public void shouldFindGeometryCollectionsWithinEurope() {
checkMinServerVersion(2.6);
// given
MultiPolygon europeanCountries = multiPolygon(uk, spain);

AllTheThings sevilla = new AllTheThings("Spain", geometryCollection(multiPoint(point(37.40759155713022, -5.964911067858338),
point(37.40341208875179, -5.9643941558897495),
point(37.40297396667302, -5.970452763140202)),
polygon(point(37.40759155713022, -5.964911067858338),
point(37.40341208875179, -5.9643941558897495),
point(37.40297396667302, -5.970452763140202),
point(37.40759155713022, -5.964911067858338)),
polygon(point(37.38744598813355, -6.001141928136349),
point(37.385990973562, -6.002588979899883),
point(37.386126928031445, -6.002463921904564),
point(37.38744598813355, -6.001141928136349))));
getDs().save(sevilla);

// insert something that's not a geocollection
Regions usa = new Regions("US", multiPolygon(polygon(point(40.75981395319104, -73.98302106186748),
point(40.7636824529618, -73.98049869574606),
point(40.76962974853814, -73.97964206524193),
point(40.75981395319104, -73.98302106186748)),
polygon(point(28.326568258926272, -81.60542246885598),
point(28.327541397884488, -81.6022228449583),
point(28.32950334995985, -81.60564735531807),
point(28.326568258926272, -81.60542246885598))));
getDs().save(usa);

AllTheThings london = new AllTheThings("London", geometryCollection(point(53.4722454, -2.2235922),
lineString(point(51.507780365645885, -0.21786745637655258),
point(51.50802478194237, -0.21474729292094707),
point(51.5086863655597, -0.20895397290587425)),
polygon(point(51.498216362670064, 0.0074849557131528854),
point(51.49176875129342, 0.01821178011596203),
point(51.492886897176504, 0.05523204803466797),
point(51.49393044412136, 0.06663135252892971),
point(51.498216362670064, 0.0074849557131528854))));
getDs().save(london);
getDs().ensureIndexes();

// when
List<AllTheThings> everythingInTheUK = getDs().find(AllTheThings.class)
.field("everything")
.within(europeanCountries)
.asList();

// then
assertThat(everythingInTheUK.size(), is(2));
assertThat(everythingInTheUK, containsInAnyOrder(london, sevilla));
}

}

0 comments on commit 42cf5c2

Please sign in to comment.