【Redis】GEO数据类型之附近的店铺实现

29 篇文章 2 订阅

目录

一、GEO

1、概念

2、相关命令

1.GEOADD

2.GEODIST

3.GEOHASH

4.GEOPOS

5.GEORADIUS

6.GEOSEARCH

7.GEOSEARCHSTORE

二、附近的店铺

1、实现思路

2、代码实现


一、GEO

1、概念

GEO全称Geolocation,Redis种的一种数据结构他代表地理坐标

2、相关命令

1.GEOADD

向redis中添加一个地理空间信息,其中包含经度、维度、值(member)

2.GEODIST

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

3.GEOHASH

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

4.GEOPOS

返回指定member的左边

5.GEORADIUS

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

6.GEOSEARCH

在指定的范围内搜索member,并按照与指定点之间的距离进行排序后返回,这个范围可以是矩形也可以是圆形

7.GEOSEARCHSTORE

与第六个相同,不过该命令可以将结果存储到一个指定的key中

二、附近的店铺

1、实现思路

在数据库层面,我们在店铺表上维护两个字段分别是x、y对应经纬度,然后将这些店铺的类型id作为geo数据类型的key将该类型对应的店铺id作为member以及经纬度xy存入member对应的经纬度中,首先我们需要将查出来的店铺信息通过代码(推荐使用Stream流)将相同类型店铺存的id作为key在同一个的map中,然后遍历该集合以类型id作为redis的key的组成,将店铺id与经纬度存入geo中,此时当客户端传来要查询的店铺类型与用户自身的地理位置时,我们只需要通过上述geo数据结构来处理按照距离由近到远的返回店铺id结合,然后查询数据库将该id对应的店铺信息查出后设置他们各自的距离后返回给前端

2、代码实现

List<Shop> test(Integer typeId, Integer page, Integer size, Double x, Double y) {
        // 参数中  typeId为类型id   page当前页数   size每页数据量  x经度  y维度
        // 首先判断是否需要根据坐标查询:判断客户端是否传回用户的地理位置
        if (x == null || y == null) {
            // 不需要按照地理位置查询,则进行正常分页查询
            // TODO:调用mapper层查询数据库
            return null;
        }
        
        // 2.计算分页参数
        int from = (page - 1) * size;
        int end = page * size;
        
        // 3.查询redis 根据距离排序
        String key = "shop:geo:" + typeId;
        GeoResults<RedisGeoCommands.GeoLocation<String>> search = stringRedisTemplate.opsForGeo()
                .search(
                    key,                            // key 
                    GeoReference.fromCoordinate(new Point(x, y)),  // 中心点的经纬度
                    new Distance(5000),                     // 查询范围单位默认是m
                    RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end) // 将距离返回查询到end
        );
        if (search == null) return null;

        // 4.截取需要的数据
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = search.getContent();
        if (content.size() < from) {
            // 没有下一页了
            return null;
        }
        List<Long> ids = new ArrayList<>(content.size());                   // 存储用户id
        Map<String,Distance> distanceMap = new HashMap<>(content.size());   // 存储距离
        // 截取相应的部分
        content.stream().skip(from).forEach(re -> {
            // 获取id
            String shopIdStr = re.getContent().getName();
            ids.add(Long.valueOf(shopIdStr));
            // 获取距离
            Distance distance = re.getDistance();
            distanceMap.put(shopIdStr,distance);
        });
        
        // 根据id去查询对应的店铺
        List<Shop> dbShops = shopMapper.queryById(ids);
        // 将对应的距离赋值给每个shop对象后返回
        for (Shop shop:dbShops) {
            shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
        }
        
        return dbShops;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1886i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值