Skip to content

Commit

Permalink
added hrandfield binary functions (#1761)
Browse files Browse the repository at this point in the history
* added hrandfield binary functions

* fixed spotless

* fixed missing function

* spotless
  • Loading branch information
zarkash-aws authored Jul 3, 2024
1 parent 867cd9b commit 3168f34
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
28 changes: 28 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1133,6 +1137,12 @@ public CompletableFuture<String> hrandfield(@NonNull String key) {
HRandField, new String[] {key}, this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<GlideString> hrandfield(@NonNull GlideString key) {
return commandManager.submitNewCommand(
HRandField, new GlideString[] {key}, this::handleGlideStringOrNullResponse);
}

@Override
public CompletableFuture<String[]> hrandfieldWithCount(@NonNull String key, long count) {
return commandManager.submitNewCommand(
Expand All @@ -1141,6 +1151,15 @@ public CompletableFuture<String[]> hrandfieldWithCount(@NonNull String key, long
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<GlideString[]> 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<String[][]> hrandfieldWithCountWithValues(
@NonNull String key, long count) {
Expand All @@ -1150,6 +1169,15 @@ public CompletableFuture<String[][]> hrandfieldWithCountWithValues(
response -> castArrayofArrays(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<GlideString[][]> 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<Long> lpush(@NonNull String key, @NonNull String[] elements) {
String[] arguments = ArrayUtils.addFirst(elements, key);
Expand Down
58 changes: 58 additions & 0 deletions java/client/src/main/java/glide/api/commands/HashBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,22 @@ public interface HashBaseCommands {
*/
CompletableFuture<String> hrandfield(String key);

/**
* Returns a random field name from the hash value stored at <code>key</code>.
*
* @since Redis 6.2 and above.
* @see <a href="https://redis.io/commands/hrandfield/">redis.io</a> for details.
* @param key The key of the hash.
* @return A random field name from the hash stored at <code>key</code>, or <code>null</code> when
* the key does not exist.
* @example
* <pre>{@code
* GlideString field = client.hrandfield(gs("my_hash")).get();
* System.out.printf("A random field from the hash is '%s'", field);
* }</pre>
*/
CompletableFuture<GlideString> hrandfield(GlideString key);

/**
* Retrieves up to <code>count</code> random field names from the hash value stored at <code>key
* </code>.
Expand All @@ -533,6 +549,26 @@ public interface HashBaseCommands {
*/
CompletableFuture<String[]> hrandfieldWithCount(String key, long count);

/**
* Retrieves up to <code>count</code> random field names from the hash value stored at <code>key
* </code>.
*
* @since Redis 6.2 and above.
* @see <a href="https://redis.io/commands/hrandfield/">redis.io</a> for details.
* @param key The key of the hash.
* @param count The number of field names to return.<br>
* If <code>count</code> is positive, returns unique elements.<br>
* If negative, allows for duplicates.
* @return An <code>array</code> of random field names from the hash stored at <code>key</code>,
* or an <code>empty array</code> when the key does not exist.
* @example
* <pre>{@code
* GlideString[] fields = client.hrandfieldWithCount(gs("my_hash"), 10).get();
* System.out.printf("Random fields from the hash are '%s'", GlideString.join(", ", fields));
* }</pre>
*/
CompletableFuture<GlideString[]> hrandfieldWithCount(GlideString key, long count);

/**
* Retrieves up to <code>count</code> random field names along with their values from the hash
* value stored at <code>key</code>.
Expand All @@ -555,6 +591,28 @@ public interface HashBaseCommands {
*/
CompletableFuture<String[][]> hrandfieldWithCountWithValues(String key, long count);

/*
* Retrieves up to <code>count</code> random field names along with their values from the hash
* value stored at <code>key</code>.
*
* @since Redis 6.2 and above.
* @see <a href="https://redis.io/commands/hrandfield/">redis.io</a> for details.
* @param key The key of the hash.
* @param count The number of field names to return.<br>
* If <code>count</code> is positive, returns unique elements.<br>
* If negative, allows for duplicates.
* @return A 2D <code>array</code> of <code>[fieldName, value]</code> <code>arrays</code>, where
* <code>fieldName</code> is a random field name from the hash and <code>value</code> is the
* associated value of the field name.<br>
* If the hash does not exist or is empty, the response will be an empty <code>array</code>.
* @example
* <pre>{@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]);
* }</pre>
*/
CompletableFuture<GlideString[][]> hrandfieldWithCountWithValues(GlideString key, long count);

/**
* Iterates fields of Hash types and their associated values.
*
Expand Down
59 changes: 59 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 3168f34

Please sign in to comment.