Redis实战案例25-附近商铺功能

1. GEO数据结构

Redis中Geohash功能应用
在这里插入图片描述
在这里插入图片描述

添加地理坐标

在这里插入图片描述
在这里插入图片描述

求两点之间距离

在这里插入图片描述

搜索天安门附近10km的火车站,按升序

在这里插入图片描述

2. 导入店铺数据到GEO

在这里插入图片描述

Redis中存储店铺的信息,将店铺的id和经纬度坐标存到GEO数据类型中去,其中member存id,经纬度对应x和y;
存储对应店铺信息之后,才能去计算对应的距离排序,之后得出附近店铺功能时,得到就是id,根据id查询店铺即可;

但是这里存储在GEO中的只有店铺id和地理坐标,并没有类型id,所以要对商铺类型进行分类;
在这里插入图片描述

这里直接写测试进行商户类型分组了,写到Redis中去

/**
 * 进行店铺分组
 */
@Test
void loadShopData(){
    //查询店铺信息
    List<Shop> list = shopService.list();
    //把店铺分组,按照typeId分组,一致的放到一个集合中
    Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
    //分批写入Redis中
    for (Map.Entry<Long, List<Shop>> entry:map.entrySet()) {
        //获取类型id
        Long typeId = entry.getKey();
        String key = SHOP_GEO_KEY + typeId;
        //获取同类型的店铺的集合
        List<Shop> value = entry.getValue();
        ArrayList<RedisGeoCommands.GeoLocation<String>> geoLocations = new ArrayList<RedisGeoCommands.GeoLocation<String>>(value.size());
        //写入Redis GEOADD key 经度 纬度 member
        for (Shop shop:value) {
            //stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getY()), shop.getId().toString());
            geoLocations.add(new RedisGeoCommands.GeoLocation<>(
                    shop.getId().toString(),
                    new Point(shop.getX(),shop.getY())
            ));
        }
        stringRedisTemplate.opsForGeo().add(key, geoLocations);
    }
}

在这里插入图片描述

3. 附近商铺搜索

在这里插入图片描述

x和y参数采用required = false,传了参数则采用GEO方式查,没传参数则采用别的方式查询排序

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

有点难,等以后有水平了再来修改吧~

/**
 * 根据商铺类型分页查询商铺信息
 * @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(5000),
            //limit是限制范围,但是只能指定结束,都是从第一条开始到结束,只能对结果手动截取
            //默认单位为m,当前为5km,结果也是m
            RedisGeoCommands.GeoRadiusCommandArgs.newGeoSearchArgs().includeCoordinates().limit(end)
    );
    //4.解析出id
    if(results == null){
        return Result.ok(Collections.emptyList());
    }
    //       当前集合从0到end,需要手动截取        list.subList() 或者stream流
    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 -> {
        //参见test测试单元的存储过程
        //获取店铺id
        String shopId = result.getContent().getName();
        //需要把id转换为long类型的进行查询店铺信息
        ids.add(Long.valueOf(shopId));
        //获取距离
        Distance distance = result.getDistance();
        //保证id与distance一一对应
        distanceMap.put(shopId,distance);
    });
    //5.根据id查询店铺
    String idStr = StrUtil.join(",", ids);
    List<Shop> shops = query().in("id", ids)
            .last("ORDER BY FIELD(id," + idStr + ")").list();//        6.返回
    //在实体类,distance是只属于实体类,用于返回给前端的字段
    for (Shop shop : shops) {
        //因为集合有存储,所以根据id来取出值,但是取出的是对象,需要调用getValue来转成相应的值
        shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
    }
    return Result.ok(shops);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/65471.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker实战-操作Docker容器实战(二)

导语   上篇分享中,我们介绍了关于如何创建容器、如何启动容器、如何停止容器。这篇我们来分享一下如何操作容器。 如何进入容器 可以通过使用-d参数启动容器后会进入后台运行,用户无法查看容器中的信息,无法对容器中的信息进行操作。 这个时候如果我们需要进入容器对容器…

C语言经典小游戏之扫雷(超详解释+源码)

“少年气&#xff0c;是历尽千帆举重若轻的沉淀&#xff0c;也是乐观淡然笑对生活的豁达&#xff01;” 今天我们学习一下扫雷游戏怎么用C语言来实现&#xff01; 扫雷小游戏 1.游戏介绍2.游戏准备3.游戏实现3.1生成菜单3.2游戏的具体实现3.2.1初始化棋盘3.2打印棋盘3.3布置雷…

黑马头条项目学习--Day1: 环境搭建、SpringCloud微服务(注册发现、网关)

Nacos注册发现、网关 a. 项目介绍b. app登录1) 需求分析2) 表结构分析3) 手动加密&#xff08;md5随机字符串&#xff09;4) 用户端微服务搭建5) 功能实现6) app网关7) 网关校验jwt8) 前端集成, 配置nginx a. 项目介绍 业务说明 技术栈说明 [外链图片转存失败,源站可能有防盗…

Spring Boot多级缓存实现方案

1.背景 缓存&#xff0c;就是让数据更接近使用者&#xff0c;让访问速度加快&#xff0c;从而提升系统性能。工作机制大概是先从缓存中加载数据&#xff0c;如果没有&#xff0c;再从慢速设备(eg:数据库)中加载数据并同步到缓存中。 所谓多级缓存&#xff0c;是指在整个系统架…

SpringBoot之logback-spring.xml详细配置

《logback官网》 各种指导文件&#xff0c;有空自己去看&#xff0c;比如&#xff1a;我们需要调整的是布局&#xff0c;直接看Layouts。 pom.xml <!-- 环境配置 --><profiles><profile><id>dev</id><properties><spring.profiles.a…

前端例程20230806:彩色灯珠装饰

演示 这里页面四周的彩色灯珠是会随着页面调整自动调整位置的。 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta na…

【数据结构与算法——TypeScript】哈希表

【数据结构与算法——TypeScript】 哈希表(HashTable) 哈希表介绍和特性 哈希表是一种非常重要的数据结构&#xff0c;但是很多学习编程的人一直搞不懂哈希表到底是如何实现的。 在这一章节中&#xff0c;我门就一点点来实现一个自己的哈希表。通过实现来理解哈希表背后的原理…

2022年03月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 已知a“161”&#xff0c;b“16”&#xff0c;c“8”,执行语句da>b and a>c&#xff0c;变量d的值为是&#xff1f; A&#xff1a;0 B&#xff1a;1 C&#xff1a;True D&am…

探究使用HTTP爬虫ip后无法访问网站的原因与解决方案

在今天的文章中&#xff0c;我们要一起来解决一个常见问题&#xff1a;使用HTTP爬虫ip后无法访问网站的原因是什么&#xff0c;以及如何解决这个问题。我们将提供一些实际的例子和操作经验&#xff0c;帮助大家解决HTTP爬虫ip无法访问网站的困扰。 1、代理服务器不可用 使用HT…

【SpringBoot笔记】定时任务(cron)

定时任务就是在固定的时间执行某个程序&#xff0c;闹钟的作用。 1.在启动类上添加注解 EnableScheduling 2.创建定时任务类 在这个类里面使用表达式设置什么时候执行 cron 表达式&#xff08;也叫七子表达式&#xff09;&#xff0c;设置执行规则 package com.Lijibai.s…

面试常问:tcp的三次握手和四次挥手你了解吗?

三次握手和四次挥手是各个公司常见的考点&#xff0c;一个简单的问题&#xff0c;却能看出面试者对网络协议的掌握程度&#xff0c;对问题分析与解决能力&#xff0c;以及数据流管理理解和异常情况应对能力。所以回答好一个tcp的三次握手和四次挥手的问题对于我们的面试成功与否…

(vue)获取对象的键遍历,同时循环el-tab页展示key及内容

(vue)获取对象的键遍历&#xff0c;同时循环el-tab页展示key及内容 效果&#xff1a; 数据结构&#xff1a; "statusData": {"订购广度": [ {"id": 11, "ztName": "广", …

C++笔记之两个类的实例之间传递参数的各种方法

C笔记之两个类的实例之间传递参数的各种方法 code review! 文章目录 C笔记之两个类的实例之间传递参数的各种方法1.构造函数参数传递2.成员函数参数传递3.友元函数4.友元类5.传递指针或引用6.静态成员变量7.静态成员函数8.全局变量或命名空间9.回调函数和函数指针10.观察者模…

pg实现月累计

获取每月累计数据&#xff1a; ​​​ SELECT a.month, SUM(b.total) AS total FROM ( SELECT month, SUM(sum) AS total FROM ( SELECT to_char(date("Joinin"),YYYY-MM) AS month , COUNT(*) AS sum FROM "APP_HR_Staff_Basic_Info" GROUP BY month ) …

做接口测试如何上次文件

在日常工作中&#xff0c;经常有上传文件功能的测试场景&#xff0c;因此&#xff0c;本文介绍两种主流编写上传文件接口测试脚本的方法。 首先&#xff0c;要知道文件上传的一般原理&#xff1a;客户端根据文件路径读取文件内容&#xff0c;将文件内容转换成二进制文件流的格式…

Vue3 第五节 一些组合式API和其他改变

1.provide和inject 2.响应式数据判断 3.Composition API的优势 4.新的组件 5.其他改变 一.provide和inject 作用&#xff1a;实现祖与后代组件间通信 套路&#xff1a;父组件有一个provide选项来提供数据&#xff0c;后代组件有一个inject选项来开始使用这些数据 &…

APP外包开发的学习流程

学习iOS App的开发是一项有趣和富有挑战性的任务&#xff0c;是一个不断学习和不断进步的过程。掌握基础知识后&#xff0c;不断实践和尝试新的项目将使您的技能不断提升。下面和大家分享一些建议&#xff0c;可以帮助您开始学习iOS App的开发。北京木奇移动技术有限公司&#…

hcip的重发布实验(1)

题目 拓扑图 IP地址配置 R1 < Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]sysname r1 [r1]int l0 [r1-LoopBack0]ip add 1.1.1.1 24 [r1-LoopBack0]int g0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 Aug 8 2023 21:28:29-08:00 r1 %%0…

升级node版本后vue2的项目node-sass、sass-loader安装报错(14.x升级到16.x)

node升级到16.x版本后&#xff0c;对应的node-sass需要升级到^6.0.0&#xff0c;此时sass-loader的版本需要升级到10.2.0以上 &#xff0c;具体对应版本规则可参考链接: https://github.com/webpack-contrib/sass-loader/releases?page3 vue2通过vue/cli创建的项目&#xff0…

super父类 事物

一个没有事物的方法。 调用他的父类里有事物的方法。 无论this 和 super 都会让父类事物方法没有事物。 如果写了super.class 文件里面&#xff0c;就是super调用。 如果没写&#xff0c;就是this调用&#xff0c;坑爹 测试&#xff0c;把父类注入&#xff0c;事物才生效。