本次要实现的功能:
根据用户所在的地点,搜索店铺,根据距离由近到远的展示。
关于Geo-Redis的基本知识不在赘述,可以参考资料:如何实现查找附近的人-GEO-腾讯云开发者社区-腾讯云 (tencent.com)https://cloud.tencent.com/developer/article/2381382?areaId=106001我们以店铺不同的类型typeId为key,把店铺的经纬度,还有id存入Redis
@Test
void add() {
// 所有店铺
List<Shop> list = service.list();
// 店铺根据类型分类:美食的一类,ktv的一类
Map<Long, List<Shop>> collect = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
for (Map.Entry<Long, List<Shop>> entry : collect.entrySet()) {
Long key = entry.getKey();
List<Shop> value = entry.getValue();
value.forEach(shop -> {
stringRedisTemplate.opsForGeo()
.add(RedisConstants.SHOP_GEO_KEY+key,new Point(shop.getX(),shop.getY()),shop.getId().toString());
});
}
}
需要关注的点:
1.关于opsForGeo().radius里面有一个Circle的参数,diantance的距离单位默认是英里,所以我们要选择一下;
2.opsForGeo().radius()的limit限定了我们获取多少个,但不符合分页,用stream流的skip实现
3.我们是通过Map把店铺和对应的距离联系起来的
4.关于分页查询时,数据不够的问题要判断一下
5.根据ids批量查询shop时,要用到ORDER BY FIELD确保顺序是按照解析出来的id顺序查询的
/**
* 根据坐标查询周围店铺
* @param typeId
* @param current
* @param x
* @param y
* @return
*/
@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
//先判断是否需要根据范围查询
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());
}
//计算分页页码
int from = (current-1) * SystemConstants.DEFAULT_PAGE_SIZE;
int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
// limit的end:代表数据从0查到end结束,
// 并没有按照我们的期望即 查到d的数据从form开始end结束(完成分页)
// 为了达到分页的目的,我们用stream流的skip实现
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = stringRedisTemplate.opsForGeo().radius(
RedisConstants.SHOP_GEO_KEY + typeId,
new Circle(new Point(x,y),new Distance(5000,Metrics.NEUTRAL)),
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(end)
);
if(radius == null) {
return Result.ok(Collections.emptyList());
}
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = radius.getContent();
if(list.size() <= from) {
// 没有下一页了
return Result.ok(Collections.emptyList());
}
//存放解析到的ShopId
List<Long> listId = new ArrayList<>(list.size());
//把店铺id 和 对应的距离联系起来
Map<String,Distance> map= new HashMap<>(list.size());
list.stream().skip(from).forEach(result ->{
//获取店铺id
String Shopid = result.getContent().getName();
listId.add(Long.valueOf(Shopid));
Distance distance = result.getDistance();
map.put(Shopid,distance);
});
List<Shop> shopList = shopMapper.queryByIds(listId);
//店铺Set上距离
shopList.forEach(shop -> {
shop.setDistance(map.get(shop.getId().toString()).getValue());
});
//返回结果
return Result.ok(shopList);
}