Add support for GEOSEARCH/GEOSEARCHSTORE commands.

Supported through Lettuce only. GeoOperations support GEOSEARCH for now.

Closes: #2043
Original Pull Request: #2113
This commit is contained in:
Mark Paluch
2021-07-05 11:20:39 +02:00
committed by Christoph Strobl
parent 2f6dc3e79e
commit ce52baff98
21 changed files with 2319 additions and 38 deletions

View File

@@ -2120,6 +2120,14 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return serializer.serialize(data);
}
@SuppressWarnings("unchecked")
private GeoReference<byte[]> serialize(GeoReference<String> data) {
return data instanceof GeoReference.GeoSearchMemberReference
? GeoReference
.fromMember(serializer.serialize(((GeoReference.GeoSearchMemberReference<String>) data).getMember()))
: (GeoReference) data;
}
@SuppressWarnings("unchecked")
private StreamOffset<byte[]>[] serialize(StreamOffset<String>[] offsets) {
@@ -3886,6 +3894,50 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return geoRemove(serialize(key), serializeMulti(members));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearch(byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
return convertAndReturn(delegate.geoSearch(key, reference, predicate, args), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @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)
*/
@Override
public Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
return convertAndReturn(delegate.geoSearchStore(destKey, key, reference, predicate, args),
Converters.identityConverter());
}
/*
* (non-Javadoc)
* @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)
*/
@Override
public GeoResults<GeoLocation<String>> geoSearch(String key, GeoReference<String> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
return convertAndReturn(delegate.geoSearch(serialize(key), serialize(reference), predicate, args),
byteGeoResultsToStringGeoResults);
}
/*
* (non-Javadoc)
* @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)
*/
@Override
public Long geoSearchStore(String destKey, String key, GeoReference<String> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
return convertAndReturn(
delegate.geoSearchStore(serialize(destKey), serialize(key), serialize(reference), predicate, args),
Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConnection#closePipeline()

View File

@@ -1540,6 +1540,22 @@ public interface DefaultedRedisConnection extends RedisConnection {
return geoCommands().geoRemove(key, members);
}
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
@Override
@Deprecated
default GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
return geoCommands().geoSearch(key, reference, predicate, args);
}
/** @deprecated in favor of {@link RedisConnection#geoCommands()}}. */
@Override
@Deprecated
default Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
return geoCommands().geoSearchStore(destKey, key, reference, predicate, args);
}
// HLL COMMANDS
/** @deprecated in favor of {@link RedisConnection#hyperLogLogCommands()}. */

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.connection;
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -27,6 +29,7 @@ import java.util.Optional;
import java.util.Set;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
@@ -37,9 +40,6 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.Command
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.Flag;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -1239,4 +1239,261 @@ public interface ReactiveGeoCommands {
*/
Flux<CommandResponse<GeoRadiusByMemberCommand, Flux<GeoResult<GeoLocation<ByteBuffer>>>>> geoRadiusByMember(
Publisher<GeoRadiusByMemberCommand> commands);
/**
* {@code GEOSEARCH} command parameters.
*
* @author Mark Paluch
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
class GeoSearchCommand extends KeyCommand {
private final @Nullable GeoReference<ByteBuffer> reference;
private final @Nullable GeoShape shape;
private final @Nullable GeoSearchCommandArgs args;
private GeoSearchCommand(@Nullable ByteBuffer key, @Nullable GeoReference<ByteBuffer> reference,
@Nullable GeoShape shape, @Nullable GeoSearchCommandArgs args) {
super(key);
this.reference = reference;
this.shape = shape;
this.args = args;
}
/**
* Creates a new {@link GeoSearchCommand} given a {@link GeoShape}.
*
* @param shape must not be {@literal null}.
* @return a new {@link GeoSearchCommand} for a {@link GeoShape}.
*/
public static GeoSearchCommand within(GeoShape shape) {
Assert.notNull(shape, "GeoShape must not be null!");
return new GeoSearchCommand(null, null, shape, null);
}
/**
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
*
* @param member must not be {@literal null}.
* @return a new {@link GeoSearchCommand} with {@literal key} applied.
*/
public GeoSearchCommand at(GeoReference<ByteBuffer> reference) {
Assert.notNull(reference, "GeoReference must not be null!");
return new GeoSearchCommand(getKey(), reference, getShape(), args);
}
/**
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
*
* @param member must not be {@literal null}.
* @return a new {@link GeoSearchCommand} with {@literal key} applied.
*/
public GeoSearchCommand in(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new GeoSearchCommand(key, getReference(), getShape(), args);
}
/**
* Sets the command {@literal args}. Constructs a new command instance with all previously configured properties.
*
* @param args must not be {@literal null}.
* @return a new {@link GeoSearchCommand} with {@literal args} applied.
*/
public GeoSearchCommand with(GeoSearchCommandArgs args) {
Assert.notNull(args, "Args must not be null!");
return new GeoSearchCommand(getKey(), getReference(), getShape(), args);
}
public Optional<GeoSearchCommandArgs> getArgs() {
return Optional.ofNullable(args);
}
@Nullable
public GeoReference<ByteBuffer> getReference() {
return reference;
}
@Nullable
public GeoShape getShape() {
return shape;
}
}
/**
* {@code GEOSEARCHSTORE} command parameters.
*
* @author Mark Paluch
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
class GeoSearchStoreCommand extends KeyCommand {
private final @Nullable ByteBuffer destKey;
private final @Nullable GeoReference<ByteBuffer> reference;
private final @Nullable GeoShape shape;
private final @Nullable GeoSearchStoreCommandArgs args;
private GeoSearchStoreCommand(@Nullable ByteBuffer key, @Nullable ByteBuffer destKey,
@Nullable GeoReference<ByteBuffer> reference, @Nullable GeoShape shape,
@Nullable GeoSearchStoreCommandArgs args) {
super(key);
this.destKey = destKey;
this.reference = reference;
this.shape = shape;
this.args = args;
}
/**
* Creates a new {@link GeoSearchStoreCommand} given a {@link GeoShape}.
*
* @param shape must not be {@literal null}.
* @return a new {@link GeoSearchStoreCommand} for a {@link GeoShape}.
*/
public static GeoSearchStoreCommand within(GeoShape shape) {
Assert.notNull(shape, "GeoShape must not be null!");
return new GeoSearchStoreCommand(null, null, null, shape, null);
}
/**
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
*
* @param member must not be {@literal null}.
* @return a new {@link GeoSearchStoreCommand} with {@literal key} applied.
*/
public GeoSearchStoreCommand at(GeoReference<ByteBuffer> reference) {
Assert.notNull(reference, "GeoReference must not be null!");
return new GeoSearchStoreCommand(getKey(), getDestKey(), reference, getShape(), args);
}
/**
* Sets the geoset {@literal key}. Constructs a new command instance with all previously configured properties.
*
* @param member must not be {@literal null}.
* @return a new {@link GeoSearchStoreCommand} with {@literal key} applied.
*/
public GeoSearchStoreCommand in(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new GeoSearchStoreCommand(key, getDestKey(), getReference(), getShape(), args);
}
/**
* Sets the geoset {@literal destKey}. Constructs a new command instance with all previously configured properties.
*
* @param member must not be {@literal null}.
* @return a new {@link GeoSearchStoreCommand} with {@literal destKey} applied.
*/
public GeoSearchStoreCommand storeAt(ByteBuffer destKey) {
Assert.notNull(destKey, "Destination key must not be null!");
return new GeoSearchStoreCommand(getKey(), destKey, getReference(), getShape(), args);
}
/**
* Sets the command {@literal args}. Constructs a new command instance with all previously configured properties.
*
* @param args must not be {@literal null}.
* @return a new {@link GeoSearchStoreCommand} with {@literal args} applied.
*/
public GeoSearchStoreCommand with(GeoSearchStoreCommandArgs args) {
Assert.notNull(args, "Args must not be null!");
return new GeoSearchStoreCommand(getKey(), getDestKey(), getReference(), getShape(), args);
}
@Nullable
public ByteBuffer getDestKey() {
return destKey;
}
public Optional<GeoSearchStoreCommandArgs> getArgs() {
return Optional.ofNullable(args);
}
@Nullable
public GeoReference<ByteBuffer> getReference() {
return reference;
}
@Nullable
public GeoShape getShape() {
return shape;
}
}
/**
* Return the members of a geo set which are within the borders of the area specified by a given {@link GeoShape
* shape}. The query's center point is provided by {@link GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param shape must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<ByteBuffer>>> geoSearch(ByteBuffer key, GeoReference<ByteBuffer> reference,
GeoShape shape, GeoSearchCommandArgs args) {
return geoSearch(Mono.just(GeoSearchCommand.within(shape).in(key).at(reference).with(args)))
.flatMap(CommandResponse::getOutput);
}
/**
* Get the {@literal member}s within given {@link GeoShape} from {@link GeoReference} applying given parameters.
*
* @param commands must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
Flux<CommandResponse<GeoSearchCommand, Flux<GeoResult<GeoLocation<ByteBuffer>>>>> geoSearch(
Publisher<GeoSearchCommand> commands);
/**
* Query the members of a geo set which are within the borders of the area specified by a given {@link GeoShape shape}
* and store the result at {@code destKey}. The query's center point is provided by {@link GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param shape must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Mono<Long> geoSearchStore(ByteBuffer destKey, ByteBuffer key, GeoReference<ByteBuffer> reference,
GeoShape shape, GeoSearchStoreCommandArgs args) {
return geoSearchStore(
Mono.just(GeoSearchStoreCommand.within(shape).in(key).storeAt(destKey).at(reference).with(args))).next()
.map(CommandResponse::getOutput);
}
/**
* Store the {@literal member}s within given {@link GeoShape} from {@link GeoReference} applying given parameters in a
* new geo set.
*
* @param commands must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
Flux<NumericResponse<GeoSearchStoreCommand, Long>> geoSearchStore(Publisher<GeoSearchStoreCommand> commands);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.connection;
import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -26,6 +28,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Shape;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -215,28 +218,159 @@ public interface RedisGeoCommands {
@Nullable
Long geoRemove(byte[] key, byte[]... members);
/**
* Return the members of a geo set which are within the borders of the area specified by a given {@link GeoShape
* shape}. The query's center point is provided by {@link GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param predicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args);
/**
* Query the members of a geo set which are within the borders of the area specified by a given {@link GeoShape shape}
* and store the result at {@code destKey}. The query's center point is provided by {@link GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param predicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args);
/**
* Search predicate for {@code GEOSEARCH} and {@code GEOSEARCHSTORE} commands.
*
* @since 2.6
*/
interface GeoShape {
/**
* Create a shape used as predicate for geo queries from a {@link Distance radius} around the query center point.
*
* @param radius
* @return
*/
static GeoShape byRadius(Distance radius) {
return new RadiusShape(radius);
}
/**
* Create a shape used as predicate for geo queries from a bounding box with specified by {@code width} and
* {@code height}.
*
* @param width must not be {@literal null}.
* @param height must not be {@literal null}.
* @param distanceUnit must not be {@literal null}.
* @return
*/
static GeoShape byBox(double width, double height, DistanceUnit distanceUnit) {
return byBox(new BoundingBox(width, height, distanceUnit));
}
/**
* Create a shape used as predicate for geo queries from a {@link BoundingBox}.
*
* @param boundingBox must not be {@literal null}.
* @return
*/
static GeoShape byBox(BoundingBox boundingBox) {
return new BoxShape(boundingBox);
}
/**
* The metric used for this geo predicate.
*
* @return
*/
Metric getMetric();
}
/**
* Radius defined by {@link Distance}.
*
* @since 2.6
*/
class RadiusShape implements GeoShape {
private final Distance radius;
public RadiusShape(Distance radius) {
Assert.notNull(radius, "Distance must not be null");
this.radius = radius;
}
public Distance getRadius() {
return radius;
}
@Override
public Metric getMetric() {
return radius.getMetric();
}
}
/**
* Bounding box defined by width and height.
*
* @since 2.6
*/
class BoxShape implements GeoShape {
private final BoundingBox boundingBox;
public BoxShape(BoundingBox boundingBox) {
Assert.notNull(boundingBox, "BoundingBox must not be null");
this.boundingBox = boundingBox;
}
public BoundingBox getBoundingBox() {
return boundingBox;
}
@Override
public Metric getMetric() {
return boundingBox.getHeight().getMetric();
}
}
/**
* Additional arguments (like count/sort/...) to be used with {@link RedisGeoCommands}.
*
* @author Ninad Divadkar
* @author Christoph Strobl
* @since 1.8
* @author Mark Paluch
* @since 2.6
*/
class GeoRadiusCommandArgs implements Cloneable {
class GeoSearchCommandArgs implements Cloneable {
Set<Flag> flags = new LinkedHashSet<>(2, 1);
@Nullable Long limit;
@Nullable Direction sortDirection;
private GeoRadiusCommandArgs() {}
private GeoSearchCommandArgs() {}
/**
* Create new {@link GeoRadiusCommandArgs}.
* Create new {@link GeoSearchCommandArgs}.
*
* @return never {@literal null}.
*/
public static GeoRadiusCommandArgs newGeoRadiusArgs() {
return new GeoRadiusCommandArgs();
public static GeoSearchCommandArgs newGeoSearchArgs() {
return new GeoSearchCommandArgs();
}
/**
@@ -244,7 +378,7 @@ public interface RedisGeoCommands {
*
* @return
*/
public GeoRadiusCommandArgs includeCoordinates() {
public GeoSearchCommandArgs includeCoordinates() {
flags.add(Flag.WITHCOORD);
return this;
@@ -255,21 +389,32 @@ public interface RedisGeoCommands {
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs includeDistance() {
public GeoSearchCommandArgs includeDistance() {
flags.add(Flag.WITHDIST);
return this;
}
/**
* Apply a sort direction.
*
* @return never {@literal null}.
*/
public GeoSearchCommandArgs sort(Direction direction) {
Assert.notNull(direction, "Sort direction must not be null");
this.sortDirection = direction;
return this;
}
/**
* Sort returned items from the nearest to the furthest, relative to the center.
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs sortAscending() {
sortDirection = Direction.ASC;
return this;
public GeoSearchCommandArgs sortAscending() {
return sort(Direction.ASC);
}
/**
@@ -277,10 +422,8 @@ public interface RedisGeoCommands {
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs sortDescending() {
sortDirection = Direction.DESC;
return this;
public GeoSearchCommandArgs sortDescending() {
return sort(Direction.DESC);
}
/**
@@ -289,13 +432,28 @@ public interface RedisGeoCommands {
* @param count
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs limit(long count) {
public GeoSearchCommandArgs limit(long count) {
Assert.isTrue(count > 0, "Count has to positive value.");
limit = count;
return this;
}
/**
* Limit the results to the first N matching items.
*
* @param count
* @param any
* @return never {@literal null}.
*/
public GeoSearchCommandArgs limit(long count, boolean any) {
Assert.isTrue(count > 0, "Count has to positive value.");
limit = count;
flags.add(Flag.ANY);
return this;
}
/**
* @return never {@literal null}.
*/
@@ -331,8 +489,248 @@ public interface RedisGeoCommands {
return limit != null;
}
public boolean hasAnyLimit() {
return hasLimit() && flags.contains(Flag.ANY);
}
@Override
protected GeoSearchCommandArgs clone() {
GeoSearchCommandArgs tmp = new GeoSearchCommandArgs();
tmp.flags = this.flags != null ? new LinkedHashSet<>(this.flags) : new LinkedHashSet<>(2);
tmp.limit = this.limit;
tmp.sortDirection = this.sortDirection;
return tmp;
}
}
/**
* Additional arguments (like count/sort/...) to be used with {@link RedisGeoCommands}.
*
* @author Mark Paluch
* @since 2.6
*/
class GeoSearchStoreCommandArgs implements Cloneable {
Set<Flag> flags = new LinkedHashSet<>(2, 1);
@Nullable Long limit;
@Nullable Direction sortDirection;
private GeoSearchStoreCommandArgs() {}
/**
* Create new {@link GeoSearchStoreCommandArgs}.
*
* @return never {@literal null}.
*/
public static GeoSearchStoreCommandArgs newGeoSearchStoreArgs() {
return new GeoSearchStoreCommandArgs();
}
/**
* Sets the {@link Flag#STOREDIST} flag to also store the distance of the returned items from the specified center.
*
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs storeDistance() {
flags.add(Flag.STOREDIST);
return this;
}
/**
* Apply a sort direction.
*
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs sort(Direction direction) {
Assert.notNull(direction, "Sort direction must not be null");
sortDirection = Direction.ASC;
return this;
}
/**
* Sort returned items from the nearest to the furthest, relative to the center.
*
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs sortAscending() {
return sort(Direction.ASC);
}
/**
* Sort returned items from the furthest to the nearest, relative to the center.
*
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs sortDescending() {
return sort(Direction.DESC);
}
/**
* Limit the results to the first N matching items.
*
* @param count
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs limit(long count) {
Assert.isTrue(count > 0, "Count has to positive value.");
limit = count;
return this;
}
/**
* Limit the results to the first N matching items.
*
* @param count
* @param any
* @return never {@literal null}.
*/
public GeoSearchStoreCommandArgs limit(long count, boolean any) {
Assert.isTrue(count > 0, "Count has to positive value.");
limit = count;
flags.add(Flag.ANY);
return this;
}
/**
* @return never {@literal null}.
*/
public Set<Flag> getFlags() {
return flags;
}
/**
* @return can be {@literal null}.
*/
@Nullable
public Long getLimit() {
return limit;
}
/**
* @return can be {@literal null}.
*/
@Nullable
public Direction getSortDirection() {
return sortDirection;
}
public boolean isStoreDistance() {
return flags.contains(Flag.STOREDIST);
}
public boolean hasSortDirection() {
return sortDirection != null;
}
public boolean hasLimit() {
return limit != null;
}
public boolean hasAnyLimit() {
return hasLimit() && flags.contains(Flag.ANY);
}
@Override
protected GeoSearchStoreCommandArgs clone() {
GeoSearchStoreCommandArgs tmp = new GeoSearchStoreCommandArgs();
tmp.flags = this.flags != null ? new LinkedHashSet<>(this.flags) : new LinkedHashSet<>(2);
tmp.limit = this.limit;
tmp.sortDirection = this.sortDirection;
return tmp;
}
}
/**
* Additional arguments (like count/sort/...) to be used with {@link RedisGeoCommands}.
*
* @author Ninad Divadkar
* @author Christoph Strobl
* @since 1.8
*/
class GeoRadiusCommandArgs extends GeoSearchCommandArgs implements Cloneable {
private GeoRadiusCommandArgs() {}
/**
* Create new {@link GeoRadiusCommandArgs}.
*
* @return never {@literal null}.
*/
public static GeoRadiusCommandArgs newGeoRadiusArgs() {
return new GeoRadiusCommandArgs();
}
/**
* Sets the {@link Flag#WITHCOORD} flag to also return the longitude, latitude coordinates of the matching items.
*
* @return
*/
public GeoRadiusCommandArgs includeCoordinates() {
super.includeCoordinates();
return this;
}
/**
* Sets the {@link Flag#WITHDIST} flag to also return the distance of the returned items from the specified center.
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs includeDistance() {
super.includeDistance();
return this;
}
/**
* Apply a sort direction.
*
* @return never {@literal null}.
* @since 2.6
*/
public GeoRadiusCommandArgs sort(Direction direction) {
super.sort(direction);
return this;
}
/**
* Sort returned items from the nearest to the furthest, relative to the center.
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs sortAscending() {
super.sortAscending();
return this;
}
/**
* Sort returned items from the furthest to the nearest, relative to the center.
*
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs sortDescending() {
super.sortDescending();
return this;
}
/**
* Limit the results to the first N matching items.
*
* @param count
* @return never {@literal null}.
*/
public GeoRadiusCommandArgs limit(long count) {
super.limit(count);
return this;
}
public enum Flag {
WITHCOORD, WITHDIST
WITHCOORD, WITHDIST, ANY, STOREDIST
}
@Override
@@ -346,6 +744,193 @@ public interface RedisGeoCommands {
}
}
/**
* Reference point for {@code GEOSEARCH} and {@code GEOSEARCHSTORE} commands. Provides factory methods to create
* {@link GeoReference} from geo-set members or reference points.
*
* @param <T>
* @since 2.6
*/
class GeoReference<T> {
/**
* Creates a {@link GeoReference} from a geoset member.
*
* @param member must not be {@literal null}.
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromMember(T member) {
Assert.notNull(member, "Geoset member must not be null");
return new GeoSearchMemberReference<>(member);
}
/**
* Creates a {@link GeoReference} from a {@link GeoLocation geoset member}.
*
* @param member must not be {@literal null}.
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromMember(GeoLocation<T> member) {
Assert.notNull(member, "GeoLocation must not be null");
return new GeoSearchMemberReference<>(member.getName());
}
/**
* Creates a {@link GeoReference} from a {@link Circle#getCenter() circle center point} .
*
* @param within must not be {@literal null}.
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromCircle(Circle within) {
Assert.notNull(within, "Circle must not be null");
return fromCoordinate(within.getCenter());
}
/**
* Creates a {@link GeoReference} from a WGS84 longitude/latitude coordinate.
*
* @param longitude
* @param latitude
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromCoordinate(double longitude, double latitude) {
return new GeoSearchCoordinateReference<>(longitude, latitude);
}
/**
* Creates a {@link GeoReference} from a WGS84 longitude/latitude coordinate.
*
* @param location must not be {@literal null}.
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromCoordinate(GeoLocation<?> location) {
Assert.notNull(location, "GeoLocation must not be null");
Assert.notNull(location.getPoint(), "GeoLocation point must not be null");
return fromCoordinate(location.getPoint());
}
/**
* Creates a {@link GeoReference} from a WGS84 longitude/latitude coordinate.
*
* @param point must not be {@literal null}.
* @param <T>
* @return
*/
public static <T> GeoReference<T> fromCoordinate(Point point) {
Assert.notNull(point, "Reference point must not be null");
return fromCoordinate(point.getX(), point.getY());
}
public static class GeoSearchMemberReference<T> extends GeoReference<T> {
private final T member;
public GeoSearchMemberReference(T member) {
this.member = member;
}
public T getMember() {
return member;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof GeoSearchMemberReference)) {
return false;
}
GeoSearchMemberReference<?> that = (GeoSearchMemberReference<?>) o;
return ObjectUtils.nullSafeEquals(member, that.member);
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(member);
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append(getClass().getSimpleName());
sb.append(" [member=").append(member);
sb.append(']');
return sb.toString();
}
}
public static class GeoSearchCoordinateReference<T> extends GeoReference<T> {
private final double longitude;
private final double latitude;
public GeoSearchCoordinateReference(double longitude, double latitude) {
this.longitude = longitude;
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public double getLatitude() {
return latitude;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof GeoSearchCoordinateReference)) {
return false;
}
GeoSearchCoordinateReference<?> that = (GeoSearchCoordinateReference<?>) o;
if (longitude != that.longitude) {
return false;
}
return latitude == that.latitude;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(longitude);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(latitude);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer();
sb.append(getClass().getSimpleName());
sb.append(" [").append(longitude);
sb.append(",").append(latitude);
sb.append(']');
return sb.toString();
}
}
}
/**
* {@link GeoLocation} representing a {@link Point} associated with a {@literal name}.
*
@@ -448,4 +1033,100 @@ public interface RedisGeoCommands {
return abbreviation;
}
}
/**
* Represents a geospatial bounding box defined by width and height.
*
* @author Mark Paluch
* @since 2.6
*/
class BoundingBox implements Shape {
private static final long serialVersionUID = 5215611530535947924L;
private final Distance width;
private final Distance height;
/**
* Creates a new {@link BoundingBox} from the given width and height. Both distances must use the same
* {@link Metric}.
*
* @param width must not be {@literal null}.
* @param height must not be {@literal null}.
*/
public BoundingBox(Distance width, Distance height) {
Assert.notNull(width, "Width must not be null!");
Assert.notNull(height, "Height must not be null!");
Assert.isTrue(width.getMetric().equals(height.getMetric()), "Metric for width and height must be the same!");
this.width = width;
this.height = height;
}
/**
* Creates a new {@link BoundingBox} from the given width, height and {@link Metric}.
*
* @param width
* @param height
* @param metric must not be {@literal null}.
*/
public BoundingBox(double width, double height, Metric metric) {
this(new Distance(width, metric), new Distance(height, metric));
}
/**
* Returns the width of this bounding box.
*
* @return will never be {@literal null}.
*/
public Distance getWidth() {
return height;
}
/**
* Returns the height of this bounding box.
*
* @return will never be {@literal null}.
*/
public Distance getHeight() {
return height;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(width);
result = 31 * result + ObjectUtils.nullSafeHashCode(height);
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BoundingBox)) {
return false;
}
BoundingBox that = (BoundingBox) o;
if (!ObjectUtils.nullSafeEquals(width, that.width)) {
return false;
}
return ObjectUtils.nullSafeEquals(height, that.height);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("Bounding box: [width=%s, height=%s]", width, height);
}
}
}

View File

@@ -2425,6 +2425,40 @@ public interface StringRedisConnection extends RedisConnection {
*/
Long geoRemove(String key, String... members);
/**
* Return the members of a geo set which are within the borders of the area specified by a given {@link GeoShape
* shape}. The query's center point is provided by
* {@link org.springframework.data.redis.connection.RedisGeoCommands.GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param predicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
GeoResults<GeoLocation<String>> geoSearch(String key, GeoReference<String> reference, GeoShape predicate,
GeoSearchCommandArgs args);
/**
* Query the members of a geo set which are within the borders of the area specified by a given {@link GeoShape shape}
* and store the result at {@code destKey}. The query's center point is provided by
* {@link org.springframework.data.redis.connection.RedisGeoCommands.GeoReference}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param predicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
Long geoSearchStore(String destKey, String key, GeoReference<String> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args);
// -------------------------------------------------------------------------
// Methods dealing with Redis Pub/Sub
// -------------------------------------------------------------------------

View File

@@ -281,6 +281,26 @@ class JedisClusterGeoCommands implements RedisGeoCommands {
return connection.zRem(key, members);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearch(byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
throw new UnsupportedOperationException("GEOSEARCH not supported through Jedis");
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearchStore(byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
*/
@Override
public Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
throw new UnsupportedOperationException("GEOSEARCHSTORE not supported through Jedis");
}
private DataAccessException convertJedisAccessException(Exception ex) {
return connection.convertJedisAccessException(ex);
}

View File

@@ -161,7 +161,6 @@ class JedisGeoCommands implements RedisGeoCommands {
Assert.notNull(members, "Members must not be null!");
Assert.noNullElements(members, "Members must not contain null!");
return connection.invoke().fromMany(BinaryJedis::geopos, MultiKeyPipelineBase::geopos, key, members)
.toList(JedisConverters::toPoint);
}
@@ -257,4 +256,24 @@ class JedisGeoCommands implements RedisGeoCommands {
public Long geoRemove(byte[] key, byte[]... members) {
return connection.zSetCommands().zRem(key, members);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearch(byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
throw new UnsupportedOperationException("GEOSEARCH not supported through Jedis");
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearchStore(byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
*/
@Override
public Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
throw new UnsupportedOperationException("GEOSEARCHSTORE not supported through Jedis");
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.redis.connection.lettuce;
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
import static org.springframework.data.redis.connection.RedisGeoCommands.GeoReference.*;
import io.lettuce.core.*;
import io.lettuce.core.cluster.models.partitions.Partitions;
import io.lettuce.core.cluster.models.partitions.RedisClusterNode.NodeFlag;
@@ -42,9 +45,6 @@ import org.springframework.data.redis.connection.BitFieldSubCommands.BitFieldSub
import org.springframework.data.redis.connection.RedisClusterNode.Flag;
import org.springframework.data.redis.connection.RedisClusterNode.LinkState;
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisNode.NodeType;
@@ -868,6 +868,17 @@ public abstract class LettuceConverters extends Converters {
* @since 1.8
*/
public static GeoArgs toGeoArgs(GeoRadiusCommandArgs args) {
return toGeoArgs((GeoSearchCommandArgs) args);
}
/**
* Convert {@link GeoRadiusCommandArgs} into {@link GeoArgs}.
*
* @param args
* @return
* @since 2.6
*/
public static GeoArgs toGeoArgs(GeoSearchCommandArgs args) {
GeoArgs geoArgs = new GeoArgs();
@@ -896,7 +907,35 @@ public abstract class LettuceConverters extends Converters {
}
if (args.hasLimit()) {
geoArgs.withCount(args.getLimit());
geoArgs.withCount(args.getLimit(), args.hasAnyLimit());
}
return geoArgs;
}
/**
* Convert {@link GeoRadiusCommandArgs} into {@link GeoArgs}.
*
* @param args
* @return
* @since 2.6
*/
static GeoArgs toGeoArgs(GeoSearchStoreCommandArgs args) {
GeoArgs geoArgs = new GeoArgs();
if (args.hasSortDirection()) {
switch (args.getSortDirection()) {
case ASC:
geoArgs.asc();
break;
case DESC:
geoArgs.desc();
break;
}
}
if (args.hasLimit()) {
geoArgs.withCount(args.getLimit(), args.hasLimit());
}
return geoArgs;
}
@@ -1114,6 +1153,41 @@ public abstract class LettuceConverters extends Converters {
return LMoveArgs.Builder.rightRight();
}
static GeoSearch.GeoPredicate toGeoPredicate(GeoShape predicate) {
if (predicate instanceof RadiusShape) {
Distance radius = ((RadiusShape) predicate).getRadius();
return GeoSearch.byRadius(radius.getValue(), toGeoArgsUnit(radius.getMetric()));
}
if (predicate instanceof BoxShape) {
BoxShape boxPredicate = (BoxShape) predicate;
BoundingBox boundingBox = boxPredicate.getBoundingBox();
return GeoSearch.byBox(boundingBox.getWidth().getValue(), boundingBox.getHeight().getValue(),
toGeoArgsUnit(boxPredicate.getMetric()));
}
throw new IllegalArgumentException(String.format("Cannot convert %s to Lettuce GeoPredicate", predicate));
}
static <T> GeoSearch.GeoRef<T> toGeoRef(GeoReference<T> reference) {
if (reference instanceof GeoSearchMemberReference) {
return GeoSearch.fromMember(((GeoSearchMemberReference<T>) reference).getMember());
}
if (reference instanceof GeoSearchCoordinateReference) {
GeoSearchCoordinateReference<?> coordinates = (GeoSearchCoordinateReference<?>) reference;
return GeoSearch.fromCoordinates(coordinates.getLongitude(), coordinates.getLatitude());
}
throw new IllegalArgumentException(String.format("Cannot convert %s to Lettuce GeoRef", reference));
}
/**
* @author Christoph Strobl
* @since 1.8

View File

@@ -16,6 +16,7 @@
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.GeoArgs;
import io.lettuce.core.GeoSearch;
import io.lettuce.core.GeoWithin;
import io.lettuce.core.api.async.RedisGeoAsyncCommands;
@@ -268,4 +269,46 @@ class LettuceGeoCommands implements RedisGeoCommands {
return connection.zSetCommands().zRem(key, values);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearch(byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<byte[]>> geoSearch(byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchCommandArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(reference, "Reference must not be null!");
Assert.notNull(predicate, "GeoPredicate must not be null!");
Assert.notNull(args, "GeoSearchCommandArgs must not be null!");
GeoSearch.GeoRef<byte[]> ref = LettuceConverters.toGeoRef(reference);
GeoSearch.GeoPredicate lettucePredicate = LettuceConverters.toGeoPredicate(predicate);
GeoArgs geoArgs = LettuceConverters.toGeoArgs(args);
return connection.invoke().from(RedisGeoAsyncCommands::geosearch, key, ref, lettucePredicate, geoArgs)
.get(LettuceConverters.geoRadiusResponseToGeoResultsConverter(predicate.getMetric()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisGeoCommands#geoSearchStore(byte[], byte[], org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
*/
@Override
public Long geoSearchStore(byte[] destKey, byte[] key, GeoReference<byte[]> reference, GeoShape predicate,
GeoSearchStoreCommandArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(reference, "Reference must not be null!");
Assert.notNull(predicate, "GeoPredicate must not be null!");
Assert.notNull(args, "GeoSearchCommandArgs must not be null!");
GeoSearch.GeoRef<byte[]> ref = LettuceConverters.toGeoRef(reference);
GeoSearch.GeoPredicate lettucePredicate = LettuceConverters.toGeoPredicate(predicate);
GeoArgs geoArgs = LettuceConverters.toGeoArgs(args);
return connection.invoke().just(
connection -> connection.geosearchstore(destKey, key, ref, lettucePredicate, geoArgs, args.isStoreDistance()));
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.GeoArgs;
import io.lettuce.core.GeoCoordinates;
import io.lettuce.core.GeoSearch;
import io.lettuce.core.GeoWithin;
import io.lettuce.core.Value;
import reactor.core.publisher.Flux;
@@ -205,6 +206,60 @@ class LettuceReactiveGeoCommands implements ReactiveGeoCommands {
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoSearch(Publisher)
*/
@Override
public Flux<CommandResponse<GeoSearchCommand, Flux<GeoResult<GeoLocation<ByteBuffer>>>>> geoSearch(
Publisher<GeoSearchCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).map(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
Assert.notNull(command.getReference(), "GeoReference must not be null!");
Assert.notNull(command.getShape(), "GeoShape must not be null!");
Assert.notNull(command.getArgs(), "Command args must not be null!");
GeoArgs geoArgs = command.getArgs().map(LettuceConverters::toGeoArgs).orElseGet(GeoArgs::new);
GeoSearch.GeoRef<ByteBuffer> ref = LettuceConverters.toGeoRef(command.getReference());
GeoSearch.GeoPredicate predicate = LettuceConverters.toGeoPredicate(command.getShape());
Flux<GeoResult<GeoLocation<ByteBuffer>>> result = cmd.geosearch(command.getKey(), ref, predicate, geoArgs)
.map(converter(command.getShape().getMetric())::convert);
return new CommandResponse<>(command, result);
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoSearchStore(Publisher)
*/
@Override
public Flux<NumericResponse<GeoSearchStoreCommand, Long>> geoSearchStore(Publisher<GeoSearchStoreCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
Assert.notNull(command.getDestKey(), "Destination key must not be null!");
Assert.notNull(command.getReference(), "GeoReference must not be null!");
Assert.notNull(command.getShape(), "GeoShape must not be null!");
Assert.notNull(command.getArgs(), "Command args must not be null!");
GeoArgs geoArgs = command.getArgs().map(LettuceConverters::toGeoArgs).orElseGet(GeoArgs::new);
Boolean storeDist = command.getArgs().map(RedisGeoCommands.GeoSearchStoreCommandArgs::isStoreDistance)
.orElse(false);
GeoSearch.GeoRef<ByteBuffer> ref = LettuceConverters.toGeoRef(command.getReference());
GeoSearch.GeoPredicate predicate = LettuceConverters.toGeoPredicate(command.getShape());
Mono<Long> result = cmd.geosearchstore(command.getDestKey(), command.getKey(), ref, predicate, geoArgs,
storeDist);
return result.map(it -> new NumericResponse<>(command, it));
}));
}
private Converter<GeoWithin<ByteBuffer>, GeoResult<GeoLocation<ByteBuffer>>> converter(Metric metric) {
return (source) -> {

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.core;
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
import java.util.List;
import java.util.Map;
@@ -23,8 +25,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.lang.Nullable;
/**
@@ -297,6 +298,8 @@ public interface BoundGeoOperations<K, M> extends BoundKeyOperations<K> {
return radius(within, args);
}
// TODO: Bound ops should not accept K key
/**
* Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
* {@literal radius}.
@@ -408,4 +411,191 @@ public interface BoundGeoOperations<K, M> extends BoundKeyOperations<K> {
default Long geoRemove(M... members) {
return remove(members);
}
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle}.
*
* @param within must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(Circle within) {
return search(GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius}.
*
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return search(reference, radius, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs}.
*
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchCommandArgs args) {
return search(reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box.
*
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox) {
return search(reference, boundingBox, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs}.
*
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox,
GeoSearchCommandArgs args) {
return search(reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs}.
*
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference, GeoShape geoPredicate,
GeoSearchCommandArgs args);
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle} and store results at {@code destKey}.
*
* @param within must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K destKey, Circle within) {
return searchAndStore(destKey, GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} and store results at {@code destKey}.
*
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return searchAndStore(destKey, reference, radius, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchStoreCommandArgs args) {
return searchAndStore(destKey, reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box and store results at {@code destKey}.
*
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox) {
return searchAndStore(destKey, reference, boundingBox, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox,
GeoSearchStoreCommandArgs args) {
return searchAndStore(destKey, reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference, GeoShape geoPredicate,
GeoSearchStoreCommandArgs args);
}

View File

@@ -24,6 +24,7 @@ import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
@@ -177,6 +178,26 @@ class DefaultBoundGeoOperations<K, M> extends DefaultBoundKeyOperations<K> imple
return ops.remove(getKey(), members);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundGeoOperations#search(org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<M>> search(RedisGeoCommands.GeoReference<M> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchCommandArgs args) {
return ops.search(getKey(), reference, geoPredicate, args);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundGeoOperations#searchAndStore(java.lang.Object, org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
*/
@Override
public Long searchAndStore(K destKey, RedisGeoCommands.GeoReference<M> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchStoreCommandArgs args) {
return ops.searchAndStore(getKey(), destKey, reference, geoPredicate, args);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundKeyOperations#getType()

View File

@@ -25,6 +25,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
@@ -241,4 +242,44 @@ class DefaultGeoOperations<K, M> extends AbstractOperations<K, M> implements Geo
byte[][] rawMembers = rawValues(members);
return execute(connection -> connection.zRem(rawKey, rawMembers), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.GeoOperations#search(java.lang.Object, org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchCommandArgs)
*/
@Override
public GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchCommandArgs args) {
byte[] rawKey = rawKey(key);
RedisGeoCommands.GeoReference<byte[]> rawMember = getGeoReference(reference);
GeoResults<GeoLocation<byte[]>> raw = execute(
connection -> connection.geoSearch(rawKey, rawMember, geoPredicate, args), true);
return deserializeGeoResults(raw);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.GeoOperations#searchAndStore(java.lang.Object, java.lang.Object, org.springframework.data.redis.connection.RedisGeoCommands.GeoReference, org.springframework.data.redis.connection.RedisGeoCommands.GeoShape, org.springframework.data.redis.connection.RedisGeoCommands.GeoSearchStoreCommandArgs)
*/
@Override
public Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchStoreCommandArgs args) {
byte[] rawKey = rawKey(key);
byte[] rawDestKey = rawKey(destKey);
RedisGeoCommands.GeoReference<byte[]> rawMember = getGeoReference(reference);
return execute(connection -> connection.geoSearchStore(rawDestKey, rawKey, rawMember, geoPredicate, args), true);
}
@SuppressWarnings("unchecked")
private RedisGeoCommands.GeoReference<byte[]> getGeoReference(RedisGeoCommands.GeoReference<M> reference) {
return reference instanceof RedisGeoCommands.GeoReference.GeoSearchMemberReference
? RedisGeoCommands.GeoReference
.fromMember(rawValue(((RedisGeoCommands.GeoReference.GeoSearchMemberReference<M>) reference).getMember()))
: (RedisGeoCommands.GeoReference<byte[]>) reference;
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.ReactiveGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.serializer.RedisSerializationContext;
@@ -334,6 +335,38 @@ class DefaultReactiveGeoOperations<K, V> implements ReactiveGeoOperations<K, V>
return template.createMono(connection -> connection.keyCommands().del(rawKey(key))).map(l -> l != 0);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveGeoOperations#search(K, RedisGeoCommands.GeoReference, GeoShape, GeoSearchCommandArgs)
*/
@Override
public Flux<GeoResult<GeoLocation<V>>> search(K key, RedisGeoCommands.GeoReference<V> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchCommandArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(reference, "GeoReference must not be null!");
RedisGeoCommands.GeoReference<ByteBuffer> rawReference = getGeoReference(reference);
return template.createFlux(connection -> connection.geoCommands()
.geoSearch(rawKey(key), rawReference, geoPredicate, args).map(this::readGeoResult));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveGeoOperations#searchAndStore(K, K, RedisGeoCommands.GeoReference, GeoShape, GeoSearchStoreCommandArgs)
*/
@Override
public Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<V> reference,
RedisGeoCommands.GeoShape geoPredicate, RedisGeoCommands.GeoSearchStoreCommandArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(reference, "GeoReference must not be null!");
RedisGeoCommands.GeoReference<ByteBuffer> rawReference = getGeoReference(reference);
return template.createMono(connection -> connection.geoCommands().geoSearchStore(rawKey(destKey), rawKey(key),
rawReference, geoPredicate, args));
}
private <T> Mono<T> createMono(Function<ReactiveGeoCommands, Publisher<T>> function) {
Assert.notNull(function, "Function must not be null!");
@@ -348,6 +381,14 @@ class DefaultReactiveGeoOperations<K, V> implements ReactiveGeoOperations<K, V>
return template.createFlux(connection -> function.apply(connection.geoCommands()));
}
@SuppressWarnings("unchecked")
private RedisGeoCommands.GeoReference<ByteBuffer> getGeoReference(RedisGeoCommands.GeoReference<V> reference) {
return reference instanceof RedisGeoCommands.GeoReference.GeoSearchMemberReference
? RedisGeoCommands.GeoReference
.fromMember(rawValue(((RedisGeoCommands.GeoReference.GeoSearchMemberReference<V>) reference).getMember()))
: (RedisGeoCommands.GeoReference<ByteBuffer>) reference;
}
private ByteBuffer rawKey(K key) {
return serializationContext.getKeySerializationPair().write(key);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.core;
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
import java.util.List;
import java.util.Map;
@@ -23,8 +25,8 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisGeoCommands.*;
import org.springframework.lang.Nullable;
/**
@@ -437,4 +439,204 @@ public interface GeoOperations<K, M> {
default Long geoRemove(K key, M... members) {
return remove(key, members);
}
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle}.
*
* @param key must not be {@literal null}.
* @param within must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(K key, Circle within) {
return search(key, GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return search(key, reference, radius, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchCommandArgs args) {
return search(key, reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference,
BoundingBox boundingBox) {
return search(key, reference, boundingBox, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
default GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox,
GeoSearchCommandArgs args) {
return search(key, reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
@Nullable
GeoResults<GeoLocation<M>> search(K key, RedisGeoCommands.GeoReference<M> reference,
GeoShape geoPredicate, GeoSearchCommandArgs args);
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param within must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K key, K destKey, Circle within) {
return searchAndStore(key, destKey, GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return searchAndStore(key, destKey, reference, radius, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchStoreCommandArgs args) {
return searchAndStore(key, destKey, reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox) {
return searchAndStore(key, destKey, reference, boundingBox, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
default Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, BoundingBox boundingBox,
GeoSearchStoreCommandArgs args) {
return searchAndStore(key, destKey, reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return never {@literal null} unless used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
@Nullable
Long searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, GeoShape geoPredicate,
GeoSearchStoreCommandArgs args);
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.core;
import static org.springframework.data.redis.connection.RedisGeoCommands.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -23,13 +25,13 @@ import java.util.List;
import java.util.Map;
import org.reactivestreams.Publisher;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisGeoCommands;
/**
* Reactive Redis operations for geo commands.
@@ -228,4 +230,193 @@ public interface ReactiveGeoOperations<K, M> {
* @param key must not be {@literal null}.
*/
Mono<Boolean> delete(K key);
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle}.
*
* @param key must not be {@literal null}.
* @param within must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<M>>> search(K key, Circle within) {
return search(key, GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<M>>> search(K key, RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return search(key, reference, radius, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<M>>> search(K key, RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchCommandArgs args) {
return search(key, reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<M>>> search(K key, RedisGeoCommands.GeoReference<M> reference,
BoundingBox boundingBox) {
return search(key, reference, boundingBox, GeoSearchCommandArgs.newGeoSearchArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
default Flux<GeoResult<GeoLocation<M>>> search(K key, RedisGeoCommands.GeoReference<M> reference,
BoundingBox boundingBox, GeoSearchCommandArgs args) {
return search(key, reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearch">Redis Documentation: GEOSEARCH</a>
*/
Flux<GeoResult<GeoLocation<M>>> search(K key, RedisGeoCommands.GeoReference<M> reference, GeoShape geoPredicate,
GeoSearchCommandArgs args);
/**
* Get the {@literal member}s within the boundaries of a given {@link Circle} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param within must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
default Mono<Long> searchAndStore(K key, K destKey, Circle within) {
return searchAndStore(key, destKey, GeoReference.fromCircle(within), GeoShape.byRadius(within.getRadius()),
GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
default Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius) {
return searchAndStore(key, destKey, reference, radius, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link Distance radius} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param radius must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
default Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, Distance radius,
GeoSearchStoreCommandArgs args) {
return searchAndStore(key, destKey, reference, GeoShape.byRadius(radius), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
default Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference,
BoundingBox boundingBox) {
return searchAndStore(key, destKey, reference, boundingBox, GeoSearchStoreCommandArgs.newGeoSearchStoreArgs());
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* bounding box applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param boundingBox must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
default Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference,
BoundingBox boundingBox, GeoSearchStoreCommandArgs args) {
return searchAndStore(key, destKey, reference, GeoShape.byBox(boundingBox), args);
}
/**
* Get the {@literal member}s using {@link GeoReference} as center of the query within the boundaries of a given
* {@link GeoShape predicate} applying {@link GeoRadiusCommandArgs} and store results at {@code destKey}.
*
* @param key must not be {@literal null}.
* @param reference must not be {@literal null}.
* @param geoPredicate must not be {@literal null}.
* @param args must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/geosearchstore">Redis Documentation: GEOSEARCHSTORE</a>
*/
Mono<Long> searchAndStore(K key, K destKey, RedisGeoCommands.GeoReference<M> reference, GeoShape geoPredicate,
GeoSearchStoreCommandArgs args);
}