Skip to content

Commit

Permalink
JAVA: Add command GEODIST (#272) (#1402)
Browse files Browse the repository at this point in the history
* JAVA: Add command GEODIST (#272)

* JAVA: Add command GEODIST

* allowing for overloaded signature to pass command with no metric option

* undo changes to examplesApp

* removing geoadd from geodist example

* converting comments to javadocs, removing explicit array creation & redundant test case

---------

Co-authored-by: TJ Zhang <tj.zhang@improving.com>

* fixing comment

* addressing review comments

* adding clickable link for metric value in javadoc

---------

Co-authored-by: TJ Zhang <tj.zhang@improving.com>
  • Loading branch information
tjzhang-BQ and TJ Zhang committed May 13, 2024
1 parent 3af09e0 commit 2f46606
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 1 deletion.
19 changes: 19 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Expire;
import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt;
import static redis_request.RedisRequestOuterClass.RequestType.GeoAdd;
import static redis_request.RedisRequestOuterClass.RequestType.GeoDist;
import static redis_request.RedisRequestOuterClass.RequestType.GeoPos;
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
Expand Down Expand Up @@ -135,6 +136,7 @@
import glide.api.models.commands.WeightAggregateOptions.KeysOrWeightedKeys;
import glide.api.models.commands.ZAddOptions;
import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamTrimOptions;
Expand Down Expand Up @@ -1241,6 +1243,23 @@ public CompletableFuture<Double[][]> geopos(@NonNull String key, @NonNull String
response -> castArrayofArrays(handleArrayResponse(response), Double.class));
}

@Override
public CompletableFuture<Double> geodist(
@NonNull String key,
@NonNull String member1,
@NonNull String member2,
@NonNull GeoUnit geoUnit) {
String[] arguments = new String[] {key, member1, member2, geoUnit.getRedisApi()};
return commandManager.submitNewCommand(GeoDist, arguments, this::handleDoubleOrNullResponse);
}

@Override
public CompletableFuture<Double> geodist(
@NonNull String key, @NonNull String member1, @NonNull String member2) {
String[] arguments = new String[] {key, member1, member2};
return commandManager.submitNewCommand(GeoDist, arguments, this::handleDoubleOrNullResponse);
}

@Override
public CompletableFuture<Long> bitcount(@NonNull String key) {
return commandManager.submitNewCommand(Bitcount, new String[] {key}, this::handleLongResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package glide.api.commands;

import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -78,4 +79,42 @@ CompletableFuture<Long> geoadd(
* }</pre>
*/
CompletableFuture<Double[][]> geopos(String key, String[] members);

/**
* Returns the distance between <code>member1</code> and <code>member2</code> saved in the
* geospatial index stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/geodist">valkey.io</a> for more details.
* @param key The key of the sorted set.
* @param member1 The name of the first member.
* @param member2 The name of the second member.
* @param geoUnit The unit of distance measurement - see {@link GeoUnit}.
* @return The distance between <code>member1</code> and <code>member2</code>. If one or both
* members do not exist, or if the key does not exist, returns <code>null</code>.
* @example
* <pre>{@code
* Double result = client.geodist("mySortedSet", "Palermo", "Catania", GeoUnit.KILOMETERS).get();
* System.out.println(result);
* }</pre>
*/
CompletableFuture<Double> geodist(String key, String member1, String member2, GeoUnit geoUnit);

/**
* Returns the distance between <code>member1</code> and <code>member2</code> saved in the
* geospatial index stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/geodist">valkey.io</a> for more details.
* @param key The key of the sorted set.
* @param member1 The name of the first member.
* @param member2 The name of the second member.
* @return The distance between <code>member1</code> and <code>member2</code>. If one or both
* members do not exist, or if the key does not exist, returns <code>null</code>. The default
* unit is {@see GeoUnit#METERS}.
* @example
* <pre>{@code
* Double result = client.geodist("mySortedSet", "Palermo", "Catania").get();
* System.out.println(result);
* }</pre>
*/
CompletableFuture<Double> geodist(String key, String member1, String member2);
}
42 changes: 42 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.GeoAdd;
import static redis_request.RedisRequestOuterClass.RequestType.GeoDist;
import static redis_request.RedisRequestOuterClass.RequestType.GeoPos;
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
Expand Down Expand Up @@ -154,6 +155,7 @@
import glide.api.models.commands.WeightAggregateOptions.WeightedKeys;
import glide.api.models.commands.ZAddOptions;
import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamAddOptions.StreamAddOptionsBuilder;
Expand Down Expand Up @@ -3037,6 +3039,46 @@ public T geopos(@NonNull String key, @NonNull String[] members) {
return getThis();
}

/**
* Returns the distance between <code>member1</code> and <code>member2</code> saved in the
* geospatial index stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/geodist">valkey.io</a> for more details.
* @param key The key of the sorted set.
* @param member1 The name of the first member.
* @param member2 The name of the second member.
* @param geoUnit The unit of distance measurement - see {@link GeoUnit}.
* @return Command Response - The distance between <code>member1</code> and <code>member2</code>.
* If one or both members do not exist or if the key does not exist returns <code>null</code>.
*/
public T geodist(
@NonNull String key,
@NonNull String member1,
@NonNull String member2,
@NonNull GeoUnit geoUnit) {
ArgsArray commandArgs = buildArgs(key, member1, member2, geoUnit.getRedisApi());
protobufTransaction.addCommands(buildCommand(GeoDist, commandArgs));
return getThis();
}

/**
* Returns the distance between <code>member1</code> and <code>member2</code> saved in the
* geospatial index stored at <code>key</code>.
*
* @see <a href="https://valkey.io/commands/geodist">valkey.io</a> for more details.
* @param key The key of the sorted set.
* @param member1 The name of the first member.
* @param member2 The name of the second member.
* @return Command Response - The distance between <code>member1</code> and <code>member2</code>.
* If one or both members do not exist or if the key does not exist returns <code>null</code>.
* The default unit is {@see GeoUnit#METERS}.
*/
public T geodist(@NonNull String key, @NonNull String member1, @NonNull String member2) {
ArgsArray commandArgs = buildArgs(key, member1, member2);
protobufTransaction.addCommands(buildCommand(GeoDist, commandArgs));
return getThis();
}

/** Build protobuf {@link Command} object for given command and arguments. */
protected Command buildCommand(RequestType requestType) {
return buildCommand(requestType, buildArgs());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models.commands.geospatial;

import glide.api.commands.GeospatialIndicesBaseCommands;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

/**
* Enumeration representing distance units options for the {@link
* GeospatialIndicesBaseCommands#geodist(String, String, String, GeoUnit)} command.
*/
@RequiredArgsConstructor
@Getter
public enum GeoUnit {
/** Represents distance in meters. */
METERS("m"),
/** Represents distance in kilometers. */
KILOMETERS("km"),
/** Represents distance in miles. */
MILES("mi"),
/** Represents distance in feet. */
FEET("ft");

private final String redisApi;
}
55 changes: 55 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.GeoAdd;
import static redis_request.RedisRequestOuterClass.RequestType.GeoDist;
import static redis_request.RedisRequestOuterClass.RequestType.GeoPos;
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
Expand Down Expand Up @@ -169,6 +170,7 @@
import glide.api.models.commands.WeightAggregateOptions.WeightedKeys;
import glide.api.models.commands.ZAddOptions;
import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamTrimOptions;
Expand Down Expand Up @@ -4186,6 +4188,59 @@ public void geopos_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void geodist_returns_success() {
// setup
String key = "testKey";
String member1 = "Catania";
String member2 = "Palermo";
String[] arguments = new String[] {key, member1, member2};
Double value = 166274.1516;

CompletableFuture<Double> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Double>submitNewCommand(eq(GeoDist), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Double> response = service.geodist(key, member1, member2);
Double payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void geodist_with_metrics_returns_success() {
// setup
String key = "testKey";
String member1 = "Catania";
String member2 = "Palermo";
GeoUnit geoUnit = GeoUnit.KILOMETERS;
String[] arguments = new String[] {key, member1, member2, GeoUnit.KILOMETERS.getRedisApi()};
Double value = 166.2742;

CompletableFuture<Double> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Double>submitNewCommand(eq(GeoDist), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Double> response = service.geodist(key, member1, member2, geoUnit);
Double payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void bitcount_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt;
import static redis_request.RedisRequestOuterClass.RequestType.FlushAll;
import static redis_request.RedisRequestOuterClass.RequestType.GeoAdd;
import static redis_request.RedisRequestOuterClass.RequestType.GeoDist;
import static redis_request.RedisRequestOuterClass.RequestType.GeoPos;
import static redis_request.RedisRequestOuterClass.RequestType.Get;
import static redis_request.RedisRequestOuterClass.RequestType.GetRange;
Expand Down Expand Up @@ -153,6 +154,7 @@
import glide.api.models.commands.WeightAggregateOptions.WeightedKeys;
import glide.api.models.commands.ZAddOptions;
import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamTrimOptions.MinId;
Expand Down Expand Up @@ -714,6 +716,11 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
transaction.geopos("key", new String[] {"Place"});
results.add(Pair.of(GeoPos, buildArgs("key", "Place")));

transaction.geodist("key", "Place", "Place2");
results.add(Pair.of(GeoDist, buildArgs("key", "Place", "Place2")));
transaction.geodist("key", "Place", "Place2", GeoUnit.KILOMETERS);
results.add(Pair.of(GeoDist, buildArgs("key", "Place", "Place2", "km")));

transaction.bitcount("key");
results.add(Pair.of(Bitcount, buildArgs("key")));

Expand Down
40 changes: 40 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import glide.api.models.commands.WeightAggregateOptions.WeightedKeys;
import glide.api.models.commands.ZAddOptions;
import glide.api.models.commands.geospatial.GeoAddOptions;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamTrimOptions.MaxLen;
Expand Down Expand Up @@ -3569,6 +3570,45 @@ public void geopos(BaseClient client) {
assertTrue(executionException.getCause() instanceof RequestException);
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void geodist(BaseClient client) {
String key1 = UUID.randomUUID().toString();
String key2 = UUID.randomUUID().toString();
String member1 = "Palermo";
String member2 = "Catania";
String member3 = "NonExisting";
GeoUnit geoUnitKM = GeoUnit.KILOMETERS;
Double expected = 166274.1516;
Double expectedKM = 166.2742;
Double delta = 1e-9;

// adding locations
Map<String, GeospatialData> membersToCoordinates = new HashMap<>();
membersToCoordinates.put("Palermo", new GeospatialData(13.361389, 38.115556));
membersToCoordinates.put("Catania", new GeospatialData(15.087269, 37.502669));
assertEquals(2, client.geoadd(key1, membersToCoordinates).get());

// assert correct result with default metric
Double actual = client.geodist(key1, member1, member2).get();
assertEquals(expected, actual, delta);

// assert correct result with manual metric specification kilometers
Double actualKM = client.geodist(key1, member1, member2, geoUnitKM).get();
assertEquals(expectedKM, actualKM, delta);

// assert null result when member index is missing
Double actualMissing = client.geodist(key1, member1, member3).get();
assertNull(actualMissing);

// key exists but holds a non-ZSET value
assertEquals(OK, client.set(key2, "geodist").get());
ExecutionException executionException =
assertThrows(ExecutionException.class, () -> client.geodist(key2, member1, member2).get());
assertTrue(executionException.getCause() instanceof RequestException);
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import glide.api.models.commands.SetOptions;
import glide.api.models.commands.WeightAggregateOptions.Aggregate;
import glide.api.models.commands.WeightAggregateOptions.KeyArray;
import glide.api.models.commands.geospatial.GeoUnit;
import glide.api.models.commands.geospatial.GeospatialData;
import glide.api.models.commands.stream.StreamAddOptions;
import glide.api.models.commands.stream.StreamTrimOptions.MinId;
Expand Down Expand Up @@ -478,13 +479,17 @@ private static Object[] geospatialCommands(BaseTransaction<?> transaction) {
"Catania",
new GeospatialData(15.087269, 37.502669)))
.geopos(geoKey1, new String[] {"Palermo", "Catania"});
transaction.geodist(geoKey1, "Palermo", "Catania");
transaction.geodist(geoKey1, "Palermo", "Catania", GeoUnit.KILOMETERS);

return new Object[] {
2L, // geoadd(geoKey1, Map.of("Palermo", ..., "Catania", ...))
new Double[][] {
{13.36138933897018433, 38.11555639549629859},
{15.08726745843887329, 37.50266842333162032},
}, // geopos(new String[]{"Palermo", "Catania"})
}, // geopos(geoKey1, new String[]{"Palermo", "Catania"})
166274.1516, // geodist(geoKey1, "Palermo", "Catania")
166.2742, // geodist(geoKey1, "Palermo", "Catania", GeoUnit.KILOMETERS)
};
}

Expand Down

0 comments on commit 2f46606

Please sign in to comment.