diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index 9a7330ae71..dc7c4e754c 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -13,6 +13,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Expire; import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt; import static redis_request.RedisRequestOuterClass.RequestType.GetString; +import static redis_request.RedisRequestOuterClass.RequestType.HLen; import static redis_request.RedisRequestOuterClass.RequestType.HSetNX; import static redis_request.RedisRequestOuterClass.RequestType.HashDel; import static redis_request.RedisRequestOuterClass.RequestType.HashExists; @@ -356,6 +357,11 @@ public CompletableFuture hdel(@NonNull String key, @NonNull String[] field return commandManager.submitNewCommand(HashDel, args, this::handleLongResponse); } + @Override + public CompletableFuture hlen(@NonNull String key) { + return commandManager.submitNewCommand(HLen, new String[] {key}, this::handleLongResponse); + } + @Override public CompletableFuture hvals(@NonNull String key) { return commandManager.submitNewCommand( diff --git a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java index 5f1481fed9..b76dd96a2c 100644 --- a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java @@ -88,6 +88,24 @@ public interface HashBaseCommands { */ CompletableFuture hdel(String key, String[] fields); + /** + * Returns the number of fields contained in the hash stored at key. + * + * @see redis.io for details. + * @param key The key of the hash. + * @return The number of fields in the hash, or 0 when the key does not exist.
+ * If key holds a value that is not a hash, an error is returned. + * @example + *
{@code
+     * Long num1 = client.hlen("myHash").get();
+     * assert num1 == 3L;
+     *
+     * Long num2 = client.hlen("nonExistingKey").get();
+     * assert num2 == 0L;
+     * }
+ */ + CompletableFuture hlen(String key); + /** * Returns all values in the hash stored at key. * diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java index d02cc16420..0e84bd6007 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -21,6 +21,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Expire; import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt; import static redis_request.RedisRequestOuterClass.RequestType.GetString; +import static redis_request.RedisRequestOuterClass.RequestType.HLen; import static redis_request.RedisRequestOuterClass.RequestType.HSetNX; import static redis_request.RedisRequestOuterClass.RequestType.HashDel; import static redis_request.RedisRequestOuterClass.RequestType.HashExists; @@ -460,6 +461,22 @@ public T hdel(@NonNull String key, @NonNull String[] fields) { return getThis(); } + /** + * Returns the number of fields contained in the hash stored at key. + * + * @see redis.io for details. + * @param key The key of the hash. + * @return Command Response - The number of fields in the hash, or 0 when the key + * does not exist.
+ * If key holds a value that is not a hash, an error is returned. + */ + public T hlen(@NonNull String key) { + ArgsArray commandArgs = buildArgs(key); + + protobufTransaction.addCommands(buildCommand(HLen, commandArgs)); + return getThis(); + } + /** * Returns all values in the hash stored at key. * diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index 397bda7e56..7191ebed0b 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -33,6 +33,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Expire; import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt; import static redis_request.RedisRequestOuterClass.RequestType.GetString; +import static redis_request.RedisRequestOuterClass.RequestType.HLen; import static redis_request.RedisRequestOuterClass.RequestType.HSetNX; import static redis_request.RedisRequestOuterClass.RequestType.HashDel; import static redis_request.RedisRequestOuterClass.RequestType.HashExists; @@ -1051,6 +1052,29 @@ public void hdel_success() { assertEquals(value, payload); } + @SneakyThrows + @Test + public void hlen_success() { + // setup + String key = "testKey"; + String[] args = {key}; + Long value = 2L; + + CompletableFuture testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.submitNewCommand(eq(HLen), eq(args), any())).thenReturn(testResponse); + + // exercise + CompletableFuture response = service.hlen(key); + Long payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + @SneakyThrows @Test public void hvals_success() { diff --git a/java/client/src/test/java/glide/api/models/TransactionTests.java b/java/client/src/test/java/glide/api/models/TransactionTests.java index 123d8ed0eb..2beaf13e10 100644 --- a/java/client/src/test/java/glide/api/models/TransactionTests.java +++ b/java/client/src/test/java/glide/api/models/TransactionTests.java @@ -19,6 +19,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Expire; import static redis_request.RedisRequestOuterClass.RequestType.ExpireAt; import static redis_request.RedisRequestOuterClass.RequestType.GetString; +import static redis_request.RedisRequestOuterClass.RequestType.HLen; import static redis_request.RedisRequestOuterClass.RequestType.HSetNX; import static redis_request.RedisRequestOuterClass.RequestType.HashDel; import static redis_request.RedisRequestOuterClass.RequestType.HashExists; @@ -177,6 +178,9 @@ public void transaction_builds_protobuf_request(BaseTransaction transaction) transaction.hdel("key", new String[] {"field"}); results.add(Pair.of(HashDel, ArgsArray.newBuilder().addArgs("key").addArgs("field").build())); + transaction.hlen("key"); + results.add(Pair.of(HLen, ArgsArray.newBuilder().addArgs("key").build())); + transaction.hvals("key"); results.add(Pair.of(Hvals, ArgsArray.newBuilder().addArgs("key").build())); diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index f296e32c09..a9c76ba3d7 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -491,6 +491,30 @@ public void hdel_multiple_existing_fields_non_existing_field_non_existing_key(Ba assertEquals(0, client.hdel("non_existing_key", new String[] {field3}).get()); } + @SneakyThrows + @ParameterizedTest + @MethodSource("getClients") + public void hlen(BaseClient client) { + String key1 = UUID.randomUUID().toString(); + String key2 = UUID.randomUUID().toString(); + String field1 = UUID.randomUUID().toString(); + String field2 = UUID.randomUUID().toString(); + String value = UUID.randomUUID().toString(); + Map fieldValueMap = Map.of(field1, value, field2, value); + + assertEquals(2, client.hset(key1, fieldValueMap).get()); + assertEquals(2, client.hlen(key1).get()); + assertEquals(1, client.hdel(key1, new String[] {field1}).get()); + assertEquals(1, client.hlen(key1).get()); + assertEquals(0, client.hlen("nonExistingHash").get()); + + // Key exists, but it is not a hash + assertEquals(OK, client.set(key2, "value").get()); + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> client.hlen(key2).get()); + assertTrue(executionException.getCause() instanceof RequestException); + } + @SneakyThrows @ParameterizedTest @MethodSource("getClients") diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java index 67fa5f0422..76442dfdab 100644 --- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java +++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java @@ -60,6 +60,7 @@ public static BaseTransaction transactionTest(BaseTransaction baseTransact baseTransaction.hset(key4, Map.of(field1, value1, field2, value2)); baseTransaction.hget(key4, field1); + baseTransaction.hlen(key4); baseTransaction.hexists(key4, field2); baseTransaction.hsetnx(key4, field1, value1); baseTransaction.hmget(key4, new String[] {field1, "non_existing_field", field2}); @@ -132,6 +133,7 @@ public static Object[] transactionTestResult() { 1L, 2L, value1, + 2L, // hlen(key4) true, Boolean.FALSE, // hsetnx(key4, field1, value1) new String[] {value1, null, value2},