上篇文章(Redis地理算法GEO解析和应用)我们对redis GEO算法进行解析以及相关shell命令的使用,这篇文章将带你进行实战应用。
依赖
注:jedis可以不引入,这里只是为了方便查看源码进行解析
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<!-- 方便查看源码 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
数据源配置
spring:
redis:
host: localhost
port: 6379
database: 4
编写实例
package com.gmall.user.server;
import com.gamll.user.base.BaseJunitTest;
import com.gmall.user.server.biz.UserBiz;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class UserBizTest extends BaseJunitTest {
@Autowired
private UserBiz userBiz;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Test
public void getCircleUser() {
// 初始化数据
Map<Double, Double> locMap = new HashMap<>(); // <纬度(x),经度(y)>
locMap.put(121.576334, 31.168569);
locMap.put(121.587664, 31.205503);
locMap.put(121.60586, 31.221726);
locMap.put(121.65238, 31.199703);
locMap.put(121.544749, 31.204989);
locMap.put(121.506297, 31.03268);
Map<Double, String> cityMap = new HashMap<>();
cityMap.put(121.576334, "上海希奥信息科技有限公司");
cityMap.put(121.587664, "上海火车站");
cityMap.put(121.60586, "上海人民广场");
cityMap.put(121.65238, "上海中医药大学");
cityMap.put(121.544749, "上海漕河泾经开区新创业园");
cityMap.put(121.506297, "上海竹林工业园");
System.out.println("--------------------------------- start to redis sync save Coordinate ---------------------------------");
// 定义坐标信息
for (Map.Entry<Double, Double> entry : locMap.entrySet()) {
Point point = new Point(entry.getKey(), entry.getValue());
redisTemplate.opsForGeo().add("user-local", point, cityMap.get(entry.getKey()));
}
// 设置检索范围
Point point = new Point(121.587623,31.201719);
Circle circle = new Circle(point, new Distance(5, Metrics.KILOMETERS));
// 定义返回结果参数,如果不指定默认只返回content即保存的member信息
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5);
GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius("user-local", circle,args);
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
for (GeoResult<RedisGeoCommands.GeoLocation<String>> l1 : list) {
System.out.println("name : " + l1.getContent().getName() +" distance : " + l1.getDistance() +" point :" + l1.getContent().getPoint());
//
}
System.out.println("--------------------------------- end to sync save Coordinate ---------------------------------");
}
}
源码解析
1、坐标添加 add
注:支持单点、多点集合Map同时添加
- point:元素属性x(经度),y(纬度)
@Override
public Long add(K key, Point point, M member) {
byte[] rawKey = rawKey(key);
byte[] rawMember = rawValue(member);
return execute(connection -> connection.geoAdd(rawKey, point, rawMember), true);
}
@Override
public Long add(K key, GeoLocation<M> location) {
return add(key, location.getPoint(), location.getName());
}
@Override
public Long add(K key, Map<M, Point> memberCoordinateMap) {
byte[] rawKey = rawKey(key);
Map<byte[], Point> rawMemberCoordinateMap = new HashMap<>();
for (M member : memberCoordinateMap.keySet()) {
byte[] rawMember = rawValue(member);
rawMemberCoordinateMap.put(rawMember, memberCoordinateMap.get(member));
}
return execute(connection -> connection.geoAdd(rawKey, rawMemberCoordinateMap), true);
}
@Override
public Long add(K key, Iterable<GeoLocation<M>> locations) {
Map<M, Point> memberCoordinateMap = new LinkedHashMap<>();
for (GeoLocation<M> location : locations) {
memberCoordinateMap.put(location.getName(), location.getPoint());
}
return add(key, memberCoordinateMap);
}
2、删除坐标remove
注:根据元素名称进行删除,支持多个同时删除
@Nullable
Long remove(K key, M... members);
3、查询周边radius
注:支持多种条件检索周边
Circle对象属性:
- Point:需要检索的中心点坐标;
- Distance:value为检索半径范围;Metric枚举类型为检索范围单位
KILOMETERS(6378.137, "km"), MILES(3963.191, "mi"), NEUTRAL(1, "");
GeoRadiusCommandArgs对象方法:
注:作用指定返回数据结果参数
-
includeCoordinates:返回结果包含坐标信息
-
includeDistance:返回结果包含具中心坐标距离信息
-
sortAscending:按照距离升序排序
-
sortDescending:按照距离降序排序
-
limit:返回结果数量限制
public static GeoRadiusCommandArgs newGeoRadiusArgs() {
return new GeoRadiusCommandArgs();
}
public GeoRadiusCommandArgs includeCoordinates() {
flags.add(Flag.WITHCOORD);
return this;
}
public GeoRadiusCommandArgs includeDistance() {
flags.add(Flag.WITHDIST);
return this;
}
public GeoRadiusCommandArgs sortAscending() {
sortDirection = Direction.ASC;
return this;
}
public GeoRadiusCommandArgs sortDescending() {
sortDirection = Direction.DESC;
return this;
}
public GeoRadiusCommandArgs limit(long count) {
Assert.isTrue(count > 0, "Count has to positive value.");
limit = count;
return this;
}
radius源码解析:
@Nullable
GeoResults<GeoLocation<M>> radius(K key, Circle within);
@Nullable
GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args);
@Nullable
GeoResults<GeoLocation<M>> radius(K key, M member, double radius);
@Nullable
GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance);
@Nullable
GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance, GeoRadiusCommandArgs args);
异常现象
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'
org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:273)
at org.springframework.data.redis.connection.lettuce.LettuceGeoCommands.convertLettuceAccessException(LettuceGeoCommands.java:424)
at org.springframework.data.redis.connection.lettuce.LettuceGeoCommands.geoAdd(LettuceGeoCommands.java:78)
at org.springframework.data.redis.connection.DefaultedRedisConnection.geoAdd(DefaultedRedisConnection.java:1187)
at org.springframework.data.redis.connection.DefaultStringRedisConnection.geoAdd(DefaultStringRedisConnection.java:2908)
at org.springframework.data.redis.core.DefaultGeoOperations.lambda$add$0(DefaultGeoOperations.java:60)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
at org.springframework.data.redis.core.DefaultGeoOperations.add(DefaultGeoOperations.java:60)
at com.gmall.user.server.UserBizTest.getCircleUser(UserBizTest.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'
at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:135)
at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:108)
at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120)
at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111)
at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:654)
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:614)
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:565)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
解决方案:
更换redis版本,因为redis GEO是从>=3.2版本之后开始支持
最后
源码地址:https://gitee.com/mackjie/gmall-service
项目中gmall-batch -->test-->com.gmall.user.server.UserBizTest#getCircleUser()
这个技能 今天你学到了吗?^_^