#64 - Add example for GeoJSON usage in MongoDB.

Add sample to show usage of GeoJSON within domain types and repository query methods.
This commit is contained in:
Christoph Strobl
2015-03-09 15:20:58 +01:00
committed by Oliver Gierke
parent df7fcb0edb
commit 35596a9045
10 changed files with 1716 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ We have separate folders for the samples of individual modules:
* `example` - Example project for general repository functionality (including geo-spatial functionality), Querydsl integration and advanced topics.
* `aggregation` - Example project to showcase the MongoDB aggregation framework support.
* `text-search` - Example project showing usage of MongoDB text search feature.
* `geo-json` - Example project showing usage of [GeoJSON](http://geojson.org) with MongoDB.
## Spring Data REST

View File

@@ -0,0 +1,81 @@
# Spring Data MongoDB - GeoJSON examples
This project contains samples of [GeoJSON](http://geojeson.org) specific features of Spring Data (MongoDB).
## Support for GeoJSON types in domain classes
Using [GeoJSON](http://geojeson.org) types in domain classes is straight forward. The `org.springframework.data.mongodb.core.geo` package contains types like `GeoJsonPoint` or `GeoJsonPolygon` which are extensions to the existing `org.springframework.data.geo` types.
Please read the [MongoDB manual on GeoJSON support](http://docs.mongodb.org/manual/core/2dsphere/#geospatial-indexes-store-geojson) to learn about requirements and restrictions.
```java
public class Store {
String id;
/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}
```
## Support for GeoJSON types in repository methods
Using GeoJson types as repository query parameters forces usage of the `$geometry` operator when creating the query.
```java
public interface StoreRepository extends CrudRepository<Store, String> {
List<Store> findByLocationWithin(Polygon polygon);
}
```
```java
/*
* {
* "location": {
* "$geoWithin": {
* "$geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [-73.992514,40.758934],
* [-73.961138,40.760348],
* [-73.991658,40.730006],
* [-73.992514,40.758934]
* ]
* ]
* }
* }
* }
* }
*/
repo.findByLocationWithin(
new GeoJsonPolygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006),
new Point(-73.992514, 40.758934)));
/*
* {
* "location" : {
* "$geoWithin" : {
* "$polygon" : [ [ -73.992514, 40.758934 ] , [ -73.961138, 40.760348 ] , [ -73.991658, 40.730006 ] ]
* }
* }
* }
*/
repo.findByLocationWithin(
new Polygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006));
```

34
mongodb/geo-json/pom.xml Normal file
View File

@@ -0,0 +1,34 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.data.examples</groupId>
<artifactId>spring-data-mongodb-examples</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-data-mongodb-geojson</artifactId>
<name>Spring Data MongoDB - GeoJson specific features</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-data-mongodb-utils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,80 @@
/*
* 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 example.springdata.mongodb;
import static com.fasterxml.jackson.databind.DeserializationFeature.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.geo.GeoModule;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.repository.init.AbstractRepositoryPopulatorFactoryBean;
import org.springframework.data.repository.init.Jackson2RepositoryPopulatorFactoryBean;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Christoph Strobl
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
/**
* Read JSON data from disk and insert those stores.
*
* @return
*/
public @Bean AbstractRepositoryPopulatorFactoryBean repositoryPopulator() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new GeoJsonModule());
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
Jackson2RepositoryPopulatorFactoryBean factoryBean = new Jackson2RepositoryPopulatorFactoryBean();
factoryBean.setResources(new Resource[] { new ClassPathResource("starbucks-in-nyc.json") });
factoryBean.setMapper(mapper);
return factoryBean;
}
/**
* Creates a new {@link GeoJsonModule} registering mixins for common and MongoDB geo-spatial types. <br />
* This should be part of Spring Data MongoDB.
*/
static class GeoJsonModule extends GeoModule {
private static final long serialVersionUID = 6239912797617786302L;
public GeoJsonModule() {
super();
setMixInAnnotation(GeoJsonPoint.class, GeoJsonPointMixin.class);
}
}
static abstract class GeoJsonPointMixin {
GeoJsonPointMixin(@JsonProperty("longitude") double x, @JsonProperty("latitude") double y) {}
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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 example.springdata.mongodb;
import lombok.Data;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* @author Christoph Strobl
*/
@Data
@Document(collection = "starbucks")
public class Store {
String id;
String name;
String street;
String city;
/**
* location is stored in GeoJSON format.
*
* <pre>
* <code>
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
* </code>
* </pre>
*/
GeoJsonPoint location;
}

View File

@@ -0,0 +1,30 @@
/*
* 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 example.springdata.mongodb;
import java.util.List;
import org.springframework.data.geo.Polygon;
import org.springframework.data.repository.CrudRepository;
/**
* @author Christoph Strobl
*/
public interface StoreRepository extends CrudRepository<Store, String> {
List<Store> findByLocationWithin(Polygon polygon);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/*
* 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 example.springdata.mongodb;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Polygon;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.geo.GeoJson;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.util.Version;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import example.springdata.mongodb.util.RequiresMongoDB;
/**
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { App.class })
public class StoreRepositoryTests {
public static @ClassRule RequiresMongoDB requiresMongoDB_2_6 = RequiresMongoDB.atLeast(new Version(2, 6, 0));
@Autowired StoreRepository repo;
@Autowired MongoOperations mongoOps;
/**
* Get all the Starbucks stores within the triange defined by
*
* <pre>
* <ol>
* <li>43rd & Ninth</li><li>60th & First</li><li>Fresh Meadows</li>
* <ol>
* </pre>
*
* Using {@code $geoWithin} and {@code $geometry} operators.
*
* <pre>
* <code>
* {
* "location": {
* "$geoWithin": {
* "$geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [-73.992514,40.758934],
* [-73.961138,40.760348],
* [-73.991658,40.730006],
* [-73.992514,40.758934]
* ]
* ]
* }
* }
* }
* }
* </code>
*
* <pre>
*/
@Test
public void findWithinGeoJsonPolygon() {
repo.findByLocationWithin(
new GeoJsonPolygon(new Point(-73.992514, 40.758934), new Point(-73.961138, 40.760348), new Point(-73.991658,
40.730006), new Point(-73.992514, 40.758934))).forEach(System.out::println);
}
/**
* The legacy format alternative to {@link #findWithinGeoJsonPolygon()}.
*
* <pre>
* <code>
* {
* "location" : {
* "$geoWithin" : {
* "$polygon" : [ [ -73.992514, 40.758934 ] , [ -73.961138, 40.760348 ] , [ -73.991658, 40.730006 ] ]
* }
* }
* }
* </code>
*
* <pre>
*/
@Test
public void findWithinLegacyPolygon() {
repo.findByLocationWithin(
new Polygon(new Point(-73.992514, 40.758934), new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006))).forEach(System.out::println);
}
/**
* The {@code $geoIntersects} keyword is not yet available via {@link Criteria} we need to fall back to manual
* creation of the query using the registered {@link MongoConverter} for {@link GeoJson} conversion.
*/
@Test
public void findStoresThatIntersectGivenPolygon() {
DBObject geoJsonDbo = new BasicDBObject();
mongoOps.getConverter().write(
new GeoJsonPolygon(new Point(-73.992514, 40.758934), new Point(-73.961138, 40.760348), new Point(-73.991658,
40.730006), new Point(-73.992514, 40.758934)), geoJsonDbo);
BasicQuery bq = new BasicQuery(new BasicDBObject("location", new BasicDBObject("$geoIntersects", new BasicDBObject(
"$geometry", geoJsonDbo))));
mongoOps.find(bq, Store.class).forEach(System.out::println);
}
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<!-- enable debug to print out query used -->
<logger name="org.springframework.data.mongodb.core.MongoTemplate" level="debug" />
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -22,6 +22,7 @@
<module>text-search</module>
<module>java8</module>
<module>util</module>
<module>geo-json</module>
</modules>
<dependencies>