0%

Redis-Geo

源码地址

Geo

Redis 在 3.2 版本中增加了 GEO 类型用于存储和查询地理位置。

  • GEOADD:添加地理位置

  • GEOPOS:查询位置信息

  • GEODIST:距离统计

  • GEORADIUS:以给定的经纬度为中心, 找出某一半径内的元素

  • GEORADIUSBYMEMBER 找出位于指定范围内的元素,中心点是由给定的位置元素决定

  • GEOHASH:返回一个或多个位置元素HASH

  • 关于删除,使用集合函数中的zrem,geo本质存在一个集合中。

演示效果

1
2
3
4
5
// 1.杭州市 蒋村商务中心 120.075338,30.294845
// 2.杭州市 中节能西溪首座 120.081806,30.294907
// 3.杭州市 西溪蝶园 120.070452,30.294221
// 4.杭州市 西溪财富中心 120.077854,30.296342
// 5.杭州市 九橙 西溪创投中心 120.055468,30.284523
  • 使用redis-cli作为客户端测试工具 geo 集合key = hz

GEOADD

  • 添加坐标信息
1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> geoadd hz 120.075338 30.294845 1 
(integer) 1
127.0.0.1:6379> geoadd hz 120.081806 30.294907 2
(integer) 1
127.0.0.1:6379> geoadd hz 120.070452 30.294221 3
(integer) 1
127.0.0.1:6379> geoadd hz 120.077854 30.296342 4
(integer) 1
127.0.0.1:6379> geoadd hz 120.055468 30.284523 5
(integer) 1
127.0.0.1:6379>

GEOPOS

  • 从键里面返回所有给定位置元素的位置(经度和纬度)
1
2
3
4
5
127.0.0.1:6379> geopos hz 1 2 
1) 1) "120.07533878087997437"
2) "30.29484408467364176"
2) 1) "120.08180826902389526"
2) "30.29490745270262408"

GEODIST

  • 返回两个给定位置之间的距离。如果两个位置之间的其中一个不存在, 那么命令返回空值。
  • 指定单位的参数 unit 必须是以下单位的其中一个
    • m 表示单位为米。
    • km 表示单位为千米。
    • mi 表示单位为英里。
    • ft 表示单位为英尺。
1
2
3
127.0.0.1:6379> geodist hz 1 2 m
"621.3522"
# 获取 1 和 2 之间的距离米
  • 百度地图的尺子测试下。差了6米问题不大。精度不高的业务够用了。

GEORADIUS

  • 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
  • 范围可以使用以下其中一个单位:
    • m 表示单位为米。
    • km 表示单位为千米。
    • mi 表示单位为英里。
    • ft 表示单位为英尺。
  • 在给定以下可选项时, 命令会返回额外的信息:
    • WITHDIST : 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
    • WITHCOORD : 将位置元素的经度和维度也一并返回。
    • WITHHASH : 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
  • 默认命令的排序
    • ASC : 根据中心的位置, 按照从近到远的方式返回位置元素。
    • DESC : 根据中心的位置, 按照从远到近的方式返回位置元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
127.0.0.1:6379> georadius hz 120.075338 30.294845 500 m 
1) "3"
2) "1"
3) "4"
# 中心坐标 120.075338 30.294845 500m内的位置。

127.0.0.1:6379> georadius hz 120.075338 30.294845 500 m WITHDIST
1) 1) "3"
2) "474.3718"
2) 1) "1"
2) "0.1264"
3) 1) "4"
2) "293.5036"
# 中心坐标 120.075338 30.294845 500m内的位置,并显示距离m。

127.0.0.1:6379> georadius hz 120.075338 30.294845 500 m WITHDIST WITHCOORD ASC
1) 1) "1"
2) "0.1264"
3) 1) "120.07533878087997437"
2) "30.29484408467364176"
2) 1) "4"
2) "293.5036"
3) 1) "120.0778546929359436"
2) "30.29634210487881063"
3) 1) "3"
2) "474.3718"
3) 1) "120.07045179605484009"
2) "30.2942205432684446"
# 中心坐标 120.075338 30.294845 500m内的位置,并显示距离,ASC排序。
  • 一般用来做附近的人(建筑等等)

GEORADIUSBYMEMBER

  • 找出位于指定范围内的元素,参数是元素而不是坐标。

  • 范围可以使用以下其中一个单位:

    • m 表示单位为米。
    • km 表示单位为千米。
    • mi 表示单位为英里。
    • ft 表示单位为英尺。
  • 在给定以下可选项时, 命令会返回额外的信息:

    • WITHDIST : 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
    • WITHCOORD : 将位置元素的经度和维度也一并返回。
    • WITHHASH : 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。
  • 默认命令的排序

    • ASC : 根据中心的位置, 按照从近到远的方式返回位置元素。
    • DESC : 根据中心的位置, 按照从远到近的方式返回位置元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> GEORADIUSBYMEMBER hz 1 500 m
1) "3"
2) "1"
3) "4"
# 元素 500m的元素

127.0.0.1:6379> GEORADIUSBYMEMBER hz 1 500 m WITHDIST WITHCOORD ASC
1) 1) "1"
2) "0.0000"
3) 1) "120.07533878087997437"
2) "30.29484408467364176"
2) 1) "4"
2) "293.4996"
3) 1) "120.0778546929359436"
2) "30.29634210487881063"
3) 1) "3"
2) "474.4311"
3) 1) "120.07045179605484009"
2) "30.2942205432684446"
# 元素 500m的元素包含完整信息。
  • 一般用来做附近的人(建筑等等)

GEOHASH

1
2
3
4
127.0.0.1:6379> GEOHASH hz 1 2 3 
1) "wtmkk7h0q00"
2) "wtmkke0bsu0"
3) "wtmkk6bssk0"

删除坐标

  • 关于删除,使用集合函数中的zrem,geo本质存在一个集合中。
1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> GEORADIUSBYMEMBER hz 1 500 m
1) "3"
2) "1"
3) "4"
# 元素 500m的元素
127.0.0.1:6379> zrem hz 4
(integer) 1
127.0.0.1:6379> GEORADIUSBYMEMBER hz 1 500 m
1) "3"
2) "1"
# 将 4 元素删除后只能看到 3 1 了。

代码演示

  • spring boot 2.4.5
  • gson

配置项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

/**
* @author z201.coding@gmail.com
* @date 2021/12/24
**/
@Configuration
public class RedisConfig {

// 防止key 出现乱码
@Bean
public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
template.setConnectionFactory(connectionFactory);
// key序列化方式
template.setKeySerializer(redisSerializer);
// value序列化
template.setValueSerializer(redisSerializer);
return template;
}

@Bean
public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory connectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(connectionFactory);
return stringRedisTemplate;
}
}

单元测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    
package cn.z201.redis;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
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;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.*;


@Slf4j
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = AppApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class AppApplicationTest {

@Autowired
RedisTemplate redisTemplate;

@Test
public void testGeo() {
// 1.杭州市 蒋村商务中心 120.075338,30.294845
// 2.杭州市 中节能西溪首座 120.081806,30.294907
// 3.杭州市 西溪蝶园 120.070452,30.294221
// 4.杭州市 西溪财富中心 120.077854,30.296342
// 5.杭州市 九橙 西溪创投中心 120.055468,30.284523
Map<String, Point> points = new HashMap<>();
points.put("1", new Point(120.075338, 30.294845));
points.put("2", new Point(120.081806, 30.294907));
points.put("3", new Point(120.070452, 30.294221));
points.put("4", new Point(120.077854, 30.296342));
points.put("5", new Point(120.055468, 30.284523));
Long result = redisTemplate.opsForGeo().add("site", points);
log.info("{}", result);
Set<String> keys = redisTemplate.keys("*");
Gson gson = new GsonBuilder().setPrettyPrinting().create();
log.info("size -> {} ", keys.size());
List<Point> pointList = redisTemplate.opsForGeo().position("site", "1", "2");
log.info("pointList -> {} ", gson.toJson(pointList));
/**
* METERS(6378137, "m"),
* KILOMETERS(6378.137, "km"),
* MILES(3963.191, "mi"),
* FEET(20925646.325, "ft");
*/
Distance distance = redisTemplate.opsForGeo().distance("site", "1", "2", RedisGeoCommands.DistanceUnit.KILOMETERS);
log.info("distance -> {} ", gson.toJson(distance));
Circle circle = new Circle(new Point(120.075338, 30.294845), new Distance(500, RedisGeoCommands.DistanceUnit.KILOMETERS));
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates()
.sortAscending();
log.info("geoLocationGeoResults -> {} ", gson.toJson(args));
GeoResults<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResults = redisTemplate.opsForGeo().radius("site", circle, args);
log.info("geoLocationGeoResults -> {} ", gson.toJson(geoLocationGeoResults));
keys.forEach(item -> {
if (!Objects.equals("hz", item)) {
redisTemplate.delete(item);
}
log.info(" keys - > {} ", item);
}
);
}
}
  • GEORADIUSBYMEMBER 方法过期了,这里就不演示了。

End