From 9a2e95a784cf449bdda372668ae78e8d5c06956d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 27 Jan 2017 16:41:34 +0100 Subject: [PATCH] #256 - Add examples for Redis Geo API. --- .../redis/commands/GeoOperationsTests.java | 116 ++++++++++++++++++ .../redis/repositories/Address.java | 6 +- .../redis/repositories/PersonRepository.java | 6 +- .../repositories/PersonRepositoryTests.java | 52 ++++++-- 4 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 redis/example/src/test/java/example/springdata/redis/commands/GeoOperationsTests.java diff --git a/redis/example/src/test/java/example/springdata/redis/commands/GeoOperationsTests.java b/redis/example/src/test/java/example/springdata/redis/commands/GeoOperationsTests.java new file mode 100644 index 00000000..7a0731ca --- /dev/null +++ b/redis/example/src/test/java/example/springdata/redis/commands/GeoOperationsTests.java @@ -0,0 +1,116 @@ +/* + * Copyright 2017 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.redis.commands; + +import static org.assertj.core.api.Assertions.*; + +import example.springdata.redis.test.util.RequiresRedisServer; + +import java.util.List; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.geo.Circle; +import org.springframework.data.geo.Distance; +import org.springframework.data.geo.GeoResults; +import org.springframework.data.geo.Point; +import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit; +import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; +import org.springframework.data.redis.core.GeoOperations; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Show usage of redis geo-index operations using Template API provided by {@link GeoOperations}. + * + * @author Mark Paluch + */ +@RunWith(SpringRunner.class) +@SpringBootApplication +public class GeoOperationsTests { + + // we only want to run this tests when redis is up an running + public static @ClassRule RequiresRedisServer requiresServer = RequiresRedisServer.onLocalhost(); + + @Autowired RedisOperations operations; + GeoOperations geoOperations; + + @Before + public void before() { + + geoOperations = operations.opsForGeo(); + + geoOperations.geoAdd("Sicily", new Point(13.361389, 38.115556), "Arigento"); + geoOperations.geoAdd("Sicily", new Point(15.087269, 37.502669), "Catania"); + geoOperations.geoAdd("Sicily", new Point(13.583333, 37.316667), "Palermo"); + } + + /** + * Look up points using a geo-index member as reference. + */ + @Test + public void geoRadiusByMember() { + + GeoResults> byDistance = geoOperations.geoRadiusByMember("Sicily", "Palermo", + new Distance(100, DistanceUnit.KILOMETERS)); + + assertThat(byDistance).hasSize(2).extracting("content.name").contains("Arigento", "Palermo"); + + GeoResults> greaterDistance = geoOperations.geoRadiusByMember("Sicily", "Palermo", + new Distance(200, DistanceUnit.KILOMETERS)); + + assertThat(greaterDistance).hasSize(3).extracting("content.name").contains("Arigento", "Catania", "Palermo"); + } + + /** + * Lookup points within a circle around coordinates. + */ + @Test + public void geoRadius() { + + Circle circle = new Circle(new Point(13.583333, 37.316667), // + new Distance(100, DistanceUnit.KILOMETERS)); + GeoResults> result = geoOperations.geoRadius("Sicily", circle); + + assertThat(result).hasSize(2).extracting("content.name").contains("Arigento", "Palermo"); + } + + /** + * Calculate the distance between two geo-index members. + */ + @Test + public void geoDistance() { + + Distance distance = geoOperations.geoDist("Sicily", "Catania", "Palermo", DistanceUnit.KILOMETERS); + + assertThat(distance.getValue()).isBetween(130d, 140d); + } + + /** + * Return the geo-hash. + */ + @Test + public void geoHash() { + + List geohashes = geoOperations.geoHash("Sicily", "Catania", "Palermo"); + + assertThat(geohashes).hasSize(2).contains("sqdtr74hyu0", "sq9sm1716e0"); + } +} diff --git a/redis/repositories/src/main/java/example/springdata/redis/repositories/Address.java b/redis/repositories/src/main/java/example/springdata/redis/repositories/Address.java index c73428fa..b61dfe8d 100644 --- a/redis/repositories/src/main/java/example/springdata/redis/repositories/Address.java +++ b/redis/repositories/src/main/java/example/springdata/redis/repositories/Address.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -18,10 +18,13 @@ package example.springdata.redis.repositories; import lombok.Data; import lombok.EqualsAndHashCode; +import org.springframework.data.geo.Point; +import org.springframework.data.redis.core.index.GeoIndexed; import org.springframework.data.redis.core.index.Indexed; /** * @author Christoph Strobl + * @author Mark Paluch */ @Data @EqualsAndHashCode @@ -29,4 +32,5 @@ class Address { private @Indexed String city; private String country; + private @GeoIndexed Point location; } diff --git a/redis/repositories/src/main/java/example/springdata/redis/repositories/PersonRepository.java b/redis/repositories/src/main/java/example/springdata/redis/repositories/PersonRepository.java index d2059202..15b4b479 100644 --- a/redis/repositories/src/main/java/example/springdata/redis/repositories/PersonRepository.java +++ b/redis/repositories/src/main/java/example/springdata/redis/repositories/PersonRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -19,10 +19,12 @@ import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.geo.Circle; import org.springframework.data.repository.CrudRepository; /** * @author Christoph Strobl + * @author Mark Paluch */ interface PersonRepository extends CrudRepository { @@ -35,4 +37,6 @@ interface PersonRepository extends CrudRepository { List findByFirstnameOrLastname(String firstname, String lastname); List findByAddress_City(String city); + + List findByAddress_LocationWithin(Circle circle); } diff --git a/redis/repositories/src/test/java/example/springdata/redis/repositories/PersonRepositoryTests.java b/redis/repositories/src/test/java/example/springdata/redis/repositories/PersonRepositoryTests.java index 48cf0e12..015c4cf1 100644 --- a/redis/repositories/src/test/java/example/springdata/redis/repositories/PersonRepositoryTests.java +++ b/redis/repositories/src/test/java/example/springdata/redis/repositories/PersonRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 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. @@ -15,10 +15,7 @@ */ package example.springdata.redis.repositories; -import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*; -import static org.hamcrest.core.Is.*; -import static org.hamcrest.core.IsCollectionContaining.*; -import static org.hamcrest.core.IsNot.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import example.springdata.redis.test.util.EmbeddedRedisServer; @@ -39,14 +36,20 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.geo.Circle; +import org.springframework.data.geo.Distance; +import org.springframework.data.geo.Metrics; +import org.springframework.data.geo.Point; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.index.GeoIndexed; import org.springframework.data.redis.core.index.Indexed; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = ApplicationConfiguration.class) @@ -182,6 +185,41 @@ public class PersonRepositoryTests { assertThat(eddardStark, not(hasItems(robb, sansa, arya, bran, rickon, jon))); } + /** + * Find entity by a {@link GeoIndexed} property on an embedded entity. + */ + @Test + public void findByGeoLocationProperty() { + + Address winterfell = new Address(); + winterfell.setCountry("the north"); + winterfell.setCity("winterfell"); + winterfell.setLocation(new Point(52.9541053, -1.2401016)); + + eddard.setAddress(winterfell); + + Address casterlystein = new Address(); + casterlystein.setCountry("Westerland"); + casterlystein.setCity("Casterlystein"); + casterlystein.setLocation(new Point(51.5287352, -0.3817819)); + + robb.setAddress(casterlystein); + + flushTestUsers(); + + Circle innerCircle = new Circle(new Point(51.8911912, -0.4979756), new Distance(50, Metrics.KILOMETERS)); + List eddardStark = repository.findByAddress_LocationWithin(innerCircle); + + assertThat(eddardStark, hasItem(robb)); + assertThat(eddardStark, hasSize(1)); + + Circle biggerCircle = new Circle(new Point(51.8911912, -0.4979756), new Distance(200, Metrics.KILOMETERS)); + List eddardAndRobbStark = repository.findByAddress_LocationWithin(biggerCircle); + + assertThat(eddardAndRobbStark, hasItems(robb, eddard)); + assertThat(eddardAndRobbStark, hasSize(2)); + } + /** * Store references to other entites without embedding all data.
* Print out the hash structure within Redis. @@ -200,9 +238,9 @@ public class PersonRepositoryTests { /* * Deceased: - * + * * - Robb was killed by Roose Bolton during the Red Wedding. - * - Jon was stabbed by brothers or the Night's Watch. + * - Jon was stabbed by brothers or the Night's Watch. */ repository.delete(Arrays.asList(robb, jon));