当涉及到Redis时,一种非常方便的数据结构是GeoHash数据结构。本质上它是一个排序集,根据经度和纬度生成一个分数。
我们将使用 Compose 启动一个 Redis 数据库:
services: redis: image: redis ports: - 6379:6379
|
运行:docker compose up依赖:
<?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>
|
我们将从配置开始。为了方便注入,我们将创建一个 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(); } }
|
模型:
package org.location; import lombok.Data; @Data public class Location { private String name; private Double lat; private Double lng; }
|
这个简单的服务将保留场地位置并获取位置附近的场地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()); } }
|
添加控制器:
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); } }
|
测试:
加入数据:
curl --location --request POST 'localhost:8080/location' \ --header 'Content-Type: application/json' \ --data-raw '{ "lng": 51.5187516, "lat":-0.0814374, "name": "liverpool-street" }'
|
查数据:
curl --location --request GET 'localhost:8080/location/nearby?lng=51.4595573&lat=0.24949&km=100' > [ "liverpool-street" ]
|
检查Redis:
ZRANGE venues_visited 0 -1 WITHSCORES 1) "liverpool-street" 2) "2770072452773375"
|