• 黑马点评--附近商铺


    附近商铺

    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.新功能

    附近商户搜索

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PVs7grnh-1669716065833)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221128181011368.png)]

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QDVF54wQ-1669716065834)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221128181642622.png)]

    导入店铺信息数据到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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注意: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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    代码实现附近商户功能:

     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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
  • 相关阅读:
    npm是如何处理多版本依赖的?
    2022年上半年部分团队的总结
    168. Excel表列名称
    1084. 销售分析III
    WPF Button去除按钮边框,添加下划线
    【leetcode】记忆化搜索
    maven根据profile变量引入或者不引入依赖,引入不同版本的依赖
    【QML】QML控件组件
    java计算机毕业设计疫情防控期间人员档案追演示录像上源码+系统+mysql数据库+lw文档
    二叉树刷题(完结篇)
  • 原文地址:https://blog.csdn.net/weixin_53050118/article/details/128102672