Skip to content

Commit ce52baf

Browse files
mp911dechristophstrobl
authored andcommitted
Add support for GEOSEARCH/GEOSEARCHSTORE commands.
Supported through Lettuce only. GeoOperations support GEOSEARCH for now. Closes: #2043 Original Pull Request: #2113
1 parent 2f6dc3e commit ce52baf

21 files changed

+2316
-35
lines changed

src/main/asciidoc/new-features.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele
77
== New in Spring Data Redis 2.6
88

99
* Support for `SubscriptionListener` when using `MessageListener` for subscription confirmation callbacks. `ReactiveRedisMessageListenerContainer` and `ReactiveRedisOperations` provide `receiveLater(…)` and `listenToLater(…)` methods to await until Redis acknowledges the subscription.
10-
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `LMOVE`/`BLMOVE`, `COPY`, `GETEX`, `GETDEL`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`, `HRANDFIELD`, `ZRANDMEMBER`, `SMISMEMBER`).
10+
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `LMOVE`/`BLMOVE`, `COPY`, `GETEX`, `GETDEL`, `GEOSEARCH`, `GEOSEARCHSTORE`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`, `HRANDFIELD`, `ZRANDMEMBER`, `SMISMEMBER`).
1111

1212
[[new-in-2.5.0]]
1313
== New in Spring Data Redis 2.5

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

+52
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,14 @@ private byte[] serialize(String data) {
21202120
return serializer.serialize(data);
21212121
}
21222122

2123+
@SuppressWarnings("unchecked")
2124+
private GeoReference<byte[]> serialize(GeoReference<String> data) {
2125+
return data instanceof GeoReference.GeoSearchMemberReference
2126+
? GeoReference
2127+
.fromMember(serializer.serialize(((GeoReference.GeoSearchMemberReference<String>) data).getMember()))
2128+
: (GeoReference) data;
2129+
}
2130+
21232131
@SuppressWarnings("unchecked")
21242132
private StreamOffset<byte[]>[] serialize(StreamOffset<String>[] offsets) {
21252133

@@ -3886,6 +3894,50 @@ public Long geoRemove(String key, String... members) {
38863894
return geoRemove(serialize(key), serializeMulti(members));
38873895
}
38883896

3897+
/*
3898+
* (non-Javadoc)
3899+
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearch(byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
3900+
*/
3901+
@Override
3902+
public GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
3903+
GeoSearchCommandArgs args) {
3904+
return convertAndReturn(delegate.geoSearch(key, reference, predicate, args), Converters.identityConverter());
3905+
}
3906+
3907+
/*
3908+
* (non-Javadoc)
3909+
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearchStore(byte[], byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
3910+
*/
3911+
@Override
3912+
public Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
3913+
GeoSearchStoreCommandArgs args) {
3914+
return convertAndReturn(delegate.geoSearchStore(destKey, key, reference, predicate, args),
3915+
Converters.identityConverter());
3916+
}
3917+
3918+
/*
3919+
* (non-Javadoc)
3920+
* @see org.springframework.data.redis.connection.StringRedisConnection#geoSearch(java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
3921+
*/
3922+
@Override
3923+
public GeoResults<GeoLocation<String>> geoSearch(String key, GeoReference<String> reference, GeoShape predicate,
3924+
GeoSearchCommandArgs args) {
3925+
return convertAndReturn(delegate.geoSearch(serialize(key), serialize(reference), predicate, args),
3926+
byteGeoResultsToStringGeoResults);
3927+
}
3928+
3929+
/*
3930+
* (non-Javadoc)
3931+
* @see org.springframework.data.redis.connection.StringRedisConnection#geoSearchStore(java.lang.String, java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
3932+
*/
3933+
@Override
3934+
public Long geoSearchStore(String destKey, String key, GeoReference<String> reference, GeoShape predicate,
3935+
GeoSearchStoreCommandArgs args) {
3936+
return convertAndReturn(
3937+
delegate.geoSearchStore(serialize(destKey), serialize(key), serialize(reference), predicate, args),
3938+
Converters.identityConverter());
3939+
}
3940+
38893941
/*
38903942
* (non-Javadoc)
38913943
* @see org.springframework.data.redis.connection.RedisConnection#closePipeline()

src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java

+16
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,22 @@ default Long geoRemove(byte[] key, byte[]... members) {
15401540
return geoCommands().geoRemove(key, members);
15411541
}
15421542

1543+
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
1544+
@Override
1545+
@Deprecated
1546+
default GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
1547+
GeoSearchCommandArgs args) {
1548+
return geoCommands().geoSearch(key, reference, predicate, args);
1549+
}
1550+
1551+
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
1552+
@Override
1553+
@Deprecated
1554+
default Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
1555+
GeoSearchStoreCommandArgs args) {
1556+
return geoCommands().geoSearchStore(destKey, key, reference, predicate, args);
1557+
}
1558+
15431559
// HLL COMMANDS
15441560

15451561
/** @deprecated in favor of {@link RedisConnection#hyperLogLogCommands()}. */

src/main/java/org/springframework/data/redis/connection/ReactiveGeoCommands.java

+260-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.redis.connection;
1717

18+
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
19+
1820
import reactor.core.publisher.Flux;
1921
import reactor.core.publisher.Mono;
2022

@@ -27,6 +29,7 @@
2729
import java.util.Set;
2830

2931
import org.reactivestreams.Publisher;
32+
3033
import org.springframework.data.domain.Sort.Direction;
3134
import org.springframework.data.geo.Circle;
3235
import org.springframework.data.geo.Distance;
@@ -37,9 +40,6 @@
3740
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
3841
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
3942
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
40-
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
41-
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
42-
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
4343
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.Flag;
4444
import org.springframework.lang.Nullable;
4545
import org.springframework.util.Assert;
@@ -1239,4 +1239,261 @@ default Flux<GeoResult<GeoLocation<ByteBuffer>>> geoRadiusByMember(ByteBuffer ke
12391239
*/
12401240
Flux<CommandResponse<GeoRadiusByMemberCommand, Flux<GeoResult<GeoLocation<ByteBuffer>>>>> geoRadiusByMember(
12411241
Publisher<GeoRadiusByMemberCommand> commands);
1242+
1243+
/**
1244+
* {@code GEOSEARCH} command parameters.
1245+
*
1246+
* @author Mark Paluch
1247+
* @since 2.6
1248+
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
1249+
*/
1250+
class GeoSearchCommand extends KeyCommand {
1251+
1252+
private final @Nullable GeoReference<ByteBuffer> reference;
1253+
private final @Nullable GeoShape shape;
1254+
private final @Nullable GeoSearchCommandArgs args;
1255+
1256+
private GeoSearchCommand(@Nullable ByteBuffer key, @Nullable GeoReference<ByteBuffer> reference,
1257+
@Nullable GeoShape shape, @Nullable GeoSearchCommandArgs args) {
1258+
super(key);
1259+
this.reference = reference;
1260+
this.shape = shape;
1261+
this.args = args;
1262+
}
1263+
1264+
/**
1265+
* Creates a new {@link GeoSearchCommand} given a {@link GeoShape}.
1266+
*
1267+
* @param shape must not be {@literal null}.
1268+
* @return a new {@link GeoSearchCommand} for a {@link GeoShape}.
1269+
*/
1270+
public static GeoSearchCommand within(GeoShape shape) {
1271+
1272+
Assert.notNull(shape, "GeoShape must not be null!");
1273+
1274+
return new GeoSearchCommand(null, null, shape, null);
1275+
}
1276+
1277+
/**
1278+
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
1279+
*
1280+
* @param member must not be {@literal null}.
1281+
* @return a new {@link GeoSearchCommand} with {@literal key} applied.
1282+
*/
1283+
public GeoSearchCommand at(GeoReference<ByteBuffer> reference) {
1284+
1285+
Assert.notNull(reference, "GeoReference must not be null!");
1286+
1287+
return new GeoSearchCommand(getKey(), reference, getShape(), args);
1288+
}
1289+
1290+
/**
1291+
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
1292+
*
1293+
* @param member must not be {@literal null}.
1294+
* @return a new {@link GeoSearchCommand} with {@literal key} applied.
1295+
*/
1296+
public GeoSearchCommand in(ByteBuffer key) {
1297+
1298+
Assert.notNull(key, "Key must not be null!");
1299+
1300+
return new GeoSearchCommand(key, getReference(), getShape(), args);
1301+
}
1302+
1303+
/**
1304+
* Sets the command {@literal args}. Constructs a new command instance with all previously configured properties.
1305+
*
1306+
* @param args must not be {@literal null}.
1307+
* @return a new {@link GeoSearchCommand} with {@literal args} applied.
1308+
*/
1309+
public GeoSearchCommand with(GeoSearchCommandArgs args) {
1310+
1311+
Assert.notNull(args, "Args must not be null!");
1312+
1313+
return new GeoSearchCommand(getKey(), getReference(), getShape(), args);
1314+
}
1315+
1316+
public Optional<GeoSearchCommandArgs> getArgs() {
1317+
return Optional.ofNullable(args);
1318+
}
1319+
1320+
@Nullable
1321+
public GeoReference<ByteBuffer> getReference() {
1322+
return reference;
1323+
}
1324+
1325+
@Nullable
1326+
public GeoShape getShape() {
1327+
return shape;
1328+
}
1329+
}
1330+
1331+
/**
1332+
* {@code GEOSEARCHSTORE} command parameters.
1333+
*
1334+
* @author Mark Paluch
1335+
* @since 2.6
1336+
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
1337+
*/
1338+
class GeoSearchStoreCommand extends KeyCommand {
1339+
1340+
private final @Nullable ByteBuffer destKey;
1341+
private final @Nullable GeoReference<ByteBuffer> reference;
1342+
private final @Nullable GeoShape shape;
1343+
private final @Nullable GeoSearchStoreCommandArgs args;
1344+
1345+
private GeoSearchStoreCommand(@Nullable ByteBuffer key, @Nullable ByteBuffer destKey,
1346+
@Nullable GeoReference<ByteBuffer> reference, @Nullable GeoShape shape,
1347+
@Nullable GeoSearchStoreCommandArgs args) {
1348+
super(key);
1349+
this.destKey = destKey;
1350+
this.reference = reference;
1351+
this.shape = shape;
1352+
this.args = args;
1353+
}
1354+
1355+
/**
1356+
* Creates a new {@link GeoSearchStoreCommand} given a {@link GeoShape}.
1357+
*
1358+
* @param shape must not be {@literal null}.
1359+
* @return a new {@link GeoSearchStoreCommand} for a {@link GeoShape}.
1360+
*/
1361+
public static GeoSearchStoreCommand within(GeoShape shape) {
1362+
1363+
Assert.notNull(shape, "GeoShape must not be null!");
1364+
1365+
return new GeoSearchStoreCommand(null, null, null, shape, null);
1366+
}
1367+
1368+
/**
1369+
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
1370+
*
1371+
* @param member must not be {@literal null}.
1372+
* @return a new {@link GeoSearchStoreCommand} with {@literal key} applied.
1373+
*/
1374+
public GeoSearchStoreCommand at(GeoReference<ByteBuffer> reference) {
1375+
1376+
Assert.notNull(reference, "GeoReference must not be null!");
1377+
1378+
return new GeoSearchStoreCommand(getKey(), getDestKey(), reference, getShape(), args);
1379+
}
1380+
1381+
/**
1382+
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
1383+
*
1384+
* @param member must not be {@literal null}.
1385+
* @return a new {@link GeoSearchStoreCommand} with {@literal key} applied.
1386+
*/
1387+
public GeoSearchStoreCommand in(ByteBuffer key) {
1388+
1389+
Assert.notNull(key, "Key must not be null!");
1390+
1391+
return new GeoSearchStoreCommand(key, getDestKey(), getReference(), getShape(), args);
1392+
}
1393+
1394+
/**
1395+
* Sets the geoset {@literal destKey}. Constructs a new command instance with all previously configured properties.
1396+
*
1397+
* @param member must not be {@literal null}.
1398+
* @return a new {@link GeoSearchStoreCommand} with {@literal destKey} applied.
1399+
*/
1400+
public GeoSearchStoreCommand storeAt(ByteBuffer destKey) {
1401+
1402+
Assert.notNull(destKey, "Destination key must not be null!");
1403+
1404+
return new GeoSearchStoreCommand(getKey(), destKey, getReference(), getShape(), args);
1405+
}
1406+
1407+
/**
1408+
* Sets the command {@literal args}. Constructs a new command instance with all previously configured properties.
1409+
*
1410+
* @param args must not be {@literal null}.
1411+
* @return a new {@link GeoSearchStoreCommand} with {@literal args} applied.
1412+
*/
1413+
public GeoSearchStoreCommand with(GeoSearchStoreCommandArgs args) {
1414+
1415+
Assert.notNull(args, "Args must not be null!");
1416+
1417+
return new GeoSearchStoreCommand(getKey(), getDestKey(), getReference(), getShape(), args);
1418+
}
1419+
1420+
@Nullable
1421+
public ByteBuffer getDestKey() {
1422+
return destKey;
1423+
}
1424+
1425+
public Optional<GeoSearchStoreCommandArgs> getArgs() {
1426+
return Optional.ofNullable(args);
1427+
}
1428+
1429+
@Nullable
1430+
public GeoReference<ByteBuffer> getReference() {
1431+
return reference;
1432+
}
1433+
1434+
@Nullable
1435+
public GeoShape getShape() {
1436+
return shape;
1437+
}
1438+
}
1439+
1440+
/**
1441+
* Return the members of a geo set which are within the borders of the area specified by a given {@link GeoShape
1442+
* shape}. The query's center point is provided by {@link GeoReference}.
1443+
*
1444+
* @param key must not be {@literal null}.
1445+
* @param reference must not be {@literal null}.
1446+
* @param shape must not be {@literal null}.
1447+
* @param args must not be {@literal null}.
1448+
* @return
1449+
* @since 2.6
1450+
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
1451+
*/
1452+
default Flux<GeoResult<GeoLocation<ByteBuffer>>> geoSearch(ByteBuffer key, GeoReference<ByteBuffer> reference,
1453+
GeoShape shape, GeoSearchCommandArgs args) {
1454+
return geoSearch(Mono.just(GeoSearchCommand.within(shape).in(key).at(reference).with(args)))
1455+
.flatMap(CommandResponse::getOutput);
1456+
}
1457+
1458+
/**
1459+
* Get the {@literal member}s within given {@link GeoShape} from {@link GeoReference} applying given parameters.
1460+
*
1461+
* @param commands must not be {@literal null}.
1462+
* @return
1463+
* @since 2.6
1464+
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
1465+
*/
1466+
Flux<CommandResponse<GeoSearchCommand, Flux<GeoResult<GeoLocation<ByteBuffer>>>>> geoSearch(
1467+
Publisher<GeoSearchCommand> commands);
1468+
1469+
/**
1470+
* Query the members of a geo set which are within the borders of the area specified by a given {@link GeoShape shape}
1471+
* and store the result at {@code destKey}. The query's center point is provided by {@link GeoReference}.
1472+
*
1473+
* @param key must not be {@literal null}.
1474+
* @param reference must not be {@literal null}.
1475+
* @param shape must not be {@literal null}.
1476+
* @param args must not be {@literal null}.
1477+
* @return
1478+
* @since 2.6
1479+
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
1480+
*/
1481+
default Mono<Long> geoSearchStore(ByteBuffer destKey, ByteBuffer key, GeoReference<ByteBuffer> reference,
1482+
GeoShape shape, GeoSearchStoreCommandArgs args) {
1483+
return geoSearchStore(
1484+
Mono.just(GeoSearchStoreCommand.within(shape).in(key).storeAt(destKey).at(reference).with(args))).next()
1485+
.map(CommandResponse::getOutput);
1486+
}
1487+
1488+
/**
1489+
* Store the {@literal member}s within given {@link GeoShape} from {@link GeoReference} applying given parameters in a
1490+
* new geo set.
1491+
*
1492+
* @param commands must not be {@literal null}.
1493+
* @return
1494+
* @since 2.6
1495+
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
1496+
*/
1497+
Flux<NumericResponse<GeoSearchStoreCommand, Long>> geoSearchStore(Publisher<GeoSearchStoreCommand> commands);
1498+
12421499
}

0 commit comments

Comments
 (0)