diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java index 56bb403ea..fe9630a04 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoTemplate.java @@ -683,7 +683,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher } } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("insert DBObject containing fields: " + dbDoc.keySet() + " in collecion: " + collectionName); + LOGGER.debug("insert DBObject containing fields: " + dbDoc.keySet() + " in collection: " + collectionName); } return execute(collectionName, new CollectionCallback() { public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { @@ -783,7 +783,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher */ public WriteResult updateFirst(String collectionName, final Query query, final Update update) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("calling update using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collecion: " + collectionName); + LOGGER.debug("calling update using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collection: " + collectionName); } return execute(collectionName, new CollectionCallback() { public WriteResult doInCollection(DBCollection collection) throws MongoException, DataAccessException { @@ -813,7 +813,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher */ public WriteResult updateMulti(String collectionName, final Query query, final Update update) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("calling updateMulti using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collecion: " + collectionName); + LOGGER.debug("calling updateMulti using query: " + query.getQueryObject() + " and update: " + update.getUpdateObject(mongoConverter) + " in collection: " + collectionName); } return execute(collectionName, new CollectionCallback() { public WriteResult doInCollection(DBCollection collection) throws MongoException, DataAccessException { @@ -858,7 +858,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher final DBObject queryObject = query.getQueryObject(); final PersistentEntity entity = getPersistentEntity(targetClass); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("remove using query: " + queryObject + " in collecion: " + collectionName); + LOGGER.debug("remove using query: " + queryObject + " in collection: " + collectionName); } execute(collectionName, new CollectionCallback() { public Void doInCollection(DBCollection collection) throws MongoException, DataAccessException { @@ -983,7 +983,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher protected List doFind(String collectionName, DBObject query, DBObject fields, Class targetClass, CursorPreparer preparer) { PersistentEntity entity = mappingContext.getPersistentEntity(targetClass); if (LOGGER.isDebugEnabled()) { - LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + targetClass + " in collecion: " + collectionName); + LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + targetClass + " in collection: " + collectionName); } return executeEach(new FindCallback(mapper.getMappedObject(query, entity), fields), preparer, @@ -1005,7 +1005,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher */ protected List doFind(String collectionName, DBObject query, DBObject fields, Class targetClass, MongoReader reader) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + targetClass + " in collecion: " + collectionName); + LOGGER.debug("find using query: " + query + " fields: " + fields + " for class: " + targetClass + " in collection: " + collectionName); } PersistentEntity entity = mappingContext.getPersistentEntity(targetClass); return executeEach(new FindCallback(mapper.getMappedObject(query, entity), fields), @@ -1048,7 +1048,7 @@ public class MongoTemplate implements MongoOperations, ApplicationEventPublisher readerToUse = this.mongoConverter; } if (LOGGER.isDebugEnabled()) { - LOGGER.debug("findAndRemove using query: " + query + " fields: " + fields + " sort: " + sort + " for class: " + targetClass + " in collecion: " + collectionName); + LOGGER.debug("findAndRemove using query: " + query + " fields: " + fields + " sort: " + sort + " for class: " + targetClass + " in collection: " + collectionName); } PersistentEntity entity = mappingContext.getPersistentEntity(targetClass); return execute(new FindAndRemoveCallback(mapper.getMappedObject(query, entity), fields, sort), diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Box.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Box.java index 0035b7663..b51cfb926 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Box.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Box.java @@ -15,83 +15,67 @@ */ package org.springframework.data.document.mongodb.geo; +import org.springframework.util.Assert; + /** * Represents a geospatial box value + * * @author Mark Pollack - * + * @author Oliver Gierke */ public class Box { - - private double xmin; - - private double ymin; - - private double xmax; - - private double ymax; - - public Box(Point lowerLeft, Point upperRight) { - xmin = lowerLeft.getX(); - ymin = lowerLeft.getY(); - xmax = upperRight.getX(); - ymax = upperRight.getY(); - } - - public Box(double[] lowerLeft, double[] upperRight) { - xmin = lowerLeft[0]; - ymin = lowerLeft[1]; - xmax = upperRight[0]; - ymax = upperRight[1]; - } - - public Point getLowerLeft() { - return new Point(xmin, ymin); - } - - public Point getUpperRight() { - return new Point(xmax, ymax); - } - @Override - public String toString() { - return "Box [xmin=" + xmin + ", ymin=" + ymin + ", xmax=" + xmax - + ", ymax=" + ymax + "]"; - } + private final Point first; + private final Point second; - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - long temp; - temp = Double.doubleToLongBits(xmax); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(xmin); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(ymax); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(ymin); - result = prime * result + (int) (temp ^ (temp >>> 32)); - return result; - } + public Box(Point lowerLeft, Point upperRight) { + Assert.notNull(lowerLeft); + Assert.notNull(upperRight); + this.first = lowerLeft; + this.second = upperRight; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Box other = (Box) obj; - if (Double.doubleToLongBits(xmax) != Double.doubleToLongBits(other.xmax)) - return false; - if (Double.doubleToLongBits(xmin) != Double.doubleToLongBits(other.xmin)) - return false; - if (Double.doubleToLongBits(ymax) != Double.doubleToLongBits(other.ymax)) - return false; - if (Double.doubleToLongBits(ymin) != Double.doubleToLongBits(other.ymin)) - return false; + public Box(double[] lowerLeft, double[] upperRight) { + Assert.isTrue(lowerLeft.length == 2, "Point array has to have 2 elements!"); + Assert.isTrue(upperRight.length == 2, "Point array has to have 2 elements!"); + this.first = new Point(lowerLeft[0], lowerLeft[1]); + this.second = new Point(upperRight[0], upperRight[1]); + } + + public Point getLowerLeft() { + return first; + } + + public Point getUpperRight() { + return second; + } + + @Override + public String toString() { + return String.format("Box [%s, %s]", first, second); + } + + @Override + public int hashCode() { + + int result = 31; + result += 17 * first.hashCode(); + result += 17 * second.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { return true; } - + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Box that = (Box) obj; + return this.first.equals(that.first) && this.second.equals(that.second); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Circle.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Circle.java index b9a28cb59..05ddaf7d4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Circle.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Circle.java @@ -15,24 +15,30 @@ */ package org.springframework.data.document.mongodb.geo; -import java.util.Arrays; +import org.springframework.util.Assert; /** * Represents a geospatial circle value * @author Mark Pollack - * + * @author Oliver Gierke */ public class Circle { - private double[] center; + private Point center; private double radius; - public Circle(double centerX, double centerY, double radius) { - this.center = new double[] { centerX, centerY }; + public Circle(Point center, double radius) { + Assert.notNull(center); + Assert.isTrue(radius >= 0, "Radius must not be negative!"); + this.center = center; this.radius = radius; } + + public Circle(double centerX, double centerY, double radius) { + this(new Point(centerX, centerY), radius); + } - public double[] getCenter() { + public Point getCenter() { return center; } @@ -42,9 +48,6 @@ public class Circle { @Override public String toString() { - return "Circle [center=" + Arrays.toString(center) + ", radius=" + radius - + "]"; + return String.format("Circle [center=%s, radius=%d]", center, radius); } - - } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java index 203b292d3..451bc277a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java @@ -15,23 +15,28 @@ */ package org.springframework.data.document.mongodb.geo; +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.util.Assert; + /** - * Represents a geospatial point value + * Represents a geospatial point value. + * * @author Mark Pollack - * + * @author Oliver Gierke */ public class Point { - private double x; + private final double x; + private final double y; - private double y; - + @PersistenceConstructor public Point(double x, double y) { this.x = x; this.y = y; } public Point(Point point) { + Assert.notNull(point); this.x = point.x; this.y = point.y; } @@ -43,6 +48,10 @@ public class Point { public double getY() { return y; } + + public double[] asArray() { + return new double[] {x, y}; + } @Override public int hashCode() { @@ -58,28 +67,29 @@ public class Point { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } Point other = (Point) obj; if (Double.doubleToLongBits(x) != Double - .doubleToLongBits(other.x)) + .doubleToLongBits(other.x)) { return false; + } if (Double.doubleToLongBits(y) != Double - .doubleToLongBits(other.y)) + .doubleToLongBits(other.y)) { return false; + } return true; } @Override public String toString() { - return "Point [latitude=" + x + ", longitude=" + y + "]"; + return String.format("Point [latitude=%d, longitude=%d]", x, y); } - - - - } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/index/GeoSpatialIndexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/index/GeoSpatialIndexed.java index fdb067f2d..613983355 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/index/GeoSpatialIndexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/index/GeoSpatialIndexed.java @@ -49,14 +49,14 @@ public @interface GeoSpatialIndexed { * * @return */ - int min() default 0; + int min() default -180; /** * Maximum value for indexed values. * * @return */ - int max() default 0; + int max() default 180; /** * Bits of precision for boundary calculations. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java index e714068fd..6a5ac5b52 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/mapping/MongoPersistentEntityIndexCreator.java @@ -37,10 +37,12 @@ import org.springframework.data.document.mongodb.index.CompoundIndexes; import org.springframework.data.document.mongodb.index.GeoSpatialIndexed; import org.springframework.data.document.mongodb.index.IndexDirection; import org.springframework.data.document.mongodb.index.Indexed; +import org.springframework.data.document.mongodb.query.GeospatialIndex; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.event.MappingContextEvent; import org.springframework.data.mapping.model.PersistentProperty; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Component that inspects {@link BasicMongoPersistentEntity} instances contained in the given {@link MongoMappingContext} @@ -62,7 +64,7 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener entity : mappingContext.getPersistentEntities()) { checkForIndexes(entity); } @@ -114,29 +116,27 @@ public class MongoPersistentEntityIndexCreator implements ApplicationListener() { - public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException { - DBObject defObj = new BasicDBObject(); - defObj.put(name, "2d"); - - DBObject opts = new BasicDBObject(); - // Min - if (min != 0) { - opts.put("min", min); - } - // Max - if (max != 0) { - opts.put("max", max); - } - // Bits - opts.put("bits", bits); - collection.ensureIndex(defObj, opts); - return null; - } - }); - } - } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Criteria.java index 76a89dd83..3a5209064 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/Criteria.java @@ -26,6 +26,7 @@ import org.springframework.data.document.InvalidDocumentStoreApiUsageException; import org.springframework.data.document.mongodb.geo.Box; import org.springframework.data.document.mongodb.geo.Circle; import org.springframework.data.document.mongodb.geo.Point; +import org.springframework.util.Assert; public class Criteria implements CriteriaDefinition { @@ -246,65 +247,66 @@ public class Criteria implements CriteriaDefinition { /** * Creates a geospatial criterion using a $within $center operation - * - * @param circle + * @param circle must not be {@literal null} * @return */ public Criteria withinCenter(Circle circle) { - LinkedList list = new LinkedList(); - list.addLast(circle.getCenter()); + Assert.notNull(circle); + LinkedList list = new LinkedList(); + list.addLast(circle.getCenter().asArray()); list.add(circle.getRadius()); criteria.put("$within", new BasicDBObject("$center", list)); return this; } - + /** - * Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher. - * - * @param circle + * Creates a geospatial criterion using a $within $center operation. This is only available for Mongo 1.7 and higher. + * @param circle must not be {@literal null} * @return */ public Criteria withinCenterSphere(Circle circle) { - LinkedList list = new LinkedList(); - list.addLast(circle.getCenter()); + Assert.notNull(circle); + LinkedList list = new LinkedList(); + list.addLast(circle.getCenter().asArray()); list.add(circle.getRadius()); criteria.put("$within", new BasicDBObject("$centerSphere", list)); return this; } - + /** * Creates a geospatial criterion using a $within $box operation * * @param circle * @return */ - public Criteria withinBox(Box box) { + public Criteria withinBox(Box box){ + Assert.notNull(box); LinkedList list = new LinkedList(); - list.addLast(new double[] { box.getLowerLeft().getX(), box.getLowerLeft().getY() }); - list.addLast(new double[] { box.getUpperRight().getX(), box.getUpperRight().getY() }); + list.addLast(box.getLowerLeft().asArray()); + list.addLast(box.getUpperRight().asArray()); criteria.put("$within", new BasicDBObject("$box", list)); return this; } - + /** * Creates a geospatial criterion using a $near operation - * - * @param point + * @param point must not be {@literal null} * @return */ public Criteria near(Point point) { - criteria.put("$near", new double[] { point.getX(), point.getY() }); + Assert.notNull(point); + criteria.put("$near", point.asArray()); return this; } /** - * Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher. - * - * @param point + * Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and higher. + * @param point must not be {@literal null} * @return */ public Criteria nearSphere(Point point) { - criteria.put("$nearSphere", new double[] { point.getX(), point.getY() }); + Assert.notNull(point); + criteria.put("$nearSphere", point.asArray()); return this; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/GeospatialIndex.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/GeospatialIndex.java index e37ad94e8..5a6b27746 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/GeospatialIndex.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/query/GeospatialIndex.java @@ -30,9 +30,8 @@ public class GeospatialIndex implements IndexDefinition { private Integer min = null; private Integer max = null; - - public GeospatialIndex() { - } + + private Integer bits = null; public GeospatialIndex(String key) { keyField = key; @@ -53,6 +52,11 @@ public class GeospatialIndex implements IndexDefinition { return this; } + public GeospatialIndex withBits(int bits) { + this.bits = Integer.valueOf(bits); + return this; + } + public DBObject getIndexKeys() { DBObject dbo = new BasicDBObject(); dbo.put(keyField, "2d"); @@ -73,7 +77,17 @@ public class GeospatialIndex implements IndexDefinition { if (max != null) { dbo.put("max", max); } + if (bits != null) { + dbo.put("bits", bits); + } return dbo; } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("Geo index: %s - Options: %s", getIndexKeys(), getIndexOptions()); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/ConvertingParameterAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/ConvertingParameterAccessor.java index 259c97b3b..aa55babba 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/ConvertingParameterAccessor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/ConvertingParameterAccessor.java @@ -33,6 +33,8 @@ import org.springframework.data.repository.query.ParameterAccessor; */ public class ConvertingParameterAccessor implements ParameterAccessor { +// private static final Set> TYPES_NOT_TO_CONVERT = new HashSet>(Arrays.asList(Circle.class, Box.class)) + private final MongoWriter writer; private final ParameterAccessor delegate; @@ -88,7 +90,7 @@ public class ConvertingParameterAccessor implements ParameterAccessor { * @return */ private Object getConvertedValue(Object value) { - + DBObject result = new BasicDBObject(); writer.write(value.getClass().isEnum() ? new EnumValueHolder((Enum) value) : new ValueHolder(value), result); return ((DBObject) result.get("value")).get("value"); @@ -99,7 +101,7 @@ public class ConvertingParameterAccessor implements ParameterAccessor { * * @author Oliver Gierke */ - private class ConvertingIterator implements Iterator { + private class ConvertingIterator implements PotentiallyConvertingIterator { private final Iterator delegate; @@ -128,7 +130,15 @@ public class ConvertingParameterAccessor implements ParameterAccessor { */ public Object next() { - return getConvertedValue(delegate.next()); + return delegate.next(); + } + + /* (non-Javadoc) + * @see org.springframework.data.document.mongodb.repository.ConvertingParameterAccessor.PotentiallConvertingIterator#nextConverted() + */ + public Object nextConverted() { + + return getConvertedValue(next()); } /* @@ -178,4 +188,19 @@ public class ConvertingParameterAccessor implements ParameterAccessor { return value; } } + + /** + * Custom {@link Iterator} that adds a method to access elements in a converted manner. + * + * @author Oliver Gierke + */ + public interface PotentiallyConvertingIterator extends Iterator { + + /** + * Returns the next element which has already been converted. + * + * @return + */ + Object nextConverted(); + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java index 23fbbac74..1497821de 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoQueryCreator.java @@ -23,9 +23,13 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.data.document.mongodb.geo.Box; +import org.springframework.data.document.mongodb.geo.Circle; +import org.springframework.data.document.mongodb.geo.Point; import org.springframework.data.document.mongodb.query.Criteria; import org.springframework.data.document.mongodb.query.CriteriaDefinition; import org.springframework.data.document.mongodb.query.Query; +import org.springframework.data.document.mongodb.repository.ConvertingParameterAccessor.PotentiallyConvertingIterator; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.ParametersParameterAccessor; @@ -66,7 +70,7 @@ class MongoQueryCreator extends AbstractQueryCreator { protected Query create(Part part, Iterator iterator) { Criteria criteria = from(part.getType(), - where(part.getProperty().toDotPath()), iterator); + where(part.getProperty().toDotPath()), (PotentiallyConvertingIterator) iterator); return new Query(criteria); } @@ -81,7 +85,7 @@ class MongoQueryCreator extends AbstractQueryCreator { Iterator iterator) { Criteria criteria = from(part.getType(), where(part.getProperty().toDotPath()), - iterator); + (PotentiallyConvertingIterator) iterator); return base.addCriteria(criteria); } @@ -126,17 +130,15 @@ class MongoQueryCreator extends AbstractQueryCreator { * @param parameters * @return */ - private Criteria from(Type type, Criteria criteria, - Iterator parameters) { + private Criteria from(Type type, Criteria criteria, PotentiallyConvertingIterator parameters) { switch (type) { case GREATER_THAN: - return criteria.gt(parameters.next()); + return criteria.gt(parameters.nextConverted()); case LESS_THAN: - return criteria.lt(parameters.next()); + return criteria.lt(parameters.nextConverted()); case BETWEEN: - return criteria.gt(parameters.next()).lt( - parameters.next()); + return criteria.gt(parameters.nextConverted()).lt(parameters.nextConverted()); case IS_NOT_NULL: return criteria.not().is(null); case IS_NULL: @@ -148,18 +150,49 @@ class MongoQueryCreator extends AbstractQueryCreator { case LIKE: String value = parameters.next().toString(); return criteria.is(toLikeRegex(value)); + case NEAR: + return criteria.near(nextAs(parameters, Point.class)); + case WITHIN: + + Object parameter = parameters.next(); + if (parameter instanceof Box) { + return criteria.withinBox((Box) parameter); + } else if (parameter instanceof Circle) { + return criteria.withinCenter((Circle) parameter); + } + throw new IllegalArgumentException("Parameter has to be either Box or Circle!"); case SIMPLE_PROPERTY: - return criteria.is(parameters.next()); + return criteria.is(parameters.nextConverted()); case NEGATING_SIMPLE_PROPERTY: - return criteria.not().is(parameters.next()); + return criteria.not().is(parameters.nextConverted()); } throw new IllegalArgumentException("Unsupported keyword!"); } + /** + * Returns the next element from the given {@link Iterator} expecting it to be of a certain type. + * + * @param + * @param iterator + * @param type + * @throws IllegalArgumentException + * in case the next element in the iterator is not of the given type. + * @return + */ + @SuppressWarnings("unchecked") + private T nextAs(Iterator iterator, Class type) { + Object parameter = iterator.next(); + if (parameter.getClass().isAssignableFrom(type)) { + return (T) parameter; + } - private Object[] nextAsArray(Iterator iterator) { - Object next = iterator.next(); + throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type, + parameter.getClass())); + } + + private Object[] nextAsArray(PotentiallyConvertingIterator iterator) { + Object next = iterator.nextConverted(); if (next instanceof Collection) { return ((Collection) next).toArray(); @@ -175,6 +208,4 @@ class MongoQueryCreator extends AbstractQueryCreator { String regex = source.replaceAll("\\*", ".*"); return Pattern.compile(regex); } - - } \ No newline at end of file diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java index 9fba28980..8f3da0a76 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/MongoRepositoryFactoryBean.java @@ -19,6 +19,9 @@ import static org.springframework.data.querydsl.QueryDslUtils.*; import java.io.Serializable; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,6 +37,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.Part; +import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.support.QueryCreationListener; import org.springframework.data.repository.support.RepositoryFactoryBeanSupport; @@ -264,6 +268,7 @@ public class MongoRepositoryFactoryBean, S, ID */ private static class IndexEnsuringQueryCreationListener implements QueryCreationListener { + private static final Set GEOSPATIAL_TYPES = new HashSet(Arrays.asList(Type.NEAR, Type.WITHIN)); private static final Log LOG = LogFactory.getLog(IndexEnsuringQueryCreationListener.class); private final MongoOperations operations; @@ -288,6 +293,9 @@ public class MongoRepositoryFactoryBean, S, ID Sort sort = tree.getSort(); for (Part part : tree.getParts()) { + if (GEOSPATIAL_TYPES.contains(part.getType())) { + return; + } String property = part.getProperty().toDotPath(); Order order = toOrder(sort, property); index.on(property, order); @@ -295,7 +303,7 @@ public class MongoRepositoryFactoryBean, S, ID MongoEntityInformation metadata = query.getQueryMethod().getEntityInformation(); operations.ensureIndex(metadata.getCollectionName(), index); - LOG.debug(String.format("Created index %s!", index.toString())); + LOG.debug(String.format("Created %s!", index)); } private static Order toOrder(Sort sort, String property) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java index 9e29653be..8b1a88b3c 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/repository/SimpleMongoRepository.java @@ -160,7 +160,7 @@ public class SimpleMongoRepository implements Paging */ public void deleteAll() { - template.dropCollection(entityInformation.getCollectionName()); + template.remove(entityInformation.getCollectionName(), new Query()); } /* diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index 1a9ceab5d..e434991af 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -9,9 +9,13 @@ import java.util.HashSet; import java.util.List; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.document.mongodb.geo.Box; +import org.springframework.data.document.mongodb.geo.Circle; +import org.springframework.data.document.mongodb.geo.Point; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort.Direction; @@ -175,6 +179,42 @@ public abstract class AbstractPersonRepositoryIntegrationTests { assertThat(result.size(), is(1)); assertThat(result, hasItem(dave)); } + + @Test + public void findsPeopleByLocationNear() { + Point point = new Point(-73.99171, 40.738868); + dave.setLocation(point); + repository.save(dave); + + List result = repository.findByLocationNear(point); + assertThat(result.size(), is(1)); + assertThat(result, hasItem(dave)); + } + + @Test + public void findsPeopleByLocationWithinCircle() { + Point point = new Point(-73.99171, 40.738868); + dave.setLocation(point); + repository.save(dave); + + List result = repository.findByLocationWithin(new Circle(-78.99171, 45.738868, 170)); + assertThat(result.size(), is(1)); + assertThat(result, hasItem(dave)); + } + + @Test + @Ignore + public void findsPeopleByLocationWithinBox() { + Point point = new Point(-73.99171, 40.738868); + dave.setLocation(point); + repository.save(dave); + + Box box = new Box(new Point(-78.99171, 35.738868), new Point(-68.99171, 45.738868)); + + List result = repository.findByLocationWithin(box); + assertThat(result.size(), is(1)); + assertThat(result, hasItem(dave)); + } @Test public void findsPagedPeopleByPredicate() throws Exception { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java index e26460bda..427246b6e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/Person.java @@ -18,6 +18,9 @@ package org.springframework.data.document.mongodb.repository; import java.util.Set; import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.document.mongodb.geo.Point; +import org.springframework.data.document.mongodb.index.GeoSpatialIndexed; import org.springframework.data.document.mongodb.mapping.Document; @@ -29,10 +32,14 @@ import org.springframework.data.document.mongodb.mapping.Document; @Document public class Person { + @Id private String id; private String firstname; private String lastname; private Integer age; + + @GeoSpatialIndexed + private Point location; private Address address; private Set
shippingAddresses; @@ -52,7 +59,7 @@ public class Person { public Person(String firstname, String lastname, Integer age) { - this.id = ObjectId.get().toString(); + this.id = new ObjectId().toString(); this.firstname = firstname; this.lastname = lastname; this.age = age; @@ -130,6 +137,20 @@ public class Person { this.age = age; } + + /** + * @return the location + */ + public Point getLocation() { + return location; + } + + /** + * @param location the location to set + */ + public void setLocation(Point location) { + this.location = location; + } /** * @return the address diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java index 564a5890c..f0e47c53d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/PersonRepository.java @@ -18,6 +18,9 @@ package org.springframework.data.document.mongodb.repository; import java.util.Collection; import java.util.List; +import org.springframework.data.document.mongodb.geo.Box; +import org.springframework.data.document.mongodb.geo.Circle; +import org.springframework.data.document.mongodb.geo.Point; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -117,4 +120,10 @@ public interface PersonRepository extends MongoRepository, Query List findByLastnameLikeAndAgeBetween(String lastname, int from, int to); List findByAgeOrLastnameLikeAndFirstnameLike(int age, String lastname, String firstname); + + List findByLocationNear(Point point); + + List findByLocationWithin(Circle circle); + + List findByLocationWithin(Box box); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/StringBasedMongoQueryUnitTests.java index 847300576..f60845145 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/StringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/repository/StringBasedMongoQueryUnitTests.java @@ -33,6 +33,9 @@ import org.springframework.data.document.mongodb.query.BasicQuery; import org.springframework.data.document.mongodb.repository.MongoRepositoryFactoryBean.EntityInformationCreator; import org.springframework.data.repository.support.RepositoryMetadata; +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + /** * Unit tests for {@link StringBasedMongoQuery}. * @@ -56,7 +59,7 @@ public class StringBasedMongoQueryUnitTests { } @Test - public void testname() throws Exception { + public void bindsSimplePropertyCorrectly() throws Exception { Method method = SampleRepository.class.getMethod("findByLastname", String.class); MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, creator); @@ -68,10 +71,32 @@ public class StringBasedMongoQueryUnitTests { assertThat(query.getQueryObject(), is(reference.getQueryObject())); } + + @Test + public void bindsComplexPropertyCorrectly() throws Exception { + + Method method = SampleRepository.class.getMethod("findByAddress", Address.class); + MongoQueryMethod queryMethod = new MongoQueryMethod(method, metadata, creator); + StringBasedMongoQuery mongoQuery = new StringBasedMongoQuery(queryMethod, template); + + Address address = new Address("Foo", "0123", "Bar"); + ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, address); + + DBObject dbObject = new BasicDBObject(); + converter.write(address, dbObject); + + org.springframework.data.document.mongodb.query.Query query = mongoQuery.createQuery(accesor); + org.springframework.data.document.mongodb.query.Query reference = new BasicQuery(new BasicDBObject("address", dbObject)); + + assertThat(query.getQueryObject(), is(reference.getQueryObject())); + } private interface SampleRepository { @Query("{ 'lastname' : ?0 }") Person findByLastname(String lastname); + + @Query("{ 'address' : ?0 }") + Person findByAddress(Address address); } } diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml index 824b82942..1f8dc51c9 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/PersonRepositoryIntegrationTests-context.xml @@ -3,18 +3,32 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml index bec490e13..9d0ff16a1 100644 --- a/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml +++ b/spring-data-mongodb/src/test/resources/org/springframework/data/document/mongodb/repository/config/MongoNamespaceIntegrationTests-context.xml @@ -11,7 +11,7 @@ - +