From 4175791d0b1840f10a5f1edc98cedb53faac3931 Mon Sep 17 00:00:00 2001 From: Mark Pollack Date: Tue, 5 Apr 2011 16:37:28 -0400 Subject: [PATCH] DATADOC-67 - Criteria API to support keywords for geo search --- .../data/document/mongodb/MongoTemplate.java | 2 +- .../data/document/mongodb/geo/Point.java | 87 +++++++++++++++++++ .../data/document/mongodb/query/Criteria.java | 26 +++++- .../document/mongodb/GeoSpatialTests.java | 39 +++++++-- 4 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java 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 7c5159054..aa00074ae 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 @@ -398,7 +398,7 @@ public class MongoTemplate implements InitializingBean, MongoOperations, Applica } return result; - } catch (MongoException e) { + } catch (RuntimeException e) { throw potentiallyConvertRuntimeException(e); } } 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 new file mode 100644 index 000000000..d6cff2a70 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/geo/Point.java @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2011 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.document.mongodb.geo; + +/** + * Represents a geospatial point value + * @author Mark Pollack + * + */ +public class Point { + + private double latitude; + + private double longitude; + + public Point(double latitude, double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + public Point(Point point) { + this.latitude = point.latitude; + this.longitude = point.longitude; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(latitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Point other = (Point) obj; + if (Double.doubleToLongBits(latitude) != Double + .doubleToLongBits(other.latitude)) + return false; + if (Double.doubleToLongBits(longitude) != Double + .doubleToLongBits(other.longitude)) + return false; + return true; + } + + @Override + public String toString() { + return "Point [latitude=" + latitude + ", longitude=" + longitude + "]"; + } + + + + +} 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 347aab417..4837d821f 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 @@ -24,6 +24,7 @@ import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import org.springframework.data.document.InvalidDocumentStoreApiUsageException; import org.springframework.data.document.mongodb.geo.Circle; +import org.springframework.data.document.mongodb.geo.Point; public class Criteria implements CriteriaDefinition { @@ -230,18 +231,37 @@ public class Criteria implements CriteriaDefinition { /** - * Creates a geospatial criterion using a $ + * Creates a geospatial criterion using a $within operation * @param circle * @return */ public Criteria within(Circle circle) { LinkedList list = new LinkedList(); list.addLast(circle.getCenter()); - list.add(circle.getRadius()); - //BasicDBObject dbo = new BasicDBObject("$within", new BasicDBObject("$center", list)); + list.add(circle.getRadius()); criteria.put("$within", new BasicDBObject("$center", list)); return this; } + + /** + * Creates a geospatial criterion using a $near operation + * @param point + * @return + */ + public Criteria near(Point point) { + criteria.put("$near", new double[]{point.getLatitude(), point.getLongitude()}); + return this; + } + + /** + * Creates a geospatical criterion using a $maxDistance operation, for use with $near + * @param maxDistance + * @return + */ + public Criteria maxDistance(double maxDistance) { + criteria.put("$maxDistance", maxDistance); + return this; + } /** * Creates a criterion using the $elemMatch operator diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/GeoSpatialTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/GeoSpatialTests.java index c9353943e..87ab20856 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/GeoSpatialTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/document/mongodb/GeoSpatialTests.java @@ -26,35 +26,51 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.dao.DataAccessException; 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.GeospatialIndex; import org.springframework.data.document.mongodb.query.Query; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBObject; +import com.mongodb.Mongo; import com.mongodb.MongoException; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:geospatial.xml") -@Ignore +/** + * Modified from https://github.com/deftlabs/mongo-java-geospatial-example + * @author Mark Pollack + * + */ +//@RunWith(SpringJUnit4ClassRunner.class) +//@ContextConfiguration("classpath:geospatial.xml") public class GeoSpatialTests { - @Autowired + private final String[] collectionsToDrop = new String[]{"newyork"}; + + ApplicationContext applicationContext; MongoTemplate template; @Before - public void setUp() { - template.dropCollection(template.getDefaultCollectionName()); + public void setUp() throws Exception { + Mongo mongo = new Mongo(); + DB db = mongo.getDB("geospatial"); + for (String coll : collectionsToDrop) { + db.getCollection(coll).drop(); + } + applicationContext = new ClassPathXmlApplicationContext("/geospatial.xml"); + template = applicationContext.getBean(MongoTemplate.class); + //template.dropCollection(template.getDefaultCollectionName()); template.ensureIndex(new GeospatialIndex("location")); addVenues(); } private void addVenues() { - // Data taken from https://github.com/deftlabs/mongo-java-geospatial-example template.insert(new Venue("Penn Station", -73.99408, 40.75057)); template.insert(new Venue("10gen Office", -73.99171, 40.738868)); template.insert(new Venue("Flatiron Building", -73.988135, 40.741404)); @@ -77,6 +93,13 @@ public class GeoSpatialTests { List venues = template.find(new Query(Criteria.where("location").within(circle)), Venue.class); assertThat(venues.size(), equalTo(8)); } + + @Test + public void nearPoint() { + Point point = new Point(-73.99171, 40.738868); + List venues = template.find(new Query(Criteria.where("location").near(point).maxDistance(0.01)), Venue.class); + assertThat(venues.size(), equalTo(8)); + } @Test public void indexCreated() {