diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index fa3ba82abe..89fbb1d547 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -551,6 +551,10 @@ protected Object[] handleArrayResponse(Response response) throws RedisException return handleRedisResponse(Object[].class, EnumSet.of(ResponseFlags.ENCODING_UTF8), response); } + protected Object[] handleArrayResponseBinary(Response response) throws RedisException { + return handleRedisResponse(Object[].class, EnumSet.noneOf(ResponseFlags.class), response); + } + protected Object[] handleArrayOrNullResponse(Response response) throws RedisException { return handleRedisResponse( Object[].class, @@ -1133,6 +1137,12 @@ public CompletableFuture hrandfield(@NonNull String key) { HRandField, new String[] {key}, this::handleStringOrNullResponse); } + @Override + public CompletableFuture hrandfield(@NonNull GlideString key) { + return commandManager.submitNewCommand( + HRandField, new GlideString[] {key}, this::handleGlideStringOrNullResponse); + } + @Override public CompletableFuture hrandfieldWithCount(@NonNull String key, long count) { return commandManager.submitNewCommand( @@ -1141,6 +1151,15 @@ public CompletableFuture hrandfieldWithCount(@NonNull String key, long response -> castArray(handleArrayResponse(response), String.class)); } + @Override + public CompletableFuture hrandfieldWithCount( + @NonNull GlideString key, long count) { + return commandManager.submitNewCommand( + HRandField, + new GlideString[] {key, GlideString.of(count)}, + response -> castArray(handleArrayResponseBinary(response), GlideString.class)); + } + @Override public CompletableFuture hrandfieldWithCountWithValues( @NonNull String key, long count) { @@ -1150,6 +1169,15 @@ public CompletableFuture hrandfieldWithCountWithValues( response -> castArrayofArrays(handleArrayResponse(response), String.class)); } + @Override + public CompletableFuture hrandfieldWithCountWithValues( + @NonNull GlideString key, long count) { + return commandManager.submitNewCommand( + HRandField, + new GlideString[] {key, GlideString.of(count), GlideString.of(WITH_VALUES_REDIS_API)}, + response -> castArrayofArrays(handleArrayResponseBinary(response), GlideString.class)); + } + @Override public CompletableFuture lpush(@NonNull String key, @NonNull String[] elements) { String[] arguments = ArrayUtils.addFirst(elements, key); 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 41c07c7f13..02cf48d996 100644 --- a/java/client/src/main/java/glide/api/commands/HashBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/HashBaseCommands.java @@ -513,6 +513,22 @@ public interface HashBaseCommands { */ CompletableFuture hrandfield(String key); + /** + * Returns a random field name from the hash value stored at key. + * + * @since Redis 6.2 and above. + * @see redis.io for details. + * @param key The key of the hash. + * @return A random field name from the hash stored at key, or null when + * the key does not exist. + * @example + *
{@code
+     * GlideString field = client.hrandfield(gs("my_hash")).get();
+     * System.out.printf("A random field from the hash is '%s'", field);
+     * }
+ */ + CompletableFuture hrandfield(GlideString key); + /** * Retrieves up to count random field names from the hash value stored at key * . @@ -533,6 +549,26 @@ public interface HashBaseCommands { */ CompletableFuture hrandfieldWithCount(String key, long count); + /** + * Retrieves up to count random field names from the hash value stored at key + * . + * + * @since Redis 6.2 and above. + * @see redis.io for details. + * @param key The key of the hash. + * @param count The number of field names to return.
+ * If count is positive, returns unique elements.
+ * If negative, allows for duplicates. + * @return An array of random field names from the hash stored at key, + * or an empty array when the key does not exist. + * @example + *
{@code
+     * GlideString[] fields = client.hrandfieldWithCount(gs("my_hash"), 10).get();
+     * System.out.printf("Random fields from the hash are '%s'", GlideString.join(", ", fields));
+     * }
+ */ + CompletableFuture hrandfieldWithCount(GlideString key, long count); + /** * Retrieves up to count random field names along with their values from the hash * value stored at key. @@ -555,6 +591,28 @@ public interface HashBaseCommands { */ CompletableFuture hrandfieldWithCountWithValues(String key, long count); + /* + * Retrieves up to count random field names along with their values from the hash + * value stored at key. + * + * @since Redis 6.2 and above. + * @see redis.io for details. + * @param key The key of the hash. + * @param count The number of field names to return.
+ * If count is positive, returns unique elements.
+ * If negative, allows for duplicates. + * @return A 2D array of [fieldName, value] arrays, where + * fieldName is a random field name from the hash and value is the + * associated value of the field name.
+ * If the hash does not exist or is empty, the response will be an empty array. + * @example + *
{@code
+     * GlideString[][] fields = client.hrandfieldWithCountWithValues(gs("my_hash"), 1).get();
+     * System.out.printf("A random field from the hash is '%s' and the value is '%s'", fields[0][0], fields[0][1]);
+     * }
+ */ + CompletableFuture hrandfieldWithCountWithValues(GlideString key, long count); + /** * Iterates fields of Hash types and their associated values. * diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index 73cbaadfe2..1745aed99d 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -1427,6 +1427,65 @@ public void hrandfield(BaseClient client) { assertInstanceOf(RequestException.class, executionException.getCause()); } + @SneakyThrows + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getClients") + public void hrandfieldBinary(BaseClient client) { + byte[] binvalue1 = {(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02}; + byte[] binvalue2 = {(byte) 0xFF, (byte) 0x66, (byte) 0xFF, (byte) 0xAF, (byte) 0x22}; + + GlideString key1 = gs(binvalue1); + GlideString key2 = gs(binvalue2); + + // key does not exist + assertNull(client.hrandfield(key1).get()); + assertEquals(0, client.hrandfieldWithCount(key1, 5).get().length); + assertEquals(0, client.hrandfieldWithCountWithValues(key1, 5).get().length); + + var data = Map.of(gs("f 1"), gs("v 1"), gs("f 2"), gs("v 2"), gs("f 3"), gs("v 3")); + assertEquals(3, client.hset(key1, data).get()); + + // random key + assertTrue(data.containsKey(client.hrandfield(key1).get())); + + // WithCount - positive count + var keys = client.hrandfieldWithCount(key1, 5).get(); + assertEquals(data.keySet().size(), keys.length); + assertEquals(data.keySet(), Set.of(keys)); + + // WithCount - negative count + keys = client.hrandfieldWithCount(key1, -5).get(); + assertEquals(5, keys.length); + Arrays.stream(keys).forEach(key -> assertTrue(data.containsKey(key))); + + // WithCountWithValues - positive count + var keysWithValues = client.hrandfieldWithCountWithValues(key1, 5).get(); + assertEquals(data.keySet().size(), keysWithValues.length); + for (var pair : keysWithValues) { + assertEquals(data.get(pair[0]), pair[1]); + } + + // WithCountWithValues - negative count + keysWithValues = client.hrandfieldWithCountWithValues(key1, -5).get(); + assertEquals(5, keysWithValues.length); + for (var pair : keysWithValues) { + assertEquals(data.get(pair[0]), pair[1]); + } + + // Key exists, but it is not a List + assertEquals(OK, client.set(key2, gs("value")).get()); + Exception executionException = + assertThrows(ExecutionException.class, () -> client.hrandfield(key2).get()); + assertInstanceOf(RequestException.class, executionException.getCause()); + executionException = + assertThrows(ExecutionException.class, () -> client.hrandfieldWithCount(key2, 2).get()); + assertInstanceOf(RequestException.class, executionException.getCause()); + executionException = + assertThrows( + ExecutionException.class, () -> client.hrandfieldWithCountWithValues(key2, 3).get()); + assertInstanceOf(RequestException.class, executionException.getCause()); + } + @SneakyThrows @ParameterizedTest(autoCloseArguments = false) @MethodSource("getClients")