伙伴匹配引入 GEO 实现搜索附近用户
前言
鱼皮哥在直播中提出可以通过Redis GEO实现编辑距离和搜索附近用户功能,也算是是一个拓展点。这个项目我做完有一个多月了,最近在做其拓展功能顺便也把这个功能实现一波,整体的实现并不困难,学完 Redis 再看会更轻松(未学过也没事)。话不多说直接开始撸代码吧。
设计思路和流程
- 在 User(用户)表中添加两个字段 longitude(经度)和 dimension(维度),用以存储用户的经纬度坐标。因为Redis GEO 通过每个用户的经纬度坐标计算用户间的距离,同时其 Redis 数据结构为ZSET,ZSET 是一个有序的 List 类似 Java 的 SortedSet。在此场景 value 就是用户id,score 是经纬度信息( ZSET 根据 score值升序排序)。

- 在 UserVO 类中添加distance字段,用以向前端返回每个用户与自己之间的距离,类型为Double。
基本业务实现
导入各个用户经纬度数据
编写测试类导入各个用户的经纬度信息并且写入Redis中,Redis GEO会根据它计算出一个 score值。进行 Redis GEO 相关操作时可以使用 Spring Data Redis 提供现成的操作 Redis 的模板——StringRedisTemplate,注意其 Key/Value 都是String类型。
stringRedisTemplate.opsForGeo().add() 支持一次一次地传入经纬度信息,可以通过List和Map集合类型传入用户经纬度信息,这里我们用List集合。第一个参数为Redis的key,这不用过多介绍。第二个参数为List类型,泛型为RedisGeoCommands.GeoLocation<String>,其参数为用户id和Point(Point可以理解为是一个圆的一个点吧,经纬度就是x/y坐标)。
stringRedisTemplate.opsForGeo().add()传入的参数:

结果:

获取用户 id = 1 与其他用户的距离
编写一个测试类计算用户 id = 1 与其他用户之间的距离。利用stringRedisTemplate.opsForGeo().distance()方法,其主要参数为member1和member2,Metric是计算距离的单位类型。从名称就可以知道member1和member2其实就是用户1和用户2的信息,因为我们在上面用 locationList.add() 添加用户id和用户的经度坐标,所以这两个member就是用户id咯。
stringRedisTemplate.opsForGeo().distance()传入的参数:

所以写个循环就可以算出用户 id = 1 与其他用户的距离
结果:

搜索附近用户
利用现成的 stringRedisTemplate.opsForGeo().radius 方法,第一个参数依然是Redis的key,第二个参数是Circle,看代码和名称就知道其是一个圆(传入Point即圆心和圆的半径)。想象搜索附近的用户就是搜索以你为圆心,半径为搜索距离的圆内的用户。理解这些代码就能顺理成章的撸出来了,是不是不算难。
注意:搜索附近的用户会搜索到自己,所以可以加一个判断以排除自己。
结果:

应用至伙伴匹配项目
改写用户推荐接口
我的recommend接口未从Redis缓存中读取数据,而是直接走数据库读取数据,有能力的可以自己实现,我后续也会实现的。同时注意返回类型是UserVO不是User,因为我的前端展示了推荐用户和自己之间的距离。
UserController.recommendUsers:
改写匹配用户接口
UserController.matchUsers:
UserServiceImpl.matchUsers:
添加搜索附近用户接口
UserController.searchNearby:
前端改动
如果有些的不对的地方,还请各位及时指正。
