added geo spatial queries in SpatialRepository
This commit is contained in:
@@ -16,16 +16,6 @@
|
||||
|
||||
package org.springframework.data.neo4j.aspects.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.data.neo4j.aspects.Person.NAME_INDEX;
|
||||
import static org.springframework.data.neo4j.aspects.Person.persistedPerson;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -47,6 +37,14 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.neo4j.aspects.Person.NAME_INDEX;
|
||||
import static org.springframework.data.neo4j.aspects.Person.persistedPerson;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
public class IndexTest extends EntityTestBase {
|
||||
@@ -163,51 +161,59 @@ public class IndexTest extends EntityTestBase {
|
||||
}
|
||||
|
||||
@NodeEntity
|
||||
static class InvalidSpatialIndexed {
|
||||
static class InvalidSpatialIndexed1 {
|
||||
|
||||
@Indexed(indexType=IndexType.POINT, indexName = "InvalidSpatialIndexed")
|
||||
String latlon;
|
||||
@Indexed(indexType=IndexType.POINT, indexName = "InvalidSpatialIndexed1")
|
||||
String wkt;
|
||||
|
||||
public void setWkt(String wkt) {
|
||||
this.wkt = wkt;
|
||||
}
|
||||
}
|
||||
@NodeEntity
|
||||
static class InvalidSpatialIndexed2 {
|
||||
|
||||
@Indexed(indexType=IndexType.POINT)
|
||||
String latlonNoIndexName;
|
||||
String wkt;
|
||||
|
||||
public void setWkt(String wkt) {
|
||||
this.wkt = wkt;
|
||||
}
|
||||
}
|
||||
@NodeEntity
|
||||
static class InvalidSpatialIndexed3 {
|
||||
|
||||
|
||||
@Indexed(indexType=IndexType.POINT, indexName = "pointLayer")
|
||||
String latlonValid;
|
||||
|
||||
public void setLatlonNoIndexName(String latlonNoIndexName) {
|
||||
this.latlonNoIndexName = latlonNoIndexName;
|
||||
}
|
||||
String wkt;
|
||||
|
||||
public void setLatlon(String latlon) {
|
||||
this.latlon = latlon;
|
||||
}
|
||||
public void setLatlonValid(String latlonValid) {
|
||||
this.latlonValid = latlonValid;
|
||||
public void setWkt(String wkt) {
|
||||
this.wkt = wkt;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@Transactional
|
||||
public void indexAccessWithFullAndNoSpatialIndexNameShouldFail() {
|
||||
InvalidSpatialIndexed invalidIndexed = persist(new InvalidSpatialIndexed());
|
||||
InvalidSpatialIndexed1 invalidIndexed = persist(new InvalidSpatialIndexed1());
|
||||
String latlon = "POINT (55 15)";
|
||||
invalidIndexed.setLatlonNoIndexName(latlon);
|
||||
invalidIndexed.setWkt(latlon);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
@Transactional
|
||||
public void indexAccessWithDefaultSpatialIndexNameShouldFail() {
|
||||
InvalidSpatialIndexed invalidIndexed = persist(new InvalidSpatialIndexed());
|
||||
InvalidSpatialIndexed2 invalidIndexed = persist(new InvalidSpatialIndexed2());
|
||||
String latlon = "POINT (55 15)";
|
||||
invalidIndexed.setLatlon( latlon);
|
||||
invalidIndexed.setWkt( latlon);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void indexAccessWithValidSpatialIndexName() {
|
||||
InvalidSpatialIndexed invalidIndexed = persist(new InvalidSpatialIndexed());
|
||||
InvalidSpatialIndexed3 invalidIndexed = persist(new InvalidSpatialIndexed3());
|
||||
String latlon = "POINT (55 15)";
|
||||
invalidIndexed.setLatlonValid( latlon);
|
||||
invalidIndexed.setWkt( latlon);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-rest-graphdb</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.5.M02.U1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.neo4j</groupId>
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
|
||||
package org.springframework.data.neo4j.repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.search.NumericRangeQuery;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
@@ -39,6 +34,14 @@ import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
|
||||
import org.springframework.data.neo4j.support.index.NullReadableIndex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.neo4j.helpers.collection.MapUtil.map;
|
||||
|
||||
/**
|
||||
* Repository like finder for Node and Relationship-Entities. Provides finder methods for direct access, access via {@link org.springframework.data.neo4j.core.TypeRepresentationStrategy}
|
||||
* and indexing.
|
||||
@@ -48,11 +51,35 @@ import org.springframework.data.neo4j.support.index.NullReadableIndex;
|
||||
*/
|
||||
@org.springframework.stereotype.Repository
|
||||
public abstract class AbstractGraphRepository<S extends PropertyContainer, T> implements GraphRepository<T>, NamedIndexRepository<T>, SpatialRepository<T> {
|
||||
|
||||
/*
|
||||
index.query( LayerNodeIndex.WITHIN_WKT_GEOMETRY_QUERY,
|
||||
"withinWKTGeometry:POLYGON ((15 56, 15 57, 16 57, 16 56, 15 56))" );
|
||||
|
||||
hits = index.query( LayerNodeIndex.WITHIN_WKT_GEOMETRY_QUERY,
|
||||
"POLYGON ((15 56, 15 57, 16 57, 16 56, 15 56))" ); lon,lat
|
||||
assertTrue( hits.hasNext() );
|
||||
final String poly = String.format("POLYGON (())", lowerLeftLon, upperRightLon, lowerLeftLat, upperRightLat);
|
||||
*/
|
||||
|
||||
@Override
|
||||
public ClosableIterable<T> findByBoundingBox( String indexName, double lowerLeftLat,
|
||||
double lowerLeftLon, double upperRightLat, double upperRightLon )
|
||||
{
|
||||
return findAllByQuery( indexName, "bbox", String.format("[%f, %f, %f, %f]", lowerLeftLon, upperRightLon, lowerLeftLat, upperRightLat) );
|
||||
public ClosableIterable<T> findWithinWellKnownText( final String indexName, String wellKnownText) {
|
||||
return geoQuery(indexName, "withinWKTGeometry", wellKnownText);
|
||||
}
|
||||
@Override
|
||||
public ClosableIterable<T> findWithinDistance( final String indexName, final double lat, double lon, double distanceKm) {
|
||||
return geoQuery(indexName, "withinDistance", map("point", new Double[] { lon, lat}, "distanceInKm", distanceKm));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClosableIterable<T> findWithinBoundingBox(final String indexName, final double lowerLeftLat,
|
||||
final double lowerLeftLon, final double upperRightLat, final double upperRightLon) {
|
||||
return geoQuery(indexName, "bbox", format("[%s, %s, %s, %s]", lowerLeftLon, upperRightLon, lowerLeftLat, upperRightLat));
|
||||
}
|
||||
|
||||
private ClosableIterable<T> geoQuery(String indexName, String geoQuery, Object params) {
|
||||
final IndexHits<S> indexHits = getIndex(indexName,null).query(geoQuery, params);
|
||||
return new GeoNodeIndexHitsWrapper(indexHits);
|
||||
}
|
||||
|
||||
public static final ClosableIterable EMPTY_CLOSABLE_ITERABLE = new ClosableIterable() {
|
||||
@@ -353,4 +380,16 @@ public abstract class AbstractGraphRepository<S extends PropertyContainer, T> im
|
||||
}
|
||||
}
|
||||
|
||||
private class GeoNodeIndexHitsWrapper extends IndexHitsWrapper {
|
||||
public GeoNodeIndexHitsWrapper(IndexHits<S> indexHits) {
|
||||
super(indexHits);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T underlyingObjectToObject(S result) {
|
||||
final Number objectNodeId = (Number) result.getProperty("id");
|
||||
if (objectNodeId==null) return null;
|
||||
return super.underlyingObjectToObject(getById(objectNodeId.longValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,21 @@ package org.springframework.data.neo4j.repository;
|
||||
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
* Repository for spatial queries.
|
||||
* WKT is well known text format like POINT( LON LAT ) POLYGON (( LON1 LAT1 LON2 LAT2 LON3 LAT3 LON1 LAT1 ))
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Well-known_text">Well Known Text Spatial Format</a>
|
||||
* Right now requires a field: @Indexed(type = POINT, indexName = "...") String wkt;
|
||||
* inside the entity.
|
||||
*/
|
||||
public interface SpatialRepository<T> {
|
||||
ClosableIterable<T> findByBoundingBox(String indexName, double lowerLeftLat,
|
||||
double lowerLeftLon,
|
||||
double upperRightLat,
|
||||
double upperRightLon);
|
||||
ClosableIterable<T> findWithinBoundingBox(String indexName, double lowerLeftLat,
|
||||
double lowerLeftLon,
|
||||
double upperRightLat,
|
||||
double upperRightLon);
|
||||
|
||||
ClosableIterable<T> findWithinDistance( final String indexName, final double lat, double lon, double distanceKm);
|
||||
|
||||
ClosableIterable<T> findWithinWellKnownText( final String indexName, String wellKnownText);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class Person {
|
||||
|
||||
@Indexed(indexType = IndexType.POINT, indexName="personLayer")
|
||||
private String wkt;
|
||||
|
||||
|
||||
@Max(100)
|
||||
@Min(0)
|
||||
@Indexed
|
||||
@@ -168,6 +168,10 @@ public class Person {
|
||||
this.wkt = locationInWkt;
|
||||
}
|
||||
|
||||
public void setLocation(double lon, double lat) {
|
||||
this.wkt = "POINT ( "+lon+" "+lat+" )";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["+graphId+"] " + name;
|
||||
|
||||
@@ -16,18 +16,6 @@
|
||||
|
||||
package org.springframework.data.neo4j.repository;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.addToCollection;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
@@ -50,6 +38,18 @@ import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.addToCollection;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
@@ -90,8 +90,8 @@ public class GraphRepositoryTest {
|
||||
|
||||
@Test
|
||||
public void testFindIterableOfPersonWithQueryAnnotationSpatial() {
|
||||
Iterable<Person> teamMembers = personRepository.findByBoundingBox( "personLayer", 55, 15, 57, 17 );
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david, testTeam.emil));
|
||||
Iterable<Person> teamMembers = personRepository.findWithinBoundingBox("personLayer", 55, 15, 57, 17);
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package org.springframework.data.neo4j.repository;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
@@ -42,6 +38,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.internal.matchers.IsCollectionContaining.hasItems;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/repository/GraphRepositoryTest-context.xml"})
|
||||
@@ -81,8 +81,20 @@ public class SpatialGraphRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindIterableOfPersonWithQueryAnnotationSpatial() {
|
||||
Iterable<Person> teamMembers = personRepository.findByBoundingBox( "personLayer", 55, 15, 57, 17 );
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david, testTeam.emil));
|
||||
public void testFindPeopleWithinBoundingBox() {
|
||||
Iterable<Person> teamMembers = personRepository.findWithinBoundingBox("personLayer", 55, 15, 57, 17);
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindPeopleWithinPolygon() {
|
||||
Iterable<Person> teamMembers = personRepository.findWithinWellKnownText("personLayer", "POLYGON ((15 55, 15 57, 17 57, 17 55, 15 55))");
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindPeopleWithinDistance() {
|
||||
Iterable<Person> teamMembers = personRepository.findWithinDistance("personLayer", 16,56,70);
|
||||
assertThat(asCollection(teamMembers), hasItems(testTeam.michael, testTeam.david));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class TestTeam {
|
||||
|
||||
david = new Person("David", 25);
|
||||
david.setBoss(emil);
|
||||
david.setLocation( "POINT (16 56)" );
|
||||
david.setLocation( 16.5, 56.5 );
|
||||
friendShip = michael.knows(david);
|
||||
friendShip.setYears(2);
|
||||
sdg = new Group();
|
||||
|
||||
Reference in New Issue
Block a user