附近人功能实现

redis自带的GEO来实现此功能。

一、

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

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for job_base_info
-- ----------------------------
DROP TABLE IF EXISTS `job_base_info`;
CREATE TABLE `job_base_info` (
`job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
`job_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称',
`job_location` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '任务地点位置经纬度-逗号隔开',
`job_detail_address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务详细地点位置',
PRIMARY KEY (`job_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '工作任务详情基础信息表' ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of job_base_info
-- ----------------------------
INSERT INTO `job_base_info` VALUES (1, '软件开发', '120.433576,36.139697', '青岛市崂山区海尔路1号');
INSERT INTO `job_base_info` VALUES (2, '儿童摄影', '120.420297,36.156589', '山东省青岛市李沧区书院路188号');
INSERT INTO `job_base_info` VALUES (3, '清洁家用电器', '120.025706,36.281478', '山东省青岛市胶州市福州支路232号东60米');
INSERT INTO `job_base_info` VALUES (4, '辩论学习', '120.505042,36.171247', '松岭路238号中国海洋大学内');

SET FOREIGN_KEY_CHECKS = 1;

(1)程序启动时,将数据库中的任务数据的坐标信息初始化到redis中(此处暂且忽略任务的增删改查对redis中数据的影响)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@PostConstruct
public void init(){
//首先要删除该key的所有值
redisTemplate.delete("company-task");
List<JobBaseInfo> jobBaseInfoList = jobBaseInfoMapper.selectList(Wrappers.emptyWrapper());
jobBaseInfoList.stream().forEach(item->{
String jobLocation = item.getJobLocation();
if(StrUtil.isNotEmpty(jobLocation)){
String[] split = jobLocation.split(",");comp
if(split.length==2){
//Point(经度, 纬度)
Point point = new Point(Double.parseDouble(split[0]),Double.parseDouble(split[1]));
//将经纬度数据及其id存到key为“company-task”中
redisTemplate.opsForGeo().add("company-task",point,item.getJobId());
}
}
});
}

(2)查询当前坐标下3km范围内的任务地点(外加根据任务名搜索的联合查询)

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
@Override
public List<JobBaseInfo> selectJobList(JobBaseInfoDTO jobBaseInfoDTO) {
String jobLocation = jobBaseInfoDTO.getJobLocation();
//距离
Double distance = jobBaseInfoDTO.getDistance();
List<Integer> idList = new ArrayList<>();
if(StringUtils.isNotNull(jobLocation) && StringUtils.isNotNull(distance)){
String[] split = jobLocation.split(",");
if(split.length==2){
//Point(经度, 纬度) Distance(距离量, 距离单位)
Circle circle = new Circle(new Point(Double.parseDouble(split[0]),Double.parseDouble(split[1])),
new Distance(distance, Metrics.KILOMETERS));
//params: key, Circle 获取存储到redis中的distance范围内的所有任务地点数据
GeoResults radius = redisTemplate.opsForGeo().radius("company-task", circle);
List<GeoResult> contentList = radius.getContent();
if(contentList.size()>0){
contentList.stream().forEach(item->{
RedisGeoCommands.GeoLocation content = (RedisGeoCommands.GeoLocation) item.getContent();
idList.add((Integer) content.getName());
});
}
}
}
jobBaseInfoDTO.setIdList(idList);
return jobBaseInfoMapper.selectJobList(jobBaseInfoDTO);
}

selectJobList(jobBaseInfoDTO)方法的sql如下

<select id="selectJobList" resultType="com.itzyq.redislikes.model.entity.JobBaseInfo">
    select
    <include refid="Base_Column_List"></include>
    from job_base_info
    <where>
        <if test="jobName!=null and jobName!=''">
            and job_name like CONCAT("%",#{jobName},"%")
        </if>

        <if test="idList!=null and idList.size > 0 ">
            and job_id in
            <foreach collection="idList" item="id" open="(" close=")" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>
二、Redis中的GEO

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增,GEO 是基于zset的一种扩展数据格式。Redis GEO 操作方法有:
•geoadd:添加地理位置的坐标。
•geopos:获取地理位置的坐标。
•geodist:计算两个位置之间的距离。
•georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。
•georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。
•geohash:返回一个或多个位置对象的 geohash 值。