黑马点评--附近商铺

附近商铺

GEO数据结构

GEO就是Geolocation的简写形式,代表地理坐标。Redis在3.2版本加入了对GEO的支持,允许存储地理坐标消息,帮助我们根据经纬度来检索数据。常见的命令有:

GEOADD:添加一个地理空间信息,包含:经度(longitude),纬度(latitude),值(member)

GEODIST:计算指定的两个点之间的距离并返回

GEOHASH:将指定member的坐标转为hash字符串形式并返回

GEOPOS:返回指定member的坐标

GEOPADIUS:指定圆心,半径,找到该圆内包含的所有member,并按照与圆心之间的距离排序后返回。6.2以后已废弃

GEOSEARCH:在指定范围内搜索member,并按照与指定点之间的距离排序后返回。范围可以是圆形或矩形。6.2新功能

GEOSEARCHSTORE:与GEOSEARCH功能一致,不过可以把结果存储到一个指定的key。6.2.新功能

附近商户搜索

黑马点评--附近商铺_第1张图片

按照商户类型做分组,类型相同的商户作为同一组,以typeid为key存入同一GEO集合中即可

黑马点评--附近商铺_第2张图片

导入店铺信息数据到GEO

//    导入店铺信息数据到GEO
    @Test
    void loadShopData(){
        //1.查询店铺信息
        List<Shop> list = shopService.list();
        //2.把店铺分组,按照typeId分组,id一致的放到一个集合
        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();
            //3.2获取同类型的店铺的集合
            List<Shop> shopList = entry.getValue();
            List<RedisGeoCommands.GeoLocation<String>> locations =new ArrayList<>(shopList.size());
            //3.3写人redis GEOADD key 经度 纬度 member
            for (Shop shop : shopList) {
                /*stringRedisTemplate.opsForGeo()
                        .add(SHOP_GEO_KEY+ typeId,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(SHOP_GEO_KEY+ typeId,locations);
        }
    }
}

注意:SpringDataRedis的2.3.9版本并不支持Redis6.2提供的GEOSEARCH命令,因此我们需要提升其版本,修改自己的pom文件,内容如下:

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.datagroupId>
                    <artifactId>spring-data-redisartifactId>
                exclusion>
                <exclusion>
                    <groupId>io.lettucegroupId>
                    <artifactId>lettuce-coreartifactId>
                exclusion>
            exclusions>
        dependency>
        <dependency>
            <groupId>org.springframework.datagroupId>
            <artifactId>spring-data-redisartifactId>
            <version>2.6.2version>
        dependency>
        <dependency>
            <groupId>io.lettucegroupId>
            <artifactId>lettuce-coreartifactId>
            <version>6.1.9.RELEASEversion>
        dependency>

代码实现附近商户功能:

 public Result getShopByType(Integer typeId, Integer current, Double x, Double y) {
        //1.判断是否需要根据坐标查询
        if (x == null || y == null){
            // 不需要坐标查询,按数据库查询
            Page<Shop> page = new Page<>(current, DEFAULT_BATCH_SIZE);
            LambdaQueryWrapper<Shop> queryWrapper=new LambdaQueryWrapper<>();
            queryWrapper.eq(Shop::getTypeId,typeId);
            Page<Shop> shopPage = page(page, queryWrapper);
            return Result.ok(shopPage.getRecords());
        }
        //2.计算分页参数
        int from =(current - 1) * DEFAULT_BATCH_SIZE;
        int end =current *DEFAULT_BATCH_SIZE;
        //3.查询redis,按照距离排序,分页,结果:shopId,distance
        //GEOSEARCH BYLONLAT x y BYRADIUS 10 WITHDISTANCE
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(
                SHOP_GEO_KEY + typeId,
                GeoReference.fromCoordinate(x, y),
                new Distance(5000),
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
        );
        //4.解析出id
        //判空
        if (results == null || CollectionUtil.isEmpty(results)){
            return Result.ok(Collections.emptyList());
        }
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
        if (list.size()<=from){
            //没有下一页,结束
            return Result.ok(Collections.emptyList());
        }
        //4.1截取from - end 的部分
        List<Long> ids =new ArrayList<>(list.size());
        Map<String,Distance> distanceMap =new HashMap<>(list.size());
        list.stream().skip(from).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
        LambdaQueryWrapper<Shop> queryWrapper=new LambdaQueryWrapper<>();
        String idStr =StrUtil.join(",",ids);
        queryWrapper.in(Shop::getId,ids).last("ORDER BY FIELD(id,"+idStr+")");
        List<Shop> shopList = list(queryWrapper);
        for (Shop shop : shopList) {
            shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
        }
        //6.返回
        return  Result.ok(shopList);
    }

你可能感兴趣的:(数据库,java,redis)