Skip to content

Commit

Permalink
Java: Added Hexists command for BaseClient and BaseTransaction (Hash …
Browse files Browse the repository at this point in the history
…Commands) (valkey-io#1016)

* Java: Added Hexists command for BaseClient and BaseTransaction (Hash Commands)

* Added examples to documentation.

* Minor fixes based on PR review.

* Minor updates based on PR comments.

* Minor update to TransactionTestUtilities.
  • Loading branch information
SanHalacogluImproving authored Feb 22, 2024
1 parent e264963 commit e71c8e6
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 0 deletions.
11 changes: 11 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Exists;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.HashDel;
import static redis_request.RedisRequestOuterClass.RequestType.HashExists;
import static redis_request.RedisRequestOuterClass.RequestType.HashGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashMGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashSet;
Expand Down Expand Up @@ -174,6 +175,10 @@ protected String handleStringOrNullResponse(Response response) throws RedisExcep
return handleRedisResponse(String.class, true, response);
}

protected Boolean handleBooleanResponse(Response response) throws RedisException {
return handleRedisResponse(Boolean.class, false, response);
}

protected Long handleLongResponse(Response response) throws RedisException {
return handleRedisResponse(Long.class, false, response);
}
Expand Down Expand Up @@ -305,6 +310,12 @@ public CompletableFuture<String[]> hmget(@NonNull String key, @NonNull String[]
HashMGet, arguments, response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<Boolean> hexists(@NonNull String key, @NonNull String field) {
return commandManager.submitNewCommand(
HashExists, new String[] {key, field}, this::handleBooleanResponse);
}

@Override
public CompletableFuture<Long> sadd(String key, String[] members) {
String[] arguments = ArrayUtils.addFirst(members, key);
Expand Down
18 changes: 18 additions & 0 deletions java/client/src/main/java/glide/api/commands/HashCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,22 @@ public interface HashCommands {
* </pre>
*/
CompletableFuture<String[]> hmget(String key, String[] fields);

/**
* Returns if <code>field</code> is an existing field in the hash stored at <code>key</code>.
*
* @see <a href="https://redis.io/commands/hexists/">redis.io</a> for details.
* @param key The key of the hash.
* @param field The field to check in the hash stored at <code>key</code>.
* @return <code>True</code> if the hash contains the specified field. If the hash does not
* contain the field, or if the key does not exist, it returns <code>False</code>.
* @example
* <pre>
* Boolean exists = client.hexists("my_hash", "field1").get()
* assert exists
* Boolean exists = client.hexists("my_hash", "non_existent_field").get()
* assert !exists
* </pre>
*/
CompletableFuture<Boolean> hexists(String key, String field);
}
18 changes: 18 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 @@ -9,6 +9,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Exists;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.HashDel;
import static redis_request.RedisRequestOuterClass.RequestType.HashExists;
import static redis_request.RedisRequestOuterClass.RequestType.HashGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashMGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashSet;
Expand Down Expand Up @@ -382,6 +383,23 @@ public T hmget(@NonNull String key, @NonNull String[] fields) {
return getThis();
}

/**
* Returns if <code>field</code> is an existing field in the hash stored at <code>key</code>.
*
* @see <a href="https://redis.io/commands/hexists/">redis.io</a> for details.
* @param key The key of the hash.
* @param field The field to check in the hash stored at <code>key</code>.
* @return Command Response - <code>True</code> if the hash contains the specified field. If the
* hash does not contain the field, or if the key does not exist, it returns <code>False
* </code>.
*/
public T hexists(@NonNull String key, @NonNull String field) {
ArgsArray commandArgs = buildArgs(key, field);

protobufTransaction.addCommands(buildCommand(HashExists, commandArgs));
return getThis();
}

/**
* Add specified members to the set stored at <code>key</code>. Specified members that are already
* a member of this set are ignored.
Expand Down
26 changes: 26 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Exists;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.HashDel;
import static redis_request.RedisRequestOuterClass.RequestType.HashExists;
import static redis_request.RedisRequestOuterClass.RequestType.HashGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashMGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashSet;
Expand Down Expand Up @@ -594,6 +595,31 @@ public void hmget_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void hexists_success() {
// setup
String key = "testKey";
String field = "testField";
String[] args = new String[] {key, field};
Boolean value = true;

CompletableFuture<Boolean> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);

// match on protobuf request
when(commandManager.<Boolean>submitNewCommand(eq(HashExists), eq(args), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Boolean> response = service.hexists(key, field);
Boolean payload = response.get();

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

@SneakyThrows
@Test
public void sadd_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Exists;
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
import static redis_request.RedisRequestOuterClass.RequestType.HashDel;
import static redis_request.RedisRequestOuterClass.RequestType.HashExists;
import static redis_request.RedisRequestOuterClass.RequestType.HashGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashMGet;
import static redis_request.RedisRequestOuterClass.RequestType.HashSet;
Expand Down Expand Up @@ -122,6 +123,10 @@ 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.hexists("key", "field");
results.add(
Pair.of(HashExists, ArgsArray.newBuilder().addArgs("key").addArgs("field").build()));

transaction.sadd("key", new String[] {"value"});
results.add(Pair.of(SAdd, ArgsArray.newBuilder().addArgs("key").addArgs("value").build()));

Expand Down
16 changes: 16 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -444,6 +445,21 @@ public void hmget_multiple_existing_fields_non_existing_field_non_existing_key(
client.hmget("non_existing_key", new String[] {field1, field2}).get());
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void hexists_existing_field_non_existing_field_non_existing_key(BaseClient client) {
String key = UUID.randomUUID().toString();
String field1 = UUID.randomUUID().toString();
String field2 = UUID.randomUUID().toString();
Map<String, String> fieldValueMap = Map.of(field1, "value1", field2, "value1");

assertEquals(2, client.hset(key, fieldValueMap).get());
assertTrue(client.hexists(key, field1).get());
assertFalse(client.hexists(key, "non_existing_field").get());
assertFalse(client.hexists("non_existing_key", field2).get());
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static BaseTransaction<?> transactionTest(BaseTransaction<?> baseTransact

baseTransaction.hset(key4, Map.of(field1, value1, field2, value2));
baseTransaction.hget(key4, field1);
baseTransaction.hexists(key4, field2);
baseTransaction.hmget(key4, new String[] {field1, "non_existing_field", field2});
baseTransaction.hdel(key4, new String[] {field1});

Expand Down Expand Up @@ -73,6 +74,7 @@ public static Object[] transactionTestResult() {
0.5,
2L,
value1,
true,
new String[] {value1, null, value2},
1L,
2L,
Expand Down

0 comments on commit e71c8e6

Please sign in to comment.