附近店铺
首先,先导入店铺信息到geo数据结构
@Test
void loadShopData(){
// 1.查询店铺信息
List<Shop> list = shopService.list();
// 2.把店铺分组,按照typeId分组,typeId一致的放到一个集合
Map<Long,List<Shop>> map=list.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();
// locations泛型就是下面member类型
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);
}
}
测试通过,查看redis数据库有2个key,因为只有mysql数据库只有2种类型id分别为1和2,redis中value存放店铺id,score为店铺地理位置转换为二进制存放
实现附近商户功能
这里我用的是georadius实现
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
GEORADIUS key x y 20 km WITHCOORD WITHDIST
WITHCOORD 包含成员的坐标信息
WITHDIST 包含成员距离中心点的距离
如果使用geosearch实现的话,需要修改版本号
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<groupId>lettuce-core</groupId>
<artifactId>io.lettuce</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>lettuce-core</groupId>
<artifactId>io.lettuce</artifactId>
<version>6.1.6.RELEASE</version>
</dependency>
GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>
<BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |
FT | MI>> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST]
[WITHHASH]
GEOSEARCH key BYLONLAT x y BYRADIUS 10 WITHDISTANCE
GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,
GeoReference.fromCoordinate(x,y),
new Distance(5000),
RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
);
逻辑分页困难
ShopController层
/**
* 根据商铺类型分页查询商铺信息
* @param typeId 商铺类型
* @param current 页码
* @param x 地理位置x
* @param y 地理位置y
* @return 商铺列表
*/
@GetMapping("/of/type")
public Result queryShopByType(
@RequestParam("typeId") Integer typeId,
@RequestParam(value = "current", defaultValue = "1") Integer current,
@RequestParam(value = "x",required = false) Double x,@RequestParam(value = "y",required = false) Double y
) {
return shopService.queryShopByType(typeId,current,x,y);
/*// 根据类型分页查询
Page<Shop> page = shopService.query()
.eq("type_id", typeId)
.page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
// 返回数据
return Result.ok(page.getRecords());*/
}
ShopServiceImpl层
@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 start = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
// 3.查询redis、按照距离排序、分页。结果:shopId、distance
String key=SHOP_GEO_KEY+typeId;
// 设置圆 中心点 半径 以及半径距离
Circle circle=new Circle(new Point(x,y),new Distance(100, Metrics.KILOMETERS));
// 设置所要返回的信息
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
// 设置最多显示end个成员
.limit(end)
// 包含成员的坐标信息 经纬度
.includeCoordinates()
// 包含成员距离中心点的距离
.includeDistance();
GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().radius(key, circle, args);
// 4.解析出id
if (results==null){
return Result.ok(Collections.emptyList());
}
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
if (list.size()<=start){
return Result.ok(Collections.emptyList());
}
// 4.1.截取front~end的部分
List<Long> ids=new ArrayList<>(list.size());
Map<String,Distance> distanceMap=new HashMap<>(list.size());
// 跳过部分数据
list.stream().skip(start).forEach(result->{
// 4.2.获取店铺id
String shopIdStr = result.getContent().getName();
ids.add(Long.valueOf(shopIdStr));
// 4.3.获取距离
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) {
// 想要获取的距离是double类型所以在加上getValue方法
shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
}
// 6.返回
return Result.ok(shops);
}
每次鼠标一往下滚动到结尾后就会再次发送分页查询请求显示店铺信息