Browse Source

DATAMONGO-1135 - Add support for GeoJson.

We’ve added special types representing GeoJson structures. This allows to use those within both queries and domain types.

GeoJson types should only be used in combination with a 2dsphere index as 2d index is not able to handle the structure. Though legacy coordinate pairs and GeoJson types can be mixed inside MongoDB, we currently do not support conversion of legacy coordinates to GeoJson types.
pull/277/head
Christoph Strobl 11 years ago committed by Oliver Gierke
parent
commit
1c43a3d1ee
  1. 360
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java
  2. 42
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJson.java
  3. 92
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java
  4. 58
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonLineString.java
  5. 106
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java
  6. 105
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
  7. 90
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java
  8. 72
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPoint.java
  9. 93
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java
  10. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
  11. 450
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoJsonConverterUnitTests.java
  12. 81
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
  13. 360
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
  14. 47
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java
  15. 41
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/BasicDbListBuilder.java
  16. 189
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java

360
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/GeoConverters.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,9 +30,18 @@ import org.springframework.data.geo.Metrics; @@ -30,9 +30,18 @@ import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.geo.Shape;
import org.springframework.data.mongodb.core.geo.GeoJson;
import org.springframework.data.mongodb.core.geo.GeoJsonGeometryCollection;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.query.GeoCommand;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@ -43,6 +52,7 @@ import com.mongodb.DBObject; @@ -43,6 +52,7 @@ import com.mongodb.DBObject;
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @since 1.5
*/
abstract class GeoConverters {
@ -69,7 +79,17 @@ abstract class GeoConverters { @@ -69,7 +79,17 @@ abstract class GeoConverters {
, DbObjectToSphereConverter.INSTANCE //
, DbObjectToPointConverter.INSTANCE //
, PointToDbObjectConverter.INSTANCE //
, GeoCommandToDbObjectConverter.INSTANCE);
, GeoCommandToDbObjectConverter.INSTANCE //
, GeoJsonToDbObjectConverter.INSTANCE //
, GeoJsonPointToDbObjectConverter.INSTANCE //
, GeoJsonPolygonToDbObjectConverter.INSTANCE //
, DbObjectToGeoJsonPointConverter.INSTANCE //
, DbObjectToGeoJsonPolygonConverter.INSTANCE //
, DbObjectToGeoJsonLineStringConverter.INSTANCE //
, DbObjectToGeoJsonMultiLineStringConverter.INSTANCE //
, DbObjectToGeoJsonMultiPointConverter.INSTANCE //
, DbObjectToGeoJsonMultiPolygonConverter.INSTANCE //
, DbObjectToGeoJsonGeometryCollectionConverter.INSTANCE);
}
/**
@ -79,7 +99,7 @@ abstract class GeoConverters { @@ -79,7 +99,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@ReadingConverter
public static enum DbObjectToPointConverter implements Converter<DBObject, Point> {
static enum DbObjectToPointConverter implements Converter<DBObject, Point> {
INSTANCE;
@ -102,7 +122,7 @@ abstract class GeoConverters { @@ -102,7 +122,7 @@ abstract class GeoConverters {
* @author Thomas Darimont
* @since 1.5
*/
public static enum PointToDbObjectConverter implements Converter<Point, DBObject> {
static enum PointToDbObjectConverter implements Converter<Point, DBObject> {
INSTANCE;
@ -123,7 +143,7 @@ abstract class GeoConverters { @@ -123,7 +143,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@WritingConverter
public static enum BoxToDbObjectConverter implements Converter<Box, DBObject> {
static enum BoxToDbObjectConverter implements Converter<Box, DBObject> {
INSTANCE;
@ -152,7 +172,7 @@ abstract class GeoConverters { @@ -152,7 +172,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@ReadingConverter
public static enum DbObjectToBoxConverter implements Converter<DBObject, Box> {
static enum DbObjectToBoxConverter implements Converter<DBObject, Box> {
INSTANCE;
@ -180,7 +200,7 @@ abstract class GeoConverters { @@ -180,7 +200,7 @@ abstract class GeoConverters {
* @author Thomas Darimont
* @since 1.5
*/
public static enum CircleToDbObjectConverter implements Converter<Circle, DBObject> {
static enum CircleToDbObjectConverter implements Converter<Circle, DBObject> {
INSTANCE;
@ -210,7 +230,7 @@ abstract class GeoConverters { @@ -210,7 +230,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@ReadingConverter
public static enum DbObjectToCircleConverter implements Converter<DBObject, Circle> {
static enum DbObjectToCircleConverter implements Converter<DBObject, Circle> {
INSTANCE;
@ -251,7 +271,7 @@ abstract class GeoConverters { @@ -251,7 +271,7 @@ abstract class GeoConverters {
* @author Thomas Darimont
* @since 1.5
*/
public static enum SphereToDbObjectConverter implements Converter<Sphere, DBObject> {
static enum SphereToDbObjectConverter implements Converter<Sphere, DBObject> {
INSTANCE;
@ -281,7 +301,7 @@ abstract class GeoConverters { @@ -281,7 +301,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@ReadingConverter
public static enum DbObjectToSphereConverter implements Converter<DBObject, Sphere> {
static enum DbObjectToSphereConverter implements Converter<DBObject, Sphere> {
INSTANCE;
@ -322,7 +342,7 @@ abstract class GeoConverters { @@ -322,7 +342,7 @@ abstract class GeoConverters {
* @author Thomas Darimont
* @since 1.5
*/
public static enum PolygonToDbObjectConverter implements Converter<Polygon, DBObject> {
static enum PolygonToDbObjectConverter implements Converter<Polygon, DBObject> {
INSTANCE;
@ -357,7 +377,7 @@ abstract class GeoConverters { @@ -357,7 +377,7 @@ abstract class GeoConverters {
* @since 1.5
*/
@ReadingConverter
public static enum DbObjectToPolygonConverter implements Converter<DBObject, Polygon> {
static enum DbObjectToPolygonConverter implements Converter<DBObject, Polygon> {
INSTANCE;
@ -392,7 +412,7 @@ abstract class GeoConverters { @@ -392,7 +412,7 @@ abstract class GeoConverters {
* @author Thomas Darimont
* @since 1.5
*/
public static enum GeoCommandToDbObjectConverter implements Converter<GeoCommand, DBObject> {
static enum GeoCommandToDbObjectConverter implements Converter<GeoCommand, DBObject> {
INSTANCE;
@ -411,6 +431,10 @@ abstract class GeoConverters { @@ -411,6 +431,10 @@ abstract class GeoConverters {
Shape shape = source.getShape();
if (shape instanceof GeoJson) {
return GeoJsonToDbObjectConverter.INSTANCE.convert((GeoJson) shape);
}
if (shape instanceof Box) {
argument.add(toList(((Box) shape).getFirst()));
@ -442,7 +466,317 @@ abstract class GeoConverters { @@ -442,7 +466,317 @@ abstract class GeoConverters {
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
@SuppressWarnings("rawtypes")
static enum GeoJsonToDbObjectConverter implements Converter<GeoJson, DBObject> {
INSTANCE;
@Override
public DBObject convert(GeoJson source) {
if (source == null) {
return null;
}
DBObject dbo = new BasicDBObject("type", source.getType());
if (source instanceof GeoJsonGeometryCollection) {
BasicDBList dbl = new BasicDBList();
for (GeoJson geometry : ((GeoJsonGeometryCollection) source).getCoordinates()) {
dbl.add(convert(geometry));
}
dbo.put("geometries", dbl);
} else {
dbo.put("coordinates", convertIfNecessarry(source.getCoordinates()));
}
return dbo;
}
private Object convertIfNecessarry(Object candidate) {
if (candidate instanceof GeoJson) {
return convertIfNecessarry(((GeoJson) candidate).getCoordinates());
}
if (candidate instanceof Iterable) {
BasicDBList dbl = new BasicDBList();
for (Object element : (Iterable) candidate) {
dbl.add(convertIfNecessarry(element));
}
return dbl;
}
if (candidate instanceof Point) {
return toList((Point) candidate);
}
return candidate;
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum GeoJsonPointToDbObjectConverter implements Converter<GeoJsonPoint, DBObject> {
INSTANCE;
@Override
public DBObject convert(GeoJsonPoint source) {
return GeoJsonToDbObjectConverter.INSTANCE.convert(source);
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum GeoJsonPolygonToDbObjectConverter implements Converter<GeoJsonPolygon, DBObject> {
INSTANCE;
@Override
public DBObject convert(GeoJsonPolygon source) {
return GeoJsonToDbObjectConverter.INSTANCE.convert(source);
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonPointConverter implements Converter<DBObject, GeoJsonPoint> {
INSTANCE;
@Override
public GeoJsonPoint convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Point"),
String.format("Cannot convert type '%s' to Point.", source.get("type")));
List<Double> dbl = (List<Double>) source.get("coordinates");
return new GeoJsonPoint(dbl.get(0).doubleValue(), dbl.get(1).doubleValue());
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonPolygonConverter implements Converter<DBObject, GeoJsonPolygon> {
INSTANCE;
@SuppressWarnings("rawtypes")
@Override
public GeoJsonPolygon convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "Polygon"),
String.format("Cannot convert type '%s' to Polygon.", source.get("type")));
return toGeoJsonPolygon((BasicDBList) source.get("coordinates"));
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonMultiPolygonConverter implements Converter<DBObject, GeoJsonMultiPolygon> {
INSTANCE;
@Override
public GeoJsonMultiPolygon convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiPolygon"),
String.format("Cannot convert type '%s' to MultiPolygon.", source.get("type")));
BasicDBList dbl = (BasicDBList) source.get("coordinates");
List<GeoJsonPolygon> polygones = new ArrayList<GeoJsonPolygon>();
for (Object polygon : dbl) {
polygones.add(toGeoJsonPolygon((BasicDBList) polygon));
}
return new GeoJsonMultiPolygon(polygones);
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonLineStringConverter implements Converter<DBObject, GeoJsonLineString> {
INSTANCE;
@Override
public GeoJsonLineString convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "LineString"),
String.format("Cannot convert type '%s' to LineString.", source.get("type")));
BasicDBList cords = (BasicDBList) source.get("coordinates");
return new GeoJsonLineString(toListOfPoint(cords));
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonMultiPointConverter implements Converter<DBObject, GeoJsonMultiPoint> {
INSTANCE;
@Override
public GeoJsonMultiPoint convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiPoint"),
String.format("Cannot convert type '%s' to MultiPoint.", source.get("type")));
BasicDBList cords = (BasicDBList) source.get("coordinates");
return new GeoJsonMultiPoint(toListOfPoint(cords));
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonMultiLineStringConverter implements Converter<DBObject, GeoJsonMultiLineString> {
INSTANCE;
@Override
public GeoJsonMultiLineString convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "MultiLineString"),
String.format("Cannot convert type '%s' to MultiLineString.", source.get("type")));
List<GeoJsonLineString> lines = new ArrayList<GeoJsonLineString>();
BasicDBList cords = (BasicDBList) source.get("coordinates");
for (Object line : cords) {
lines.add(new GeoJsonLineString(toListOfPoint((BasicDBList) line)));
}
return new GeoJsonMultiLineString(lines);
}
}
/**
* @author Christoph Strobl
* @since 1.7
*/
static enum DbObjectToGeoJsonGeometryCollectionConverter implements Converter<DBObject, GeoJsonGeometryCollection> {
INSTANCE;
@Override
public GeoJsonGeometryCollection convert(DBObject source) {
if (source == null) {
return null;
}
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"),
String.format("Cannot convert type '%s' to GeometryCollection.", source.get("type")));
List<GeoJson<?>> geometries = new ArrayList<GeoJson<?>>();
for (Object o : (List) source.get("geometries")) {
geometries.add(convertGeometries((DBObject) o));
}
return new GeoJsonGeometryCollection(geometries);
}
private GeoJson<?> convertGeometries(DBObject source) {
Object type = source.get("type");
if (ObjectUtils.nullSafeEquals(type, "Point")) {
return DbObjectToGeoJsonPointConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiPoint")) {
return DbObjectToGeoJsonMultiPointConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "LineString")) {
return DbObjectToGeoJsonLineStringConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiLineString")) {
return DbObjectToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "Polygon")) {
return DbObjectToGeoJsonPolygonConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiPolygon")) {
return DbObjectToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
}
throw new IllegalArgumentException(String.format("Cannot convert unknown GeoJson type %s", type));
}
}
static List<Double> toList(Point point) {
return Arrays.asList(point.getX(), point.getY());
}
/**
* Converts a coordinate pairs nested in in {@link BasicDBList} into {@link GeoJsonPoint}s.
*
* @param listOfCoordinatePairs
* @return
* @since 1.7
*/
@SuppressWarnings("unchecked")
static List<Point> toListOfPoint(BasicDBList listOfCoordinatePairs) {
List<Point> points = new ArrayList<Point>();
for (Object point : listOfCoordinatePairs) {
Assert.isInstanceOf(List.class, point);
List<Double> coordinatesList = (List<Double>) point;
points.add(new GeoJsonPoint(coordinatesList.get(0).doubleValue(), coordinatesList.get(1).doubleValue()));
}
return points;
}
/**
* Converts a coordinate pairs nested in in {@link BasicDBList} into {@link GeoJsonPolygon}.
*
* @param dbl
* @return
* @since 1.7
*/
static GeoJsonPolygon toGeoJsonPolygon(BasicDBList dbl) {
List<Point> outer = toListOfPoint((BasicDBList) dbl.get(0));
return new GeoJsonPolygon(outer);
}
}

42
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJson.java

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
/**
* Interface definition for structures defined in GeoJSON ({@link http://geojson.org/}) format.
*
* @author Christoph Strobl
* @since 1.7
*/
public interface GeoJson<T extends Iterable<?>> {
/**
* String value representing the type of the {@link GeoJson} object.
*
* @return never {@literal null}.
* @see http://geojson.org/geojson-spec.html#geojson-objects
*/
String getType();
/**
* The value of the coordinates member is always an {@link Iterable}. The structure for the elements within is
* determined by {@link #getType()} of geometry.
*
* @return never {@literal null}.
* @see http://geojson.org/geojson-spec.html#geometry-objects
*/
T getCoordinates();
}

92
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonGeometryCollection.java

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Defines a {@link GeoJsonGeometryCollection} that consists of a {@link List} of {@link GeoJson} objects.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#geometry-collection
*/
public class GeoJsonGeometryCollection implements GeoJson<Iterable<GeoJson<?>>> {
private static final String TYPE = "GeometryCollection";
private final List<GeoJson<?>> geometries = new ArrayList<GeoJson<?>>();
/**
* @param geometries
*/
public GeoJsonGeometryCollection(List<GeoJson<?>> geometries) {
Assert.notNull(geometries);
this.geometries.addAll(geometries);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public Iterable<GeoJson<?>> getCoordinates() {
return Collections.unmodifiableList(this.geometries);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.geometries);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof GeoJsonGeometryCollection)) {
return false;
}
GeoJsonGeometryCollection other = (GeoJsonGeometryCollection) obj;
return ObjectUtils.nullSafeEquals(this.geometries, other.geometries);
}
}

58
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonLineString.java

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.List;
import org.springframework.data.geo.Point;
/**
* {@link GeoJsonLineString} is defined as list of at least 2 {@link Point}s.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#linestring
*/
public class GeoJsonLineString extends GeoJsonMultiPoint {
private static final String TYPE = "LineString";
/**
* @param points must not be {@literal null} and have at least 2 entries.
*/
public GeoJsonLineString(List<Point> points) {
super(points);
}
/**
* @param p0 must not be {@literal null}
* @param p1 must not be {@literal null}
* @param others can be {@literal null}
*/
public GeoJsonLineString(Point p0, Point p1, Point... others) {
super(p0, p1, others);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint#getType()
*/
@Override
public String getType() {
return TYPE;
}
}

106
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiLineString.java

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* {@link GeoJsonMultiLineString} is defined as list of {@link GeoJsonLineString}s.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#multilinestring
*/
public class GeoJsonMultiLineString implements GeoJson<Iterable<GeoJsonLineString>> {
private static final String TYPE = "MultiLineString";
private List<GeoJsonLineString> coordinates = new ArrayList<GeoJsonLineString>();
/**
* Creates new {@link GeoJsonMultiLineString}.
*
* @param lines must not be {@literal null}.
*/
@SuppressWarnings("unchecked")
public GeoJsonMultiLineString(List<Point>... lines) {
Assert.notEmpty(lines, "Lines for MultiLineString must not be null!");
for (List<Point> line : lines) {
this.coordinates.add(new GeoJsonLineString(line));
}
}
/**
* @param lines must not be {@literal null}.
*/
public GeoJsonMultiLineString(List<GeoJsonLineString> lines) {
Assert.notNull(lines, "Lines for MultiLineString must not be null!");
this.coordinates.addAll(lines);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public Iterable<GeoJsonLineString> getCoordinates() {
return Collections.unmodifiableList(this.coordinates);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.coordinates);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof GeoJsonMultiLineString)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.coordinates, ((GeoJsonMultiLineString) obj).coordinates);
}
}

105
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* {@link GeoJsonMultiPoint} is defined as list of {@link Point}s.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#multipoint
*/
public class GeoJsonMultiPoint implements GeoJson<Iterable<Point>> {
private static final String TYPE = "MultiPoint";
private final List<Point> points;
/**
* @param points points must not be {@literal null} and have at least 2 entries.
*/
public GeoJsonMultiPoint(List<Point> points) {
Assert.notNull(points, "Points must not be null.");
Assert.isTrue(points.size() >= 2, "Minimum of 2 Points required.");
this.points = new ArrayList<Point>(points);
}
public GeoJsonMultiPoint(Point p0, Point p1, Point... others) {
this.points = new ArrayList<Point>();
this.points.add(p0);
this.points.add(p1);
if (!ObjectUtils.isEmpty(others)) {
this.points.addAll(Arrays.asList(others));
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public List<Point> getCoordinates() {
return Collections.unmodifiableList(this.points);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.points);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof GeoJsonMultiPoint)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.points, ((GeoJsonMultiPoint) obj).points);
}
}

90
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPolygon.java

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* {@link GeoJsonMultiPolygon} is defined as a list of {@link GeoJsonPolygon}s.
*
* @author Christoph Strobl
* @since 1.7
*/
public class GeoJsonMultiPolygon implements GeoJson<Iterable<GeoJsonPolygon>> {
private static final String TYPE = "MultiPolygon";
private List<GeoJsonPolygon> coordinates = new ArrayList<GeoJsonPolygon>();
/**
* @param polygons must not be {@literal null}.
*/
public GeoJsonMultiPolygon(List<GeoJsonPolygon> polygons) {
Assert.notNull(polygons, "Polygons for MultiPolygon must not be null!");
this.coordinates.addAll(polygons);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public List<GeoJsonPolygon> getCoordinates() {
return Collections.unmodifiableList(this.coordinates);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.coordinates);
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof GeoJsonMultiPolygon)) {
return false;
}
return ObjectUtils.nullSafeEquals(this.coordinates, ((GeoJsonMultiPolygon) obj).coordinates);
}
}

72
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPoint.java

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.Arrays;
import java.util.List;
import org.springframework.data.geo.Point;
/**
* {@link GeoJson} representation of {@link Point}.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#point
*/
public class GeoJsonPoint extends Point implements GeoJson<List<Double>> {
private static final long serialVersionUID = -8026303425147474002L;
private static final String TYPE = "Point";
/**
* Creates {@link GeoJsonPoint} for given coordinates.
*
* @param x
* @param y
*/
public GeoJsonPoint(double x, double y) {
super(x, y);
}
/**
* Creates {@link GeoJsonPoint} for given {@link Point}.
*
* @param point must not be {@literal null}.
*/
public GeoJsonPoint(Point point) {
super(point);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public List<Double> getCoordinates() {
return Arrays.asList(Double.valueOf(getX()), Double.valueOf(getY()));
}
}

93
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonPolygon.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.util.ObjectUtils;
/**
* {@link GeoJson} representation of {@link Polygon}. Unlike {@link Polygon} the {@link GeoJsonPolygon} requires a
* closed border. Which means that the first and last {@link Point} have to have same coordinate pairs.
*
* @author Christoph Strobl
* @since 1.7
* @see http://geojson.org/geojson-spec.html#polygon
*/
public class GeoJsonPolygon extends Polygon implements GeoJson<List<GeoJsonLineString>> {
private static final long serialVersionUID = 3936163018187247185L;
private static final String TYPE = "Polygon";
private List<GeoJsonLineString> coordinates = new ArrayList<GeoJsonLineString>();
/**
* Creates new {@link GeoJsonPolygon}.
*
* @param p0 must not be {@literal null}.
* @param p1 must not be {@literal null}.
* @param p2 must not be {@literal null}.
* @param p3 must not be {@literal null}.
* @param others can be {@literal null}.
*/
public GeoJsonPolygon(Point p0, Point p1, Point p2, Point p3, final Point... others) {
this(new ArrayList<Point>(Arrays.asList(p0, p1, p2, p3)) {
private static final long serialVersionUID = 3143657022446395361L;
{
if (!ObjectUtils.isEmpty(others)) {
for (Point p : others) {
add(p);
}
}
}
});
}
/**
* Creates new {@link GeoJsonPolygon}.
*
* @param points must not be {@literal null}.
*/
public GeoJsonPolygon(List<Point> points) {
super(points);
this.coordinates.add(new GeoJsonLineString(points));
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
*/
@Override
public String getType() {
return TYPE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getCoordinates()
*/
@Override
public List<GeoJsonLineString> getCoordinates() {
return Collections.unmodifiableList(this.coordinates);
}
}

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

@ -29,6 +29,7 @@ import org.springframework.data.geo.Circle; @@ -29,6 +29,7 @@ import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Shape;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.geo.GeoJson;
import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@ -439,6 +440,11 @@ public class Criteria implements CriteriaDefinition { @@ -439,6 +440,11 @@ public class Criteria implements CriteriaDefinition {
* @return
*/
public Criteria maxDistance(double maxDistance) {
if (createNearCriteriaForCommand("$near", maxDistance) || createNearCriteriaForCommand("$nearSphere", maxDistance)) {
return this;
}
criteria.put("$maxDistance", maxDistance);
return this;
}
@ -540,7 +546,13 @@ public class Criteria implements CriteriaDefinition { @@ -540,7 +546,13 @@ public class Criteria implements CriteriaDefinition {
boolean not = false;
for (String k : this.criteria.keySet()) {
Object value = this.criteria.get(k);
if (requiresGeoJsonFormat(value)) {
value = new BasicDBObject("$geometry", value);
}
if (not) {
DBObject notDbo = new BasicDBObject();
notDbo.put(k, value);
@ -593,6 +605,31 @@ public class Criteria implements CriteriaDefinition { @@ -593,6 +605,31 @@ public class Criteria implements CriteriaDefinition {
}
}
private boolean requiresGeoJsonFormat(Object value) {
return value instanceof GeoJson
|| (value instanceof GeoCommand && ((GeoCommand) value).getShape() instanceof GeoJson);
}
private boolean createNearCriteriaForCommand(String command, double maxDistance) {
if (!criteria.containsKey(command)) {
return false;
}
Object existingNearOperationValue = criteria.get(command);
if (existingNearOperationValue instanceof DBObject) {
((DBObject) existingNearOperationValue).put("$maxDistance", maxDistance);
return true;
} else if (existingNearOperationValue instanceof GeoJson) {
BasicDBObject dbo = new BasicDBObject("$geometry", existingNearOperationValue);
dbo.put("$maxDistance", maxDistance);
criteria.put(command, dbo);
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)

450
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/GeoJsonConverterUnitTests.java

@ -0,0 +1,450 @@ @@ -0,0 +1,450 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonLineStringConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonMultiLineStringConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonMultiPointConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonMultiPolygonConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonPointConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.DbObjectToGeoJsonPolygonConverter;
import org.springframework.data.mongodb.core.convert.GeoConverters.GeoJsonToDbObjectConverter;
import org.springframework.data.mongodb.core.geo.GeoJson;
import org.springframework.data.mongodb.core.geo.GeoJsonGeometryCollection;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
/**
* @author Christoph Strobl
*/
@RunWith(Suite.class)
@SuiteClasses({ GeoJsonConverterUnitTests.GeoJsonToDbObjectConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonPointConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonPolygonConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonLineStringConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonMultiPolygonConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonMultiLineStringConverterUnitTests.class,
GeoJsonConverterUnitTests.DbObjectToGeoJsonMultiPointConverterUnitTests.class })
public class GeoJsonConverterUnitTests {
/*
* --- GeoJson
*/
static final GeoJsonPoint SINGLE_POINT = new GeoJsonPoint(100, 50);
static final Point POINT_0 = new Point(0, 0);
static final Point POINT_1 = new Point(100, 0);
static final Point POINT_2 = new Point(100, 100);
static final Point POINT_3 = new Point(0, 100);
static final GeoJsonMultiPoint MULTI_POINT = new GeoJsonMultiPoint(POINT_0, POINT_2, POINT_3);
static final GeoJsonLineString LINE_STRING = new GeoJsonLineString(POINT_0, POINT_1, POINT_2);
@SuppressWarnings("unchecked") static final GeoJsonMultiLineString MULTI_LINE_STRING = new GeoJsonMultiLineString(
Arrays.asList(POINT_0, POINT_1, POINT_2), Arrays.asList(POINT_3, POINT_0));
static final GeoJsonPolygon POLYGON = new GeoJsonPolygon(POINT_0, POINT_1, POINT_2, POINT_3, POINT_0);
static final GeoJsonMultiPolygon MULTI_POLYGON = new GeoJsonMultiPolygon(Arrays.asList(POLYGON));
static final GeoJsonGeometryCollection GEOMETRY_COLLECTION = new GeoJsonGeometryCollection(
Arrays.<GeoJson<?>> asList(SINGLE_POINT, POLYGON));
/*
* -- GeoJson DBObjects
*/
// Point
static final BasicDBList SINGE_POINT_CORDS = new BasicDbListBuilder() //
.add(SINGLE_POINT.getX()) //
.add(SINGLE_POINT.getY()) //
.get(); //
static final DBObject SINGLE_POINT_DBO = new BasicDBObjectBuilder() //
.add("type", "Point") //
.add("coordinates", SINGE_POINT_CORDS)//
.get();
// MultiPoint
static final BasicDBList MULTI_POINT_CORDS = new BasicDbListBuilder() //
.add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) //
.get();
static final DBObject MULTI_POINT_DBO = new BasicDBObjectBuilder() //
.add("type", "MultiPoint")//
.add("coordinates", MULTI_POINT_CORDS)//
.get();
// Polygon
static final BasicDBList POLYGON_OUTER_CORDS = new BasicDbListBuilder() //
.add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_1.getX()).add(POINT_1.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) //
.get();
static final BasicDBList POLYGON_CORDS = new BasicDbListBuilder().add(POLYGON_OUTER_CORDS).get();
static final DBObject POLYGON_DBO = new BasicDBObjectBuilder() //
.add("type", "Polygon") //
.add("coordinates", POLYGON_CORDS) //
.get();
// LineString
static final BasicDBList LINE_STRING_CORDS_0 = new BasicDbListBuilder() //
.add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_1.getX()).add(POINT_1.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_2.getX()).add(POINT_2.getY()).get()) //
.get();
static final BasicDBList LINE_STRING_CORDS_1 = new BasicDbListBuilder() //
.add(new BasicDbListBuilder().add(POINT_3.getX()).add(POINT_3.getY()).get()) //
.add(new BasicDbListBuilder().add(POINT_0.getX()).add(POINT_0.getY()).get()) //
.get();
static final DBObject LINE_STRING_DBO = new BasicDBObjectBuilder().add("type", "LineString")
.add("coordinates", LINE_STRING_CORDS_0).get();
// MultiLineString
static final BasicDBList MUILT_LINE_STRING_CORDS = new BasicDbListBuilder() //
.add(LINE_STRING_CORDS_0) //
.add(LINE_STRING_CORDS_1) //
.get();
static final DBObject MULTI_LINE_STRING_DBO = new BasicDBObjectBuilder().add("type", "MultiLineString")
.add("coordinates", MUILT_LINE_STRING_CORDS).get();
// MultiPolygoin
static final BasicDBList MULTI_POLYGON_CORDS = new BasicDbListBuilder().add(POLYGON_CORDS).get();
static final DBObject MULTI_POLYGON_DBO = new BasicDBObjectBuilder().add("type", "MultiPolygon")
.add("coordinates", MULTI_POLYGON_CORDS).get();
// GeometryCollection
static final BasicDBList GEOMETRY_COLLECTION_GEOMETRIES = new BasicDbListBuilder() //
.add(SINGLE_POINT_DBO)//
.add(POLYGON_DBO)//
.get();
static final DBObject GEOMETRY_COLLECTION_DBO = new BasicDBObjectBuilder().add("type", "GeometryCollection")
.add("geometries", GEOMETRY_COLLECTION_GEOMETRIES).get();
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonPolygonConverterUnitTests {
DbObjectToGeoJsonPolygonConverter converter = DbObjectToGeoJsonPolygonConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(POLYGON_DBO), equalTo(POLYGON));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPolygon() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to Polygon");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonPointConverterUnitTests {
DbObjectToGeoJsonPointConverter converter = DbObjectToGeoJsonPointConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(SINGLE_POINT_DBO), equalTo(SINGLE_POINT));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to Point");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonLineStringConverterUnitTests {
DbObjectToGeoJsonLineStringConverter converter = DbObjectToGeoJsonLineStringConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(LINE_STRING_DBO), equalTo(LINE_STRING));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to LineString");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonMultiLineStringConverterUnitTests {
DbObjectToGeoJsonMultiLineStringConverter converter = DbObjectToGeoJsonMultiLineStringConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(MULTI_LINE_STRING_DBO), equalTo(MULTI_LINE_STRING));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to MultiLineString");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonMultiPointConverterUnitTests {
DbObjectToGeoJsonMultiPointConverter converter = DbObjectToGeoJsonMultiPointConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(MULTI_POINT_DBO), equalTo(MULTI_POINT));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to MultiPoint");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class DbObjectToGeoJsonMultiPolygonConverterUnitTests {
DbObjectToGeoJsonMultiPolygonConverter converter = DbObjectToGeoJsonMultiPolygonConverter.INSTANCE;
public @Rule ExpectedException expectedException = ExpectedException.none();
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertDboCorrectly() {
assertThat(converter.convert(MULTI_POLYGON_DBO), equalTo(MULTI_POLYGON));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldReturnNullWhenConvertIsGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldThrowExceptionWhenTypeDoesNotMatchPoint() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("'YouDontKonwMe' to MultiPolygon");
converter.convert(new BasicDBObject("type", "YouDontKonwMe"));
}
}
/**
* @author Christoph Strobl
*/
public static class GeoJsonToDbObjectConverterUnitTests {
GeoJsonToDbObjectConverter converter = GeoJsonToDbObjectConverter.INSTANCE;
/**
* @see DATAMONGO-1135
*/
public void convertShouldReturnNullWhenGivenNull() {
assertThat(converter.convert(null), nullValue());
}
/**
* @see DATAMONGO-1135
*/
@Test
public void shouldConvertGeoJsonPointCorrectly() {
assertThat(converter.convert(SINGLE_POINT), equalTo(SINGLE_POINT_DBO));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void shouldConvertGeoJsonPolygonCorrectly() {
assertThat(converter.convert(POLYGON), equalTo(POLYGON_DBO));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertGeoJsonLineStringCorrectly() {
assertThat(converter.convert(LINE_STRING), equalTo(LINE_STRING_DBO));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertGeoJsonMultiLineStringCorrectly() {
assertThat(converter.convert(MULTI_LINE_STRING), equalTo(MULTI_LINE_STRING_DBO));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertGeoJsonMultiPointCorrectly() {
assertThat(converter.convert(MULTI_POINT), equalTo(MULTI_POINT_DBO));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertGeoJsonMultiPolygonCorrectly() {
assertThat(converter.convert(MULTI_POLYGON), equalTo(MULTI_POLYGON_DBO));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouldConvertGeometryCollectionCorrectly() {
assertThat(converter.convert(GEOMETRY_COLLECTION), equalTo(GEOMETRY_COLLECTION_DBO));
}
}
}

81
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

@ -20,6 +20,7 @@ import static org.junit.Assert.*; @@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.math.BigInteger;
import java.util.ArrayList;
@ -36,9 +37,12 @@ import org.mockito.runners.MockitoJUnitRunner; @@ -36,9 +37,12 @@ import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@ -702,6 +706,75 @@ public class QueryMapperUnitTests { @@ -702,6 +706,75 @@ public class QueryMapperUnitTests {
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("nested.id", 1).get()));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void nearShouldUseGeoJsonRepresentationOnUnmappedProperty() {
Query query = query(where("foo").near(new GeoJsonPoint(100, 50)));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(ClassWithGeoTypes.class));
assertThat(dbo, isBsonObject().containing("foo.$near.$geometry.type", "Point"));
assertThat(dbo, isBsonObject().containing("foo.$near.$geometry.coordinates.[0]", 100D));
assertThat(dbo, isBsonObject().containing("foo.$near.$geometry.coordinates.[1]", 50D));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void nearShouldUseGeoJsonRepresentationWhenMappingToGoJsonType() {
Query query = query(where("geoJsonPoint").near(new GeoJsonPoint(100, 50)));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(ClassWithGeoTypes.class));
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$near.$geometry.type", "Point"));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void nearSphereShouldUseGeoJsonRepresentationWhenMappingToGoJsonType() {
Query query = query(where("geoJsonPoint").nearSphere(new GeoJsonPoint(100, 50)));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(ClassWithGeoTypes.class));
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$nearSphere.$geometry.type", "Point"));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void shouldMapNameCorrectlyForGeoJsonType() {
Query query = query(where("namedGeoJsonPoint").nearSphere(new GeoJsonPoint(100, 50)));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(ClassWithGeoTypes.class));
assertThat(dbo,
isBsonObject().containing("geoJsonPointWithNameViaFieldAnnotation.$nearSphere.$geometry.type", "Point"));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void withinShouldUseGeoJsonPolygonWhenMappingPolygonOn2DSphereIndex() {
Query query = query(where("geoJsonPoint").within(
new GeoJsonPolygon(new Point(0, 0), new Point(100, 100), new Point(100, 0), new Point(0, 0))));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(ClassWithGeoTypes.class));
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoWithin.$geometry.type", "Polygon"));
}
@Document
public class Foo {
@Id private ObjectId id;
@ -794,4 +867,12 @@ public class QueryMapperUnitTests { @@ -794,4 +867,12 @@ public class QueryMapperUnitTests {
@Field("id") String id;
}
static class ClassWithGeoTypes {
double[] justAnArray;
Point legacyPoint;
GeoJsonPoint geoJsonPoint;
@Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint;
}
}

360
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java

@ -0,0 +1,360 @@ @@ -0,0 +1,360 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.geo;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import com.mongodb.WriteConcern;
/**
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class GeoJsonTests {
@Configuration
static class TestConfig extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "database";
}
@Override
public Mongo mongo() throws Exception {
return new MongoClient();
}
}
@Autowired MongoTemplate template;
@Before
public void setUp() {
template.setWriteConcern(WriteConcern.FSYNC_SAFE);
createIndex();
addVenues();
}
@After
public void tearDown() {
dropIndex();
removeCollections();
}
/**
* @see DATAMONGO-1135
*/
@Test
public void geoNear() {
NearQuery geoNear = NearQuery.near(new GeoJsonPoint(-73, 40), Metrics.KILOMETERS).num(10).maxDistance(150);
GeoResults<Venue2DSphere> result = template.geoNear(geoNear, Venue2DSphere.class);
assertThat(result.getContent().size(), is(not(0)));
assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void withinPolygon() {
Point first = new Point(-73.99756, 40.73083);
Point second = new Point(-73.99756, 40.741404);
Point third = new Point(-73.988135, 40.741404);
Point fourth = new Point(-73.988135, 40.73083);
GeoJsonPolygon polygon = new GeoJsonPolygon(first, second, third, fourth, first);
List<Venue2DSphere> venues = template.find(query(where("location").within(polygon)), Venue2DSphere.class);
assertThat(venues.size(), is(4));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void nearPoint() {
GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868);
Query query = query(where("location").near(point).maxDistance(0.01));
List<Venue2DSphere> venues = template.find(query, Venue2DSphere.class);
assertThat(venues.size(), is(1));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void nearSphere() {
GeoJsonPoint point = new GeoJsonPoint(-73.99171, 40.738868);
Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784));
List<Venue2DSphere> venues = template.find(query, Venue2DSphere.class);
assertThat(venues.size(), is(1));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonPointTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonPoint";
obj.geoJsonPoint = new GeoJsonPoint(100, 50);
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonPoint, equalTo(obj.geoJsonPoint));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonPolygonTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonPolygon";
obj.geoJsonPolygon = new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 0),
new Point(0, 0));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonPolygon, equalTo(obj.geoJsonPolygon));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonLineStringTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonLineString";
obj.geoJsonLineString = new GeoJsonLineString(new Point(0, 0), new Point(0, 1), new Point(1, 1));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonLineString, equalTo(obj.geoJsonLineString));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonMultiLineStringTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonMultiLineString";
obj.geoJsonMultiLineString = new GeoJsonMultiLineString(Arrays.asList(new GeoJsonLineString(new Point(0, 0),
new Point(0, 1), new Point(1, 1)), new GeoJsonLineString(new Point(199, 0), new Point(2, 3))));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonMultiLineString, equalTo(obj.geoJsonMultiLineString));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonMultiPointTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonMultiPoint";
obj.geoJsonMultiPoint = new GeoJsonMultiPoint(Arrays.asList(new Point(0, 0), new Point(0, 1), new Point(1, 1)));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonMultiPoint, equalTo(obj.geoJsonMultiPoint));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonMultiPolygonTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonMultiPolygon";
obj.geoJsonMultiPolygon = new GeoJsonMultiPolygon(Arrays.asList(new GeoJsonPolygon(new Point(0, 0),
new Point(0, 1), new Point(1, 1), new Point(0, 0))));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonMultiPolygon, equalTo(obj.geoJsonMultiPolygon));
}
/**
* @see DATAMONGO-1137
*/
@Test
public void shouleSaveAndRetrieveDocumentWithGeoJsonGeometryCollectionTypeCorrectly() {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonGeometryCollection";
obj.geoJsonGeometryCollection = new GeoJsonGeometryCollection(Arrays.<GeoJson<?>> asList(
new GeoJsonPoint(100, 200), new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1,
0), new Point(0, 0))));
template.save(obj);
DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
DocumentWithPropertyUsingGeoJsonType.class);
assertThat(result.geoJsonGeometryCollection, equalTo(obj.geoJsonGeometryCollection));
}
private void addVenues() {
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
template.insert(new Venue2DSphere("10gen Office", -73.99171, 40.738868));
template.insert(new Venue2DSphere("Flatiron Building", -73.988135, 40.741404));
template.insert(new Venue2DSphere("Players Club", -73.997812, 40.739128));
template.insert(new Venue2DSphere("City Bakery ", -73.992491, 40.738673));
template.insert(new Venue2DSphere("Splash Bar", -73.992491, 40.738673));
template.insert(new Venue2DSphere("Momofuku Milk Bar", -73.985839, 40.731698));
template.insert(new Venue2DSphere("Shake Shack", -73.98820, 40.74164));
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
template.insert(new Venue2DSphere("Empire State Building", -73.98602, 40.74894));
template.insert(new Venue2DSphere("Ulaanbaatar, Mongolia", 106.9154, 47.9245));
template.insert(new Venue2DSphere("Maplewood, NJ", -74.2713, 40.73137));
}
protected void createIndex() {
dropIndex();
template.indexOps(Venue2DSphere.class).ensureIndex(
new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));
}
protected void dropIndex() {
try {
template.indexOps(Venue2DSphere.class).dropIndex("location");
} catch (Exception e) {
}
}
protected void removeCollections() {
template.dropCollection(Venue2DSphere.class);
template.dropCollection(DocumentWithPropertyUsingGeoJsonType.class);
}
@Document(collection = "venue2dsphere")
static class Venue2DSphere {
@Id private String id;
private String name;
private @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) double[] location;
@PersistenceConstructor
public Venue2DSphere(String name, double[] location) {
this.name = name;
this.location = location;
}
public Venue2DSphere(String name, double x, double y) {
this.name = name;
this.location = new double[] { x, y };
}
public String getName() {
return name;
}
public double[] getLocation() {
return location;
}
@Override
public String toString() {
return "Venue2DSphere [id=" + id + ", name=" + name + ", location=" + Arrays.toString(location) + "]";
}
}
static class DocumentWithPropertyUsingGeoJsonType {
String id;
GeoJsonPoint geoJsonPoint;
GeoJsonPolygon geoJsonPolygon;
GeoJsonLineString geoJsonLineString;
GeoJsonMultiLineString geoJsonMultiLineString;
GeoJsonMultiPoint geoJsonMultiPoint;
GeoJsonMultiPolygon geoJsonMultiPolygon;
GeoJsonGeometryCollection geoJsonGeometryCollection;
}
}

47
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java

@ -19,7 +19,10 @@ import static org.hamcrest.CoreMatchers.*; @@ -19,7 +19,10 @@ import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.test.util.IsBsonObject;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
@ -164,4 +167,48 @@ public class CriteriaTests { @@ -164,4 +167,48 @@ public class CriteriaTests {
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("$not", new BasicDBObject("$lt", "foo")).get()));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void geoJsonTypesShouldBeWrappedInGeometry() {
DBObject dbo = new Criteria("foo").near(new GeoJsonPoint(100, 200)).getCriteriaObject();
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$near.$geometry", new GeoJsonPoint(100, 200)));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void legacyCoordinateTypesShouldNotBeWrappedInGeometry() {
DBObject dbo = new Criteria("foo").near(new Point(100, 200)).getCriteriaObject();
assertThat(dbo, IsBsonObject.isBsonObject().notContaining("foo.$near.$geometry"));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void maxDistanceShouldBeMappedInsideNearWhenUsedAlongWithGeoJsonType() {
DBObject dbo = new Criteria("foo").near(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject();
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$near.$maxDistance", 50D));
}
/**
* @see DATAMONGO-1135
*/
@Test
public void maxDistanceShouldBeMappedInsideNearSphereWhenUsedAlongWithGeoJsonType() {
DBObject dbo = new Criteria("foo").nearSphere(new GeoJsonPoint(100, 200)).maxDistance(50D).getCriteriaObject();
assertThat(dbo, IsBsonObject.isBsonObject().containing("foo.$nearSphere.$maxDistance", 50D));
}
}

41
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/BasicDbListBuilder.java

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.test.util;
import com.mongodb.BasicDBList;
/**
* @author Christoph Strobl
*/
public class BasicDbListBuilder {
private final BasicDBList dbl;
public BasicDbListBuilder() {
this.dbl = new BasicDBList();
}
public BasicDbListBuilder add(Object value) {
this.dbl.add(value);
return this;
}
public BasicDBList get() {
return this.dbl;
}
}

189
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java

@ -0,0 +1,189 @@ @@ -0,0 +1,189 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.test.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.bson.BSONObject;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.hamcrest.core.IsEqual;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.util.ClassUtils;
import com.mongodb.DBObject;
/**
* @author Christoph Strobl
* @param <T>
*/
public class IsBsonObject<T extends BSONObject> extends TypeSafeMatcher<T> {
private List<ExpectedBsonContent> expectations = new ArrayList<ExpectedBsonContent>();;
public static <T extends BSONObject> IsBsonObject<T> isBsonObject() {
return new IsBsonObject<T>();
}
@Override
protected void describeMismatchSafely(T item, Description mismatchDescription) {
mismatchDescription.appendText("was ").appendValue(SerializationUtils.serializeToJsonSafely(item));
}
@Override
public void describeTo(Description description) {
for (ExpectedBsonContent expectation : expectations) {
if (expectation.not) {
description.appendText(String.format("Path %s should not be present.", expectation.path));
} else if (expectation.value == null) {
description.appendText(String.format("Expected to find path %s.", expectation.path));
} else {
description.appendText(String.format("Expected to find %s for path %s.", expectation.value, expectation.path));
}
}
}
@Override
protected boolean matchesSafely(T item) {
if (expectations.isEmpty()) {
return true;
}
for (ExpectedBsonContent expectation : expectations) {
Object o = getValue(item, expectation.path);
if (o == null && expectation.not) {
return true;
}
if (o == null) {
return false;
}
if (expectation.type != null && !ClassUtils.isAssignable(expectation.type, o.getClass())) {
return false;
}
if (expectation.value != null && !new IsEqual<Object>(expectation.value).matches(o)) {
return false;
}
}
return true;
}
public IsBsonObject<T> containing(String key) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> containing(String key, Class<?> type) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.type = type;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> containing(String key, Object value) {
if (value == null) {
return notContaining(key);
}
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.type = ClassUtils.getUserClass(value);
expected.value = value;
this.expectations.add(expected);
return this;
}
public IsBsonObject<T> notContaining(String key) {
ExpectedBsonContent expected = new ExpectedBsonContent();
expected.path = key;
expected.not = true;
this.expectations.add(expected);
return this;
}
static class ExpectedBsonContent {
String path;
Class<?> type;
Object value;
boolean not = false;
}
Object getValue(BSONObject source, String path) {
String[] fragments = path.split("\\.");
if (fragments.length == 1) {
return source.get(path);
}
Iterator<String> it = Arrays.asList(fragments).iterator();
Object current = source;
while (it.hasNext()) {
String key = it.next();
if (!(current instanceof BSONObject) && !key.startsWith("[")) {
return null;
}
if (key.startsWith("[")) {
String indexNumber = key.substring(1, key.indexOf("]"));
if (current instanceof List) {
current = ((List) current).get(Integer.valueOf(indexNumber));
}
if (!it.hasNext()) {
return current;
}
} else {
if (current instanceof DBObject) {
current = ((DBObject) current).get(key);
}
if (!it.hasNext()) {
return current;
}
}
}
throw new NoSuchElementException(String.format("Unable to find '%s' in %s.", path, source));
}
}
Loading…
Cancel
Save