目录
为了支持 GEOSEARCH 命令,需要更新 Redis 版本
前言
首先需要了解 Redis 中 GEO 的使用,和GeoHash算法的基本原理
链接:Redis——GEO
附近商户搜索
将类型相同的商户分为同一组
为了支持 GEOSEARCH 命令,需要更新 Redis 版本
实现代码
加载商铺数据(含坐标)
@Test
void loadShopData(){
//1.查询商户信息
List<Shop> shops = shopService.list();
//2.把店铺分组,按照typeId分组
// Map<Long, List<Shop>> map = new Map<Long, List<Shop>>;
Map<Long,List<Shop>> map=shops.stream().collect(Collectors.groupingBy(Shop::getTypeId));
//3.分批完成写入Redis
for(Map.Entry<Long,List<Shop>> entry:map.entrySet()){
//3.1获取类型id
Long typeId=entry.getKey();
String key="shop:geo:"+typeId;
//3.2获取同类型的店铺集合
List<Shop> value=entry.getValue();
List<RedisGeoCommands.GeoLocation<String>> locations=new ArrayList<>(value.size());
//3.3写入Redis GEOADD key 经度 纬度 member
for (Shop shop : value) {
//此为一个一个添加,可以通过迭代器的方法一次性添加完成
// stringRedisTemplate.opsForGeo().add(key,new Point(shop.getX(),shop.getY()),shop.getId().toString());
locations.add(new RedisGeoCommands.GeoLocation<>(
shop.getId().toString(),new Point(shop.getX(),shop.getY())));
}
stringRedisTemplate.opsForGeo().add(key,locations);
}
}
typeId为商铺类型,current为当前页码,x,y为经纬度坐标
@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
//1.判断是否需要根据坐标查询
if(x==null||y==null){
//不需要坐标查询,按数据库查询
Page<Shop> page = query()
.eq("type_id", typeId)
.page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
return Result.ok(page.getRecords());
}
//2.计算分页参数
int begin = (current-1)*SystemConstants.DEFAULT_PAGE_SIZE;
int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
//3.查询redis、按照距离升序、分页 结果:shopId、distance
String key=SHOP_GEO_KEY+typeId;
GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo() //GEOSEARCH key BYLONLAT x y BYRADIUS 10 WITHINDISTANCE
.search(key,
GeoReference.fromCoordinate(x, y),
new Distance(5000), //5000米内
RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
);
//4.解析出id
if(results==null){
return Result.ok(Collections.emptyList());
}
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
if(list.size()<=begin){
//没有下一页,结束
return Result.ok(Collections.emptyList());
}
//4.1截取begin——end的部分
List<Long> ids=new ArrayList<>(list.size());
Map<String,Distance> distanceMap=new HashMap<>(list.size());
list.stream().skip(begin).forEach(result->{
//4.2获取店铺id
String shopIdStr = result.getContent().getName();
ids.add(Long.valueOf(shopIdStr));
//4.3获取距离
@NonNull
Distance distance = result.getDistance();
distanceMap.put(shopIdStr,distance);
});
//5.根据id查询Shop
String idStr=StrUtil.join(",",ids);
List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ") ").list();
for (Shop shop : shops) {
shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
}
//6.返回
return Result.ok(shops);
}