One very handy Data Structure when it comes to Redis is the GeoHash Data structure. Essentially it is a sorted set that generates a score based on the longitude and latitude.
We will spin up a Redis database using Compose
services: redis: image: redis ports: - 6379:6379
Can be run like this
docker compose up
You can find more on Compose on the Developers Essential Guide to Docker Compose.
Letβs add our dependencies
<?xml version="1.0" encoding="UTF-8"?> <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"> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> <modelVersion>4.0.0</modelVersion> <artifactId>location-service</artifactId> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.7.5</version> </dependency> </dependencies> </project>
We shall start with our Configuration. For convenience on injecting we shall create a GeoOperations<String,String> bean.
package org.location;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfiguration {
@Bean
public GeoOperations<String,String> geoOperations(RedisTemplate<String,String> template) {
return template.opsForGeo();
}
}Our model would be this one
package org.location;
import lombok.Data;
@Data
public class Location {
private String name;
private Double lat;
private Double lng;
}This simple service will persist venue locations and also fetch venues nearby of a location.
package org.location;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.domain.geo.GeoLocation;
import org.springframework.stereotype.Service;
@Service
public class GeoService {
public static final String VENUS_VISITED = "venues_visited";
private final GeoOperations<String, String> geoOperations;
public GeoService(GeoOperations<String, String> geoOperations) {
this.geoOperations = geoOperations;
}
public void add(Location location) {
Point point = new Point(location.getLng(), location.getLat());
geoOperations.add(VENUS_VISITED, point, location.getName());
}
public List<String> nearByVenues(Double lng, Double lat, Double kmDistance) {
Circle circle = new Circle(new Point(lng, lat), new Distance(kmDistance, Metrics.KILOMETERS));
GeoResults<RedisGeoCommands.GeoLocation<String>> res = geoOperations.radius(VENUS_VISITED, circle);
return res.getContent().stream()
.map(GeoResult::getContent)
.map(GeoLocation::getName)
.collect(Collectors.toList());
}
}We shall also add a controller
package org.location;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LocationController {
private final GeoService geoService;
public LocationController(GeoService geoService) {
this.geoService = geoService;
}
@PostMapping("/location")
public ResponseEntity<String> addLocation(@RequestBody Location location) {
geoService.add(location);
return ResponseEntity.ok("Success");
}
@GetMapping("/location/nearby")
public ResponseEntity<List<String>> locations(Double lng, Double lat, Double km) {
List<String> locations = geoService.nearByVenues(lng, lat, km);
return ResponseEntity.ok(locations);
}
}Then letβs add an element.
curl --location --request POST 'localhost:8080/location' \
--header 'Content-Type: application/json' \
--data-raw '{
"lng": 51.5187516,
"lat":-0.0814374,
"name": "liverpool-street"
}'Letβs retrieve the element of the api
curl --location --request GET 'localhost:8080/location/nearby?lng=51.4595573&lat=0.24949&km=100' > [ "liverpool-street" ]
And also letβs check redis
ZRANGE venues_visited 0 -1 WITHSCORES 1) "liverpool-street" 2) "2770072452773375"
We did it, pretty convenient for our day to day distance use cases.
Published on Java Code Geeks with permission by Emmanouil Gkatziouras, partner at our JCG program. See the original article here: Use Redis GeoHash with Spring boot Opinions expressed by Java Code Geeks contributors are their own. |
Thank you!
We will contact you soon.
Emmanouil GkatziourasNovember 17th, 2022Last Updated: November 15th, 2022

This site uses Akismet to reduce spam. Learn how your comment data is processed.