From 59e54cecd224b34ce18e4c5ea9f24d072360d4a4 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 9 Jan 2015 11:12:35 +0100 Subject: [PATCH] DATAMONGO-1136 - Use $geoWithin instead of $within for geo queries. We now use the $geoWithin operator for geospatial criteria which requires to run on at least MongoDB 2.4. Original pull request: #263. --- .../data/mongodb/core/query/Criteria.java | 12 +- .../data/mongodb/core/query/GeoCommand.java | 41 +- ...ests.java => AbstractGeoSpatialTests.java} | 392 ++++++++---------- .../core/geo/GeoSpatial2DSphereTests.java | 68 +++ .../mongodb/core/geo/GeoSpatial2DTests.java | 82 ++++ .../query/MongoQueryCreatorUnitTests.java | 30 +- src/main/asciidoc/preface.adoc | 2 +- .../reference/mongo-repositories.adoc | 4 +- src/main/asciidoc/reference/mongodb.adoc | 14 +- 9 files changed, 411 insertions(+), 234 deletions(-) rename spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/{GeoSpatialTests.java => AbstractGeoSpatialTests.java} (55%) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index a57dee028..f8190738f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 the original author or authors. + * Copyright 2010-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. @@ -376,8 +376,8 @@ public class Criteria implements CriteriaDefinition { } /** - * Creates a geospatial criterion using a {@literal $within $centerSphere} operation. This is only available for Mongo - * 1.7 and higher. + * Creates a geospatial criterion using a {@literal $geoWithin $centerSphere} operation. This is only available for + * Mongo 2.4 and higher. * * @see http://docs.mongodb.org/manual/reference/operator/query/geoWithin/ * @see http://docs.mongodb.org/manual/reference/operator/query/centerSphere/ @@ -386,12 +386,12 @@ public class Criteria implements CriteriaDefinition { */ public Criteria withinSphere(Circle circle) { Assert.notNull(circle); - criteria.put("$within", new GeoCommand(new Sphere(circle))); + criteria.put("$geoWithin", new GeoCommand(new Sphere(circle))); return this; } /** - * Creates a geospatial criterion using a {@literal $within} operation. + * Creates a geospatial criterion using a {@literal $geoWithin} operation. * * @see http://docs.mongodb.org/manual/reference/operator/query/geoWithin/ * @param shape @@ -400,7 +400,7 @@ public class Criteria implements CriteriaDefinition { public Criteria within(Shape shape) { Assert.notNull(shape); - criteria.put("$within", new GeoCommand(shape)); + criteria.put("$geoWithin", new GeoCommand(shape)); return this; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java index 960b1bc46..651846570 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/GeoCommand.java @@ -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. @@ -21,11 +21,13 @@ import org.springframework.data.geo.Polygon; import org.springframework.data.geo.Shape; import org.springframework.data.mongodb.core.geo.Sphere; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Wrapper around a {@link Shape} to allow appropriate query rendering. * * @author Thomas Darimont + * @author Christoph Strobl * @since 1.5 */ public class GeoCommand { @@ -82,4 +84,41 @@ public class GeoCommand { throw new IllegalArgumentException("Unknown shape: " + shape); } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = 0; + result += ObjectUtils.nullSafeHashCode(this.command); + result += ObjectUtils.nullSafeHashCode(this.shape); + return result; + } + + /* + * (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 GeoCommand)) { + return false; + } + + GeoCommand that = (GeoCommand) obj; + if (!ObjectUtils.nullSafeEquals(this.command, that.command)) { + return false; + } + return ObjectUtils.nullSafeEquals(this.shape, that.shape); + } + } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java similarity index 55% rename from spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java rename to spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java index 45f8508a1..15ba5b74c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatialTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/AbstractGeoSpatialTests.java @@ -1,216 +1,176 @@ -/* - * Copyright 2010-2014 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.Collection; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; -import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.geo.Box; -import org.springframework.data.geo.Circle; -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.geo.Polygon; -import org.springframework.data.mongodb.config.AbstractIntegrationTests; -import org.springframework.data.mongodb.core.CollectionCallback; -import org.springframework.data.mongodb.core.IndexOperations; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.index.GeospatialIndex; -import org.springframework.data.mongodb.core.index.IndexField; -import org.springframework.data.mongodb.core.index.IndexInfo; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -import com.mongodb.DBCollection; -import com.mongodb.DBObject; -import com.mongodb.MongoException; -import com.mongodb.WriteConcern; - -/** - * Modified from https://github.com/deftlabs/mongo-java-geospatial-example - * - * @author Mark Pollack - * @author Oliver Gierke - * @author Thomas Darimont - */ -public class GeoSpatialTests extends AbstractIntegrationTests { - - private static final Log LOGGER = LogFactory.getLog(GeoSpatialTests.class); - - @Autowired MongoTemplate template; - - ExpressionParser parser = new SpelExpressionParser(); - - @Before - public void setUp() throws Exception { - - template.setWriteConcern(WriteConcern.FSYNC_SAFE); - template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location")); - - indexCreated(); - addVenues(); - } - - private void addVenues() { - - 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)); - template.insert(new Venue("Players Club", -73.997812, 40.739128)); - template.insert(new Venue("City Bakery ", -73.992491, 40.738673)); - template.insert(new Venue("Splash Bar", -73.992491, 40.738673)); - template.insert(new Venue("Momofuku Milk Bar", -73.985839, 40.731698)); - template.insert(new Venue("Shake Shack", -73.98820, 40.74164)); - template.insert(new Venue("Penn Station", -73.99408, 40.75057)); - template.insert(new Venue("Empire State Building", -73.98602, 40.74894)); - // template.insert(new Venue("Washington Square Park", -73.99756, 40.73083)); - template.insert(new Venue("Ulaanbaatar, Mongolia", 106.9154, 47.9245)); - template.insert(new Venue("Maplewood, NJ", -74.2713, 40.73137)); - } - - @Test - public void geoNear() { - - NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150); - - GeoResults result = template.geoNear(geoNear, Venue.class); - - assertThat(result.getContent().size(), is(not(0))); - assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS)); - } - - @Test - public void withinCenter() { - Circle circle = new Circle(-73.99171, 40.738868, 0.01); - List venues = template.find(query(where("location").within(circle)), Venue.class); - assertThat(venues.size(), is(7)); - } - - @Test - public void withinCenterSphere() { - Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); - List venues = template.find(query(where("location").withinSphere(circle)), Venue.class); - assertThat(venues.size(), is(11)); - } - - @Test - public void withinBox() { - - Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); - List venues = template.find(query(where("location").within(box)), Venue.class); - assertThat(venues.size(), is(4)); - } - - @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); - - Polygon polygon = new Polygon(first, second, third, fourth); - - List venues = template.find(query(where("location").within(polygon)), Venue.class); - assertThat(venues.size(), is(4)); - } - - @Test - public void nearPoint() { - Point point = new Point(-73.99171, 40.738868); - List venues = template.find(query(where("location").near(point).maxDistance(0.01)), Venue.class); - assertThat(venues.size(), is(7)); - } - - @Test - public void nearSphere() { - Point point = new Point(-73.99171, 40.738868); - Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784)); - List venues = template.find(query, Venue.class); - assertThat(venues.size(), is(11)); - } - - @Test - public void searchAllData() { - - Venue foundVenue = template.findOne(query(where("name").is("Penn Station")), Venue.class); - assertThat(foundVenue, is(notNullValue())); - - List venues = template.findAll(Venue.class); - assertThat(venues.size(), is(12)); - - Collection names = (Collection) parser.parseExpression("![name]").getValue(venues); - assertThat(names.size(), is(12)); - - } - - public void indexCreated() { - - List indexInfo = getIndexInfo(Venue.class); - LOGGER.debug(indexInfo); - - assertThat(indexInfo.size(), is(2)); - assertThat(indexInfo.get(1).get("name").toString(), is("location_2d")); - assertThat(indexInfo.get(1).get("ns").toString(), is("database.newyork")); - } - - /** - * @see DATAMONGO-360 - */ - @Test - public void indexInfoIsCorrect() { - - IndexOperations operations = template.indexOps(Venue.class); - List indexInfo = operations.getIndexInfo(); - - assertThat(indexInfo.size(), is(2)); - - List fields = indexInfo.get(0).getIndexFields(); - assertThat(fields.size(), is(1)); - assertThat(fields, hasItem(IndexField.create("_id", Direction.ASC))); - - fields = indexInfo.get(1).getIndexFields(); - assertThat(fields.size(), is(1)); - assertThat(fields, hasItem(IndexField.geo("location"))); - } - - // TODO move to MongoAdmin - public List getIndexInfo(Class clazz) { - return template.execute(clazz, new CollectionCallback>() { - - public List doInCollection(DBCollection collection) throws MongoException, DataAccessException { - return collection.getIndexInfo(); - } - }); - } -} +/* + * 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.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.geo.Box; +import org.springframework.data.geo.Circle; +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.geo.Polygon; +import org.springframework.data.mongodb.config.AbstractMongoConfiguration; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.Venue; +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 abstract class AbstractGeoSpatialTests { + + @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(); + removeVenues(); + } + + /** + * Create the index required to run the tests. + */ + protected abstract void createIndex(); + + /** + * Remove index + */ + protected abstract void dropIndex(); + + protected void removeVenues() { + template.dropCollection(Venue.class); + } + + protected void addVenues() { + + 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)); + template.insert(new Venue("Players Club", -73.997812, 40.739128)); + template.insert(new Venue("City Bakery ", -73.992491, 40.738673)); + template.insert(new Venue("Splash Bar", -73.992491, 40.738673)); + template.insert(new Venue("Momofuku Milk Bar", -73.985839, 40.731698)); + template.insert(new Venue("Shake Shack", -73.98820, 40.74164)); + template.insert(new Venue("Penn Station", -73.99408, 40.75057)); + template.insert(new Venue("Empire State Building", -73.98602, 40.74894)); + template.insert(new Venue("Ulaanbaatar, Mongolia", 106.9154, 47.9245)); + template.insert(new Venue("Maplewood, NJ", -74.2713, 40.73137)); + } + + @Test + public void geoNear() { + + NearQuery geoNear = NearQuery.near(-73, 40, Metrics.KILOMETERS).num(10).maxDistance(150); + + GeoResults result = template.geoNear(geoNear, Venue.class); + + assertThat(result.getContent().size(), is(not(0))); + assertThat(result.getAverageDistance().getMetric(), is((Metric) Metrics.KILOMETERS)); + } + + @Test + public void withinCenter() { + + Circle circle = new Circle(-73.99171, 40.738868, 0.01); + List venues = template.find(query(where("location").within(circle)), Venue.class); + assertThat(venues.size(), is(7)); + } + + @Test + public void withinCenterSphere() { + + Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); + List venues = template.find(query(where("location").withinSphere(circle)), Venue.class); + assertThat(venues.size(), is(11)); + } + + @Test + public void withinBox() { + + Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); + List venues = template.find(query(where("location").within(box)), Venue.class); + assertThat(venues.size(), is(4)); + } + + @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); + + Polygon polygon = new Polygon(first, second, third, fourth); + + List venues = template.find(query(where("location").within(polygon)), Venue.class); + assertThat(venues.size(), is(4)); + } + + @Test + public void nearSphere() { + Point point = new Point(-73.99171, 40.738868); + Query query = query(where("location").nearSphere(point).maxDistance(0.003712240453784)); + List venues = template.find(query, Venue.class); + assertThat(venues.size(), is(11)); + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java new file mode 100644 index 000000000..086397067 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DSphereTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2010-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 java.util.List; + +import org.junit.Test; +import org.springframework.data.domain.Sort.Direction; +import org.springframework.data.mongodb.core.IndexOperations; +import org.springframework.data.mongodb.core.Venue; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeospatialIndex; +import org.springframework.data.mongodb.core.index.IndexField; +import org.springframework.data.mongodb.core.index.IndexInfo; + +/** + * @author Christoph Strobl + */ +public class GeoSpatial2DSphereTests extends AbstractGeoSpatialTests { + + /** + * @see DATAMONGO-360 + */ + @Test + public void indexInfoIsCorrect() { + + IndexOperations operations = template.indexOps(Venue.class); + List indexInfo = operations.getIndexInfo(); + + assertThat(indexInfo.size(), is(2)); + + List fields = indexInfo.get(0).getIndexFields(); + assertThat(fields.size(), is(1)); + assertThat(fields, hasItem(IndexField.create("_id", Direction.ASC))); + + fields = indexInfo.get(1).getIndexFields(); + assertThat(fields.size(), is(1)); + assertThat(fields, hasItem(IndexField.geo("location"))); + } + + @Override + protected void createIndex() { + template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); + } + + @Override + protected void dropIndex() { + template.indexOps(Venue.class).dropIndex("location_2dsphere"); + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java new file mode 100644 index 000000000..e9248f63b --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoSpatial2DTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2010-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.List; + +import org.junit.Test; +import org.springframework.data.domain.Sort.Direction; +import org.springframework.data.geo.Point; +import org.springframework.data.mongodb.core.IndexOperations; +import org.springframework.data.mongodb.core.Venue; +import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; +import org.springframework.data.mongodb.core.index.GeospatialIndex; +import org.springframework.data.mongodb.core.index.IndexField; +import org.springframework.data.mongodb.core.index.IndexInfo; + +/** + * Modified from https://github.com/deftlabs/mongo-java-geospatial-example + * + * @author Mark Pollack + * @author Oliver Gierke + * @author Thomas Darimont + * @author Christoph Strobl + */ +public class GeoSpatial2DTests extends AbstractGeoSpatialTests { + + @Test + public void nearPoint() { + Point point = new Point(-73.99171, 40.738868); + List venues = template.find(query(where("location").near(point).maxDistance(0.01)), Venue.class); + assertThat(venues.size(), is(7)); + } + + /** + * @see DATAMONGO-360 + */ + @Test + public void indexInfoIsCorrect() { + + IndexOperations operations = template.indexOps(Venue.class); + List indexInfo = operations.getIndexInfo(); + + assertThat(indexInfo.size(), is(2)); + + List fields = indexInfo.get(0).getIndexFields(); + assertThat(fields.size(), is(1)); + assertThat(fields, hasItem(IndexField.create("_id", Direction.ASC))); + + fields = indexInfo.get(1).getIndexFields(); + assertThat(fields.size(), is(1)); + assertThat(fields, hasItem(IndexField.geo("location"))); + } + + @Override + protected void createIndex() { + template.indexOps(Venue.class).ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2D)); + } + + @Override + protected void dropIndex() { + template.indexOps(Venue.class).dropIndex("location_2d"); + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index abc7d3baf..cc5777141 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-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. @@ -39,6 +39,8 @@ import org.mockito.stubbing.Answer; import org.springframework.data.geo.Distance; 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.mapping.context.MappingContext; import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.Venue; @@ -483,6 +485,24 @@ public class MongoQueryCreatorUnitTests { assertThat(query, is(query(where("location").near(point).maxDistance(1.0)))); } + /** + * @see DATAMONGO-1136 + */ + @Test + public void shouldCreateWithinQueryCorrectly() { + + Point first = new Point(1, 1); + Point second = new Point(2, 2); + Point third = new Point(3, 3); + Shape shape = new Polygon(first, second, third); + + PartTree tree = new PartTree("findByAddress_GeoWithin", User.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, shape), context); + Query query = creator.createQuery(); + + assertThat(query, is(query(where("address.geo").within(shape)))); + } + interface PersonRepository extends Repository { List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); @@ -495,5 +515,13 @@ public class MongoQueryCreatorUnitTests { @DBRef User creator; List emailAddresses; + + Address address; + } + + static class Address { + + String street; + Point geo; } } diff --git a/src/main/asciidoc/preface.adoc b/src/main/asciidoc/preface.adoc index bb1de636d..7002954f9 100644 --- a/src/main/asciidoc/preface.adoc +++ b/src/main/asciidoc/preface.adoc @@ -32,7 +32,7 @@ The jumping off ground for learning about MongoDB is http://www.mongodb.org/[www Spring Data MongoDB 1.x binaries requires JDK level 6.0 and above, and http://spring.io/docs[Spring Framework] 3.2.x and above. -In terms of document stores, http://www.mongodb.org/[MongoDB] preferably version 2.4. +In terms of document stores, http://www.mongodb.org/[MongoDB] at least 2.4, preferably version 2.6. == Additional Help Resources diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc index 252c72b11..45c5508d2 100644 --- a/src/main/asciidoc/reference/mongo-repositories.adoc +++ b/src/main/asciidoc/reference/mongo-repositories.adoc @@ -212,11 +212,11 @@ NOTE: Note that for version 1.0 we currently don't support referring to paramete | `Within` | `findByLocationWithin(Circle circle)` -| `{"location" : {"$within" : {"$center" : [ [x, y], distance]}}}` +| `{"location" : {"$geoWithin" : {"$center" : [ [x, y], distance]}}}` | `Within` | `findByLocationWithin(Box box)` -| `{"location" : {"$within" : {"$box" : [ [x1, y1], x2, y2]}}}True` +| `{"location" : {"$geoWithin" : {"$box" : [ [x1, y1], x2, y2]}}}` | `IsTrue, True` | `findByActiveIsTrue()` diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index 41f15df34..9f655cdef 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1041,9 +1041,9 @@ As you can see most methods return the `Criteria` object to provide a fluent sty There are also methods on the Criteria class for geospatial queries. Here is a listing but look at the section on <> to see them in action. -* `Criteria` *withinCenter* `(Circle circle)` Creates a geospatial criterion using `$within $center` operators -* `Criteria` *withinCenterSphere* `(Circle circle)` Creates a geospatial criterion using `$within $center` operators. This is only available for MongoDB 1.7 and higher. -* `Criteria` *withinBox* `(Box box)` Creates a geospatial criterion using a `$within $box` operation `` +* `Criteria` *within* `(Circle circle)` Creates a geospatial criterion using `$geoWithin $center` operators. +* `Criteria` *within* `(Box box)` Creates a geospatial criterion using a `$geoWithin $box` operation. +* `Criteria` *withinSphere* `(Circle circle)` Creates a geospatial criterion using `$geoWithin $center` operators. * `Criteria` *near* `(Point point)` Creates a geospatial criterion using a `$near `operation * `Criteria` *nearSphere* `(Point point)` Creates a geospatial criterion using `$nearSphere$center` operations. This is only available for MongoDB 1.7 and higher. * `Criteria` *maxDistance* `(double maxDistance)` Creates a geospatial criterion using the `$maxDistance` operation, for use with $near. @@ -1073,7 +1073,7 @@ The query methods need to specify the target type T that will be returned and th [[mongo.geospatial]] === GeoSpatial Queries -MongoDB supports GeoSpatial queries through the use of operators such as `$near`, `$within`, and `$nearSphere`. Methods specific to geospatial queries are available on the `Criteria` class. There are also a few shape classes, `Box`, `Circle`, and `Point` that are used in conjunction with geospatial related `Criteria` methods. +MongoDB supports GeoSpatial queries through the use of operators such as `$near`, `$within`, `geoWithin` and `$nearSphere`. Methods specific to geospatial queries are available on the `Criteria` class. There are also a few shape classes, `Box`, `Circle`, and `Point` that are used in conjunction with geospatial related `Criteria` methods. To understand how to perform GeoSpatial queries we will use the following Venue class taken from the integration tests.which relies on using the rich `MappingMongoConverter`. @@ -1122,7 +1122,7 @@ To find locations within a `Circle`, the following query can be used. ---- Circle circle = new Circle(-73.99171, 40.738868, 0.01); List venues = - template.find(new Query(Criteria.where("location").withinCenter(circle)), Venue.class); + template.find(new Query(Criteria.where("location").within(circle)), Venue.class); ---- To find venues within a `Circle` using spherical coordinates the following query can be used @@ -1131,7 +1131,7 @@ To find venues within a `Circle` using spherical coordinates the following query ---- Circle circle = new Circle(-73.99171, 40.738868, 0.003712240453784); List venues = - template.find(new Query(Criteria.where("location").withinCenterSphere(circle)), Venue.class); + template.find(new Query(Criteria.where("location").withinSphere(circle)), Venue.class); ---- To find venues within a `Box` the following query can be used @@ -1141,7 +1141,7 @@ To find venues within a `Box` the following query can be used //lower-left then upper-right Box box = new Box(new Point(-73.99756, 40.73083), new Point(-73.988135, 40.741404)); List venues = - template.find(new Query(Criteria.where("location").withinBox(box)), Venue.class); + template.find(new Query(Criteria.where("location").within(box)), Venue.class); ---- To find venues near a `Point`, the following query can be used