(黑马点评)七、附近商户系列功能实现

news/2024/9/20 0:18:27 标签: redis, 学习, 数据库, 黑马点评, spring boot

 7.1 GEO数据结构的认识及其基本使用演示

7.1.1 GEO的介绍

GEO,代表地理坐标Redis3.2版本中加入了对GEO的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

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

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

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

GEOPOS:返回指定member的坐标

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

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

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

7.1.2 GEO的基本使用演示

通过 GEOADD  group x y member 添加经纬坐标

以下是通过ADD命令添加的三条数据:


北京南站( 116.378248 39.865275 )
北京站( 116.42803 39.903738 )
北京西站( 116.322287 39.893729 )

通过GEODIST group a地 b地 单位,计算两点间直线距离 

以下,分别计算北京南到北京西、北京站到北京西的距离

通过GEOSEARCH ,计算附近xx距离的所有点的信息

以下是搜索天安门附近10km火车站

7.2 附近商户搜索功能的实现

7.2.1 需求分析及接口说明

        这个需求需要使用GEO数据结构去统计以当前登录用户的地理位置为中心,向外扩散一段距离的店铺信息。同时,为了更好的对商店信息进行管理,我们需要先将店铺信息按类型存储到Redis中。

 

7.2.2 功能实现说明

1. 编写测试类,提前将店铺坐标信息存入Redis中
@Test
    void LoadShopData(){
        //1. 查询店铺信息
        List<Shop> list = shopService.list();

        //2. 把店铺按照typeId分组
        Map<Long,List<Shop>> shopMap = list.stream().collect(Collectors.groupingBy(shop -> shop.getTypeId()));

        //3. 分批写入redis
        for(Map.Entry<Long,List<Shop>> entry : shopMap.entrySet()) {
            //3.1 获取类型id
            Long typeId = entry.getKey();
            String key = RedisConstants.SHOP_GEO_KEY + typeId;

            //3.2 获取同类型的店铺列表
            List<Shop> shops = entry.getValue();
            // Redis 将 name 和 Point 封装在一起的一种用法
            List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>();

            //3.3 写入Redis GEOADD key x y member
            for(Shop shop : shops){
//                stringRedisTemplate.opsForGeo().add(key,new Point(shop.getX(),shop.getY()),shop.getId().toString());

                // 先把所有的点存好,再一次性存入Redis,性能更好
                locations.add(new RedisGeoCommands.GeoLocation<>(
                        shop.getId().toString(),
                        new Point(shop.getX(),shop.getY())
                ));
            }
            stringRedisTemplate.opsForGeo().add(key, locations);
        }

    }

分类存放:

2. 更换依赖版本

原先的依赖没办法使用GEO的最新语法,我们需要在配置文件中进行版本更替

            <!--排除旧版本-->
            <exclusions>
                <exclusion>
                    <artifactId>lettuce-core</artifactId>
                    <groupId>io.lettuce</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>spring-data-redis</artifactId>
                    <groupId>org.springframework.data</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--引入新版本依赖-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>6.1.6.RELEASE</version>
        </dependency>

【推荐插件】Maven helper

用它可以更加方便的进行包管理

3. 修改queryShopByType接口,实现分类分页查询功能

需要传入的参数变成四个

/**
     * 根据商铺类型分页查询商铺信息
     * @param typeId 商铺类型
     * @param current 页码
     * @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);
    }

实现类中:

【步骤说明】

1. 根据传参判断本次查询任务需不需要坐标,如果不需要,那就直接按照标准分页查询去做

2. 需要查坐标,则计算分页参数:

        包括: 起始值 from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;

                    终点值 end = current * SystemConstants.DEFAULT_PAGE_SIZE;

3. 在Redis中使用查询店铺地址信息

opsForGeo().search(...RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs()
        .includeDistance()
        .limit(end)) 

4. 使用getContent()方法解析出店铺信息、距离信息

5. 使用stream流截取from 到 end 部分的数据进行遍历

6. 获取店铺id、距离信息分装成一一对应的Map集合

7. 通过ids集合,查询店铺信息集合

8. 通过Map集合,匹配店铺信息结合与距离信息

9. 返回结果

      

/**
     * 根据类型分页分类查询店铺信息
     * @param typeId
     * @param current
     * @param x
     * @param y
     * @return
     */
    @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 from = (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()
                .search(
                        key,
                        GeoReference.fromCoordinate(x,y),
                        new Distance(Shop_FEO_DISTANCE),
                        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() <= from){
            return Result.ok(Collections.emptyList());
        }

        //4.1 截取 from 到 end 的部分
//        list.subList(from,end);
        // 定义一个集合,保存店铺id和距离的关系
        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. 查询店铺
        List<Shop> shops = query().in("id",ids)
                .last("ORDER BY FIELD(id," + StrUtil.join(",",ids) + ")")
                .list();

        // 匹配店铺距离
        for(Shop shop : shops) {
            shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
        }

        //6. 返回结果
        return Result.ok(shops);
    }


http://www.niftyadmin.cn/n/5666368.html

相关文章

#nginx配置案例

示例配置 1&#xff1a;反向代理 负载均衡 缓存控制 http {# 定义后端服务器池&#xff0c;用于负载均衡upstream backend_servers {server backend1.example.com weight3; # 权重为3server backend2.example.com weight1; # 权重为1server backend3.example.com backup; …

vue2+js项目升级vue3项目流程

Vue 3 相较于 Vue 2 在性能、特性和开发体验上都有了显著的提升。升级到 Vue 3 可以让你的项目受益于这些改进。但是&#xff0c;升级过程也需要谨慎&#xff0c;因为涉及到代码的重构和潜在的兼容性问题。 1. 升级前的准备 备份项目&#xff1a; 在开始升级之前&#xff0c;…

【Python报错已解决】ModuleNotFoundError: No module named ‘sklearn‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

L67 【哈工大_操作系统】操作系统历史 学习任务

L6 操作系统历史 线条一 1、上古神机 IBM7094 专注于计算批处理操作系统&#xff08;Batch system&#xff09; 2、OS/360 一台计算机干多种事&#xff0c;多道程序作业之间的 切换和调度 成为核心 &#xff08;多进程结构和进程管理概念萌芽&#xff01;&#xff09; 3…

【新手上路】衡石分析平台使用手册-认证方式

认证方式​ 用户登录衡石系统时&#xff0c;系统需要对输入的用户名和密码进行验证&#xff0c;保证系统的安全。衡石提供 CAS、SAML2、OAUTH2等多种单点登录认证方式。在 SSO 单点登录中&#xff0c;衡石是服务提供者 SP&#xff08;Service Provider&#xff09;为用户提供所…

JAVA虚拟机----JVM

(一)认识JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。 常⻅的虚拟机&#xff1a;JVM、VMwave、Virtual Box。 &#xff08;二&#xff09;JVM运…

GUI编程16:图片按钮、单选框、多选框

视频链接&#xff1a;18、图片按钮、单选框、多选框_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p18&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.图片按钮代码示例 package com.yundait.lesson05;import javax.swing.*; import java.awt.*; impo…

运维最难的是哪些部分呢

在讨论运维工作中&#xff0c;监控通常被视为一个核心且至关重要的环节&#xff0c;但它是否可以被简单地定义为“最难”的工作&#xff0c;则取决于多种因素&#xff0c;包括但不限于技术复杂度、资源投入、团队结构、业务特性以及故障应对的及时性等。以下是对运维中监控工作…