From 81259cd951998b8c1628ac699ff0547a9bdc6926 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 4 Mar 2021 14:51:21 +0100 Subject: [PATCH] Introduce GeoValue value type #1643 We now provide a GeoValue value object to encapsulate GeoCoordinates and the actual geo set member. The value can be used to geoadd members to the geo set or can be consumed as GeoValue from a GeoWithin result. --- .../core/AbstractRedisAsyncCommands.java | 10 + .../core/AbstractRedisReactiveCommands.java | 12 +- .../java/io/lettuce/core/GeoCoordinates.java | 3 +- src/main/java/io/lettuce/core/GeoSearch.java | 1 + src/main/java/io/lettuce/core/GeoValue.java | 186 ++++++++++++++++++ src/main/java/io/lettuce/core/GeoWithin.java | 12 +- .../io/lettuce/core/RedisCommandBuilder.java | 22 +++ src/main/java/io/lettuce/core/Value.java | 8 +- .../core/api/async/RedisGeoAsyncCommands.java | 24 ++- .../reactive/RedisGeoReactiveCommands.java | 24 ++- .../core/api/sync/RedisGeoCommands.java | 24 ++- .../async/NodeSelectionGeoAsyncCommands.java | 24 ++- .../api/sync/NodeSelectionGeoCommands.java | 24 ++- .../coroutines/RedisGeoCoroutinesCommands.kt | 23 ++- .../RedisGeoCoroutinesCommandsImpl.kt | 9 + .../io/lettuce/core/api/RedisGeoCommands.java | 23 ++- .../io/lettuce/core/GeoValueUnitTests.java | 73 +++++++ .../io/lettuce/core/ScoredValueUnitTests.java | 7 +- .../commands/GeoCommandIntegrationTests.java | 25 ++- 19 files changed, 503 insertions(+), 31 deletions(-) create mode 100644 src/main/java/io/lettuce/core/GeoValue.java create mode 100644 src/test/java/io/lettuce/core/GeoValueUnitTests.java diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 8ea03deef8..3752284b42 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -731,11 +731,21 @@ public RedisFuture geoadd(K key, Object... lngLatMember) { return geoadd(key, null, lngLatMember); } + @Override + public RedisFuture geoadd(K key, GeoValue... values) { + return dispatch(commandBuilder.geoadd(key, values, null)); + } + @Override public RedisFuture geoadd(K key, GeoAddArgs args, Object... lngLatMember) { return dispatch(commandBuilder.geoadd(key, lngLatMember, args)); } + @Override + public RedisFuture geoadd(K key, GeoAddArgs args, GeoValue... values) { + return dispatch(commandBuilder.geoadd(key, values, args)); + } + @Override public RedisFuture geodist(K key, V from, V to, GeoArgs.Unit unit) { return dispatch(commandBuilder.geodist(key, from, to, unit)); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 8e659007e8..142f53fc0e 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -786,7 +786,12 @@ public Mono geoadd(K key, double longitude, double latitude, V member, Geo @Override public Mono geoadd(K key, Object... lngLatMember) { - return geoadd(key, lngLatMember, null); + return createMono(() -> commandBuilder.geoadd(key, lngLatMember, null)); + } + + @Override + public Mono geoadd(K key, GeoValue... values) { + return createMono(() -> commandBuilder.geoadd(key, values, null)); } @Override @@ -794,6 +799,11 @@ public Mono geoadd(K key, GeoAddArgs args, Object... lngLatMember) { return createMono(() -> commandBuilder.geoadd(key, lngLatMember, args)); } + @Override + public Mono geoadd(K key, GeoAddArgs args, GeoValue... values) { + return createMono(() -> commandBuilder.geoadd(key, values, args)); + } + @Override public Mono geodist(K key, V from, V to, Unit unit) { return createMono(() -> commandBuilder.geodist(key, from, to, unit)); diff --git a/src/main/java/io/lettuce/core/GeoCoordinates.java b/src/main/java/io/lettuce/core/GeoCoordinates.java index 9cd60ffbbf..b807649fe4 100644 --- a/src/main/java/io/lettuce/core/GeoCoordinates.java +++ b/src/main/java/io/lettuce/core/GeoCoordinates.java @@ -18,7 +18,8 @@ import io.lettuce.core.internal.LettuceAssert; /** - * A tuple consisting of numerical geo data points to describe geo coordinates. + * A tuple consisting of numerical geo data points to describe geo coordinates (longitude/latitude coordinates according to + * WGS84). * * @author Mark Paluch */ diff --git a/src/main/java/io/lettuce/core/GeoSearch.java b/src/main/java/io/lettuce/core/GeoSearch.java index 50486c96d5..400482c15c 100644 --- a/src/main/java/io/lettuce/core/GeoSearch.java +++ b/src/main/java/io/lettuce/core/GeoSearch.java @@ -26,6 +26,7 @@ */ public final class GeoSearch { + // TODO: Should be V /** * Create a {@link GeoRef} from a Geo set {@code member}. * diff --git a/src/main/java/io/lettuce/core/GeoValue.java b/src/main/java/io/lettuce/core/GeoValue.java new file mode 100644 index 0000000000..396148e373 --- /dev/null +++ b/src/main/java/io/lettuce/core/GeoValue.java @@ -0,0 +1,186 @@ +/* + * Copyright 2011-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Function; + +import io.lettuce.core.internal.LettuceAssert; + +/** + * A Geo value extension to {@link Value}. + * + * @param Value type. + * @author Mark Paluch + * @since 6.1 + */ +@SuppressWarnings("serial") +public class GeoValue extends Value { + + private static final GeoValue EMPTY = new GeoValue<>(new GeoCoordinates(0, 0), null); + + private final GeoCoordinates coordinates; + + /** + * Serializable constructor. + */ + protected GeoValue() { + super(null); + this.coordinates = null; + } + + private GeoValue(GeoCoordinates coordinates, V value) { + super(value); + this.coordinates = coordinates; + } + + /** + * Returns an empty {@code GeoValue} instance. No value is present for this instance. + * + * @param + * @return the {@link GeoValue} + */ + @SuppressWarnings("unchecked") + public static GeoValue empty() { + return (GeoValue) EMPTY; + } + + /** + * Creates a {@link GeoValue} from a {@code key} and {@code value}. The resulting value contains the value. + * + * @param longitude the longitude coordinate according to WGS84. + * @param latitude the latitude coordinate according to WGS84. + * @param value the value. Must not be {@code null}. + * @param + * @param + * @return the {@link GeoValue} + */ + public static GeoValue just(double longitude, double latitude, T value) { + return new GeoValue<>(new GeoCoordinates(longitude, latitude), value); + } + + /** + * Creates a {@link GeoValue} from a {@code key} and {@code value}. The resulting value contains the value. + * + * @param coordinates the coordinates. + * @param value the value. Must not be {@code null}. + * @param + * @param + * @return the {@link GeoValue} + */ + public static GeoValue just(GeoCoordinates coordinates, T value) { + + LettuceAssert.notNull(coordinates, "GeoCoordinates must not be null"); + + return new GeoValue<>(coordinates, value); + } + + public GeoCoordinates getCoordinates() { + return coordinates; + } + + /** + * @return the longitude if this instance has a {@link #hasValue()}. + * @throws NoSuchElementException if the value is not present. + */ + public double getLongitude() { + + if (coordinates == null) { + throw new NoSuchElementException(); + } + + return coordinates.getX().doubleValue(); + } + + /** + * @return the latitude if this instance has a {@link #hasValue()}. + * @throws NoSuchElementException if the value is not present. + */ + public double getLatitude() { + + if (coordinates == null) { + throw new NoSuchElementException(); + } + + return coordinates.getY().doubleValue(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof GeoValue)) { + return false; + } + if (!super.equals(o)) { + return false; + } + GeoValue geoValue = (GeoValue) o; + return Objects.equals(coordinates, geoValue.coordinates); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), coordinates); + } + + @Override + public String toString() { + return hasValue() ? String.format("GeoValue[%s, %s]", coordinates, getValue()) + : String.format("GeoValue[%s].empty", coordinates); + } + + /** + * Returns a {@link GeoValue} consisting of the results of applying the given function to the value of this element. Mapping + * is performed only if a {@link #hasValue() value is present}. + * + * @param The element type of the new stream + * @param mapper a stateless function to apply to each element + * @return the new {@link GeoValue} + */ + @SuppressWarnings("unchecked") + public GeoValue map(Function mapper) { + + LettuceAssert.notNull(mapper, "Mapper function must not be null"); + + if (hasValue()) { + return new GeoValue<>(coordinates, mapper.apply(getValue())); + } + + return (GeoValue) this; + } + + /** + * Returns a {@link GeoValue} consisting of the results of applying the given function to the {@link GeoCoordinates} of this + * element. Mapping is performed only if a {@link #hasValue() value is present}. + * + * @param mapper a stateless function to apply to each element + * @return the new {@link GeoValue} + */ + public GeoValue mapCoordinates(Function mapper) { + + LettuceAssert.notNull(mapper, "Mapper function must not be null"); + + if (hasValue()) { + return new GeoValue<>(mapper.apply(coordinates), getValue()); + } + + return this; + } + +} diff --git a/src/main/java/io/lettuce/core/GeoWithin.java b/src/main/java/io/lettuce/core/GeoWithin.java index be194b7fb4..5e729be2cb 100644 --- a/src/main/java/io/lettuce/core/GeoWithin.java +++ b/src/main/java/io/lettuce/core/GeoWithin.java @@ -54,7 +54,6 @@ public GeoWithin(V member, Double distance, Long geohash, GeoCoordinates coordin } /** - * * @return the member within the Geo set. */ public V getMember() { @@ -62,7 +61,6 @@ public V getMember() { } /** - * * @return distance if requested otherwise {@code null}. */ public Double getDistance() { @@ -70,7 +68,6 @@ public Double getDistance() { } /** - * * @return geohash if requested otherwise {@code null}. */ public Long getGeohash() { @@ -78,13 +75,20 @@ public Long getGeohash() { } /** - * * @return coordinates if requested otherwise {@code null}. */ public GeoCoordinates getCoordinates() { return coordinates; } + /** + * @return a {@link GeoValue} if {@code coordinates} are set. + * @since 6.1 + */ + public GeoValue toValue() { + return GeoValue.just(coordinates, this.member); + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index 3d5405da9d..0feada934f 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -876,6 +876,28 @@ Command geoadd(K key, Object[] lngLatMember, GeoAddArgs geoArgs) { return createCommand(GEOADD, new IntegerOutput<>(codec), args); } + Command geoadd(K key, GeoValue[] values, GeoAddArgs geoArgs) { + + notNullKey(key); + LettuceAssert.notNull(values, "Values " + MUST_NOT_BE_NULL); + LettuceAssert.notEmpty(values, "Values " + MUST_NOT_BE_EMPTY); + LettuceAssert.noNullElements(values, "Values " + MUST_NOT_CONTAIN_NULL_ELEMENTS); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + + if (geoArgs != null) { + geoArgs.build(args); + } + + for (GeoValue value : values) { + args.add(value.getCoordinates().getX().doubleValue()); + args.add(value.getCoordinates().getY().doubleValue()); + args.addValue(value.getValue()); + } + + return createCommand(GEOADD, new IntegerOutput<>(codec), args); + } + Command geodist(K key, V from, V to, GeoArgs.Unit unit) { notNullKey(key); LettuceAssert.notNull(from, "From " + MUST_NOT_BE_NULL); diff --git a/src/main/java/io/lettuce/core/Value.java b/src/main/java/io/lettuce/core/Value.java index d7507728ef..be3a7d4a4d 100644 --- a/src/main/java/io/lettuce/core/Value.java +++ b/src/main/java/io/lettuce/core/Value.java @@ -72,7 +72,7 @@ public static Value from(Optional optional) { LettuceAssert.notNull(optional, "Optional must not be null"); if (optional.isPresent()) { - return new Value(optional.get()); + return new Value<>(optional.get()); } return (Value) EMPTY; @@ -92,7 +92,7 @@ public static Value fromNullable(T value) { return empty(); } - return new Value(value); + return new Value<>(value); } /** @@ -117,7 +117,7 @@ public static Value just(T value) { LettuceAssert.notNull(value, "Value must not be null"); - return new Value(value); + return new Value<>(value); } @Override @@ -234,7 +234,7 @@ public Value map(Function mapper) { LettuceAssert.notNull(mapper, "Mapper function must not be null"); if (hasValue()) { - return new Value(mapper.apply(getValue())); + return new Value<>(mapper.apply(getValue())); } return (Value) this; diff --git a/src/main/java/io/lettuce/core/api/async/RedisGeoAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisGeoAsyncCommands.java index 63e6e70520..070a235de6 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisGeoAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisGeoAsyncCommands.java @@ -23,6 +23,7 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoRadiusStoreArgs; import io.lettuce.core.GeoSearch; +import io.lettuce.core.GeoValue; import io.lettuce.core.GeoWithin; import io.lettuce.core.RedisFuture; import io.lettuce.core.Value; @@ -73,13 +74,34 @@ public interface RedisGeoAsyncCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + RedisFuture geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ RedisFuture geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + RedisFuture geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisGeoReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisGeoReactiveCommands.java index e815d631b5..f13be3828d 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisGeoReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisGeoReactiveCommands.java @@ -22,6 +22,7 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoRadiusStoreArgs; import io.lettuce.core.GeoSearch; +import io.lettuce.core.GeoValue; import io.lettuce.core.GeoWithin; import io.lettuce.core.Value; @@ -71,13 +72,34 @@ public interface RedisGeoReactiveCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + Mono geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ Mono geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + Mono geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/main/java/io/lettuce/core/api/sync/RedisGeoCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisGeoCommands.java index ee5efdf786..f565f58c04 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisGeoCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisGeoCommands.java @@ -23,6 +23,7 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoRadiusStoreArgs; import io.lettuce.core.GeoSearch; +import io.lettuce.core.GeoValue; import io.lettuce.core.GeoWithin; import io.lettuce.core.Value; @@ -72,13 +73,34 @@ public interface RedisGeoCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + Long geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ Long geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + Long geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionGeoAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionGeoAsyncCommands.java index 684c874438..175ddadbaf 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionGeoAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionGeoAsyncCommands.java @@ -23,6 +23,7 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoRadiusStoreArgs; import io.lettuce.core.GeoSearch; +import io.lettuce.core.GeoValue; import io.lettuce.core.GeoWithin; import io.lettuce.core.Value; @@ -72,13 +73,34 @@ public interface NodeSelectionGeoAsyncCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + AsyncExecutions geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ AsyncExecutions geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + AsyncExecutions geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionGeoCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionGeoCommands.java index ce5fb8845c..feba48d3ae 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionGeoCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionGeoCommands.java @@ -23,6 +23,7 @@ import io.lettuce.core.GeoCoordinates; import io.lettuce.core.GeoRadiusStoreArgs; import io.lettuce.core.GeoSearch; +import io.lettuce.core.GeoValue; import io.lettuce.core.GeoWithin; import io.lettuce.core.Value; @@ -72,13 +73,34 @@ public interface NodeSelectionGeoCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + Executions geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ Executions geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + Executions geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommands.kt index 54fe2f73ca..70f0d038a8 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommands.kt @@ -72,13 +72,34 @@ interface RedisGeoCoroutinesCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of Double longitude, Double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + suspend fun geoadd(key: K, vararg values: GeoValue): Long? + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values @link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ suspend fun geoadd(key: K, args: GeoAddArgs, vararg lngLatMember: Any): Long? + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values @link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + suspend fun geoadd(key: K, args: GeoAddArgs, vararg values: GeoValue): Long? + /** * Retrieve distance between points `from` and `to`. If one or more elements are missing `null` is * returned. Default in meters by, otherwise according to `unit` diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommandsImpl.kt index b617c5bac2..f59bcee7e4 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisGeoCoroutinesCommandsImpl.kt @@ -52,12 +52,21 @@ internal class RedisGeoCoroutinesCommandsImpl(internal val ops override suspend fun geoadd(key: K, vararg lngLatMember: Any): Long? = ops.geoadd(key, *lngLatMember).awaitFirstOrNull() + override suspend fun geoadd(key: K, vararg values: GeoValue): Long? = + ops.geoadd(key, *values).awaitFirstOrNull() + override suspend fun geoadd( key: K, args: GeoAddArgs, vararg lngLatMember: Any ): Long? = ops.geoadd(key, args, *lngLatMember).awaitFirstOrNull() + override suspend fun geoadd( + key: K, + args: GeoAddArgs, + vararg values: GeoValue + ): Long? = ops.geoadd(key, args, *values).awaitFirstOrNull() + override suspend fun geopos(key: K, vararg members: V): List = ops.geopos(key, *members).map { it.value }.asFlow().toList() diff --git a/src/main/templates/io/lettuce/core/api/RedisGeoCommands.java b/src/main/templates/io/lettuce/core/api/RedisGeoCommands.java index c8e3cb7d4c..ed96b065a3 100644 --- a/src/main/templates/io/lettuce/core/api/RedisGeoCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisGeoCommands.java @@ -64,13 +64,34 @@ public interface RedisGeoCommands { * Multi geo add. * * @param key the key of the geo set. - * @param args additional arguments. * @param lngLatMember triplets of double longitude, double latitude and V member. * @return Long integer-reply the number of elements that were added to the set. * @since 6.1 */ + Long geoadd(K key, GeoValue... values); + + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ Long geoadd(K key, GeoAddArgs args, Object... lngLatMember); + /** + * Multi geo add. + * + * @param key the key of the geo set. + * @param args additional arguments. + * @param values {@link io.lettuce.core.GeoValue} values to add. + * @return Long integer-reply the number of elements that were added to the set. + * @since 6.1 + */ + Long geoadd(K key, GeoAddArgs args, GeoValue... values); + /** * Retrieve distance between points {@code from} and {@code to}. If one or more elements are missing {@code null} is * returned. Default in meters by, otherwise according to {@code unit} diff --git a/src/test/java/io/lettuce/core/GeoValueUnitTests.java b/src/test/java/io/lettuce/core/GeoValueUnitTests.java new file mode 100644 index 0000000000..2f811f71c0 --- /dev/null +++ b/src/test/java/io/lettuce/core/GeoValueUnitTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2011-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link GeoValue}. + * + * @author Mark Paluch + */ +class GeoValueUnitTests { + + @Test + void shouldCreateEmptyValue() { + + GeoValue value = GeoValue.empty(); + + assertThat(value.hasValue()).isFalse(); + } + + @Test + void justShouldCreateValueFromValue() { + + GeoValue value = GeoValue.just(42, 43, "hello"); + + assertThat(value.hasValue()).isTrue(); + assertThat(value.getValue()).isEqualTo("hello"); + } + + @Test + void justShouldRejectEmptyValueFromValue() { + assertThatThrownBy(() -> GeoValue.just(null)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void equals() { + GeoValue sv1 = GeoValue.just(1.0, 2.0, "a"); + assertThat(sv1.equals(GeoValue.just(1.0, 2.0, "a"))).isTrue(); + assertThat(sv1.equals(null)).isFalse(); + assertThat(sv1.equals(GeoValue.just(1.1, 2.0, "a"))).isFalse(); + assertThat(sv1.equals(GeoValue.just(1.0, 2.0, "b"))).isFalse(); + } + + @Test + void testHashCode() { + assertThat(GeoValue.just(1.0, 2.0, "a").hashCode() != 0).isTrue(); + assertThat(GeoValue.just(0.0, 2.0, "a").hashCode() != 0).isTrue(); + } + + @Test + void toStringShouldRenderCorrectly() { + + assertThat(GeoValue.just(12, 34, "hello")).hasToString("GeoValue[(12.0, 34.0), hello]"); + assertThat(GeoValue.empty()).hasToString("GeoValue[(0, 0)].empty"); + } + +} diff --git a/src/test/java/io/lettuce/core/ScoredValueUnitTests.java b/src/test/java/io/lettuce/core/ScoredValueUnitTests.java index 639dd8f815..84f3dedbb6 100644 --- a/src/test/java/io/lettuce/core/ScoredValueUnitTests.java +++ b/src/test/java/io/lettuce/core/ScoredValueUnitTests.java @@ -1,4 +1,3 @@ - /* * Copyright 2011-2021 the original author or authors. * @@ -16,15 +15,15 @@ */ package io.lettuce.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.Assertions.offset; +import static org.assertj.core.api.Assertions.*; import java.util.Optional; import org.junit.jupiter.api.Test; /** + * Unit tests for {@link ScoredValue}. + * * @author Will Glozer * @author Mark Paluch */ diff --git a/src/test/java/io/lettuce/core/commands/GeoCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/GeoCommandIntegrationTests.java index 5aeafa7e6a..2ff4298699 100644 --- a/src/test/java/io/lettuce/core/commands/GeoCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/GeoCommandIntegrationTests.java @@ -28,16 +28,7 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.extension.ExtendWith; -import io.lettuce.core.GeoAddArgs; -import io.lettuce.core.GeoArgs; -import io.lettuce.core.GeoCoordinates; -import io.lettuce.core.GeoRadiusStoreArgs; -import io.lettuce.core.GeoSearch; -import io.lettuce.core.GeoWithin; -import io.lettuce.core.ScoredValue; -import io.lettuce.core.TestSupport; -import io.lettuce.core.TransactionResult; -import io.lettuce.core.Value; +import io.lettuce.core.*; import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; @@ -73,6 +64,15 @@ void geoadd() { assertThat(readd).isEqualTo(0); } + @Test + void geoaddValue() { + + redis.geoadd(key, GeoValue.just(8.6638775, 49.5282537, "Weinheim"), GeoValue.just(8.665351, 49.553302, "Bahn")); + + Set georadius = redis.georadius(key, 8.6582861, 49.5285695, 1, GeoArgs.Unit.km); + assertThat(georadius).hasSize(1).contains("Weinheim"); + } + @Test @EnabledOnCommand("XAUTOCLAIM") // Redis 6.2 void geoaddXXNXCH() { @@ -246,6 +246,11 @@ void georadiusWithArgs() { assertThat(weinheim.getMember()).isEqualTo("Weinheim"); assertThat(weinheim.getGeohash()).isEqualTo(3666615932941099L); + GeoValue value = weinheim.toValue(); + assertThat(value.getValue()).isEqualTo("Weinheim"); + assertThat(value.getLongitude()).isEqualTo(8.663875, offset(0.5)); + assertThat(value.getLatitude()).isEqualTo(49.52825, offset(0.5)); + assertThat(weinheim.getDistance()).isEqualTo(2.7882, offset(0.5)); assertThat(weinheim.getCoordinates().getX().doubleValue()).isEqualTo(8.663875, offset(0.5)); assertThat(weinheim.getCoordinates().getY().doubleValue()).isEqualTo(49.52825, offset(0.5));