Skip to content

Commit

Permalink
Java: Add LRange and LTrim commands. (List Commands) (valkey-io#1041)
Browse files Browse the repository at this point in the history
  • Loading branch information
SanHalacogluImproving authored Mar 4, 2024
1 parent cfc0780 commit 5f687a3
Showing 7 changed files with 280 additions and 57 deletions.
18 changes: 18 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.LPop;
import static redis_request.RedisRequestOuterClass.RequestType.LPush;
import static redis_request.RedisRequestOuterClass.RequestType.LRange;
import static redis_request.RedisRequestOuterClass.RequestType.LTrim;
import static redis_request.RedisRequestOuterClass.RequestType.MGet;
import static redis_request.RedisRequestOuterClass.RequestType.MSet;
import static redis_request.RedisRequestOuterClass.RequestType.PExpire;
@@ -359,6 +361,22 @@ public CompletableFuture<String[]> lpopCount(@NonNull String key, long count) {
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<String[]> lrange(@NonNull String key, long start, long end) {
return commandManager.submitNewCommand(
LRange,
new String[] {key, Long.toString(start), Long.toString(end)},
response -> castArray(handleArrayOrNullResponse(response), String.class));
}

@Override
public CompletableFuture<String> ltrim(@NonNull String key, long start, long end) {
return commandManager.submitNewCommand(
LTrim,
new String[] {key, Long.toString(start), Long.toString(end)},
this::handleStringResponse);
}

@Override
public CompletableFuture<Long> rpush(@NonNull String key, @NonNull String[] elements) {
String[] arguments = ArrayUtils.addFirst(elements, key);
55 changes: 55 additions & 0 deletions java/client/src/main/java/glide/api/commands/ListBaseCommands.java
Original file line number Diff line number Diff line change
@@ -67,6 +67,61 @@ public interface ListBaseCommands {
*/
CompletableFuture<String[]> lpopCount(String key, long count);

/**
* Returns the specified elements of the list stored at <code>key</code>.<br>
* The offsets <code>start</code> and <code>end</code> are zero-based indexes, with 0 being the
* first element of the list, 1 being the next element and so on. These offsets can also be
* negative numbers indicating offsets starting at the end of the list, with -1 being the last
* element of the list, -2 being the penultimate, and so on.
*
* @see <a href="https://redis.io/commands/lrange/">redis.io</a> for details.
* @param key The key of the list.
* @param start The starting point of the range.
* @param end The end of the range.
* @return Array of elements in the specified range.<br>
* If <code>start</code> exceeds the end of the list, or if <code>start</code> is greater than
* <code>end</code>, an empty array will be returned.<br>
* If <code>end</code> exceeds the actual end of the list, the range will stop at the actual
* end of the list.<br>
* If <code>key</code> does not exist an empty array will be returned.<br>
* @example
* <pre>
* String[] payload = lient.lrange("my_list", 0, 2).get()
* assert payload.equals(new String[] {"value1", "value2", "value3"})
* String[] payload = client.lrange("my_list", -2, -1).get()
* assert payload.equals(new String[] {"value2", "value3"})
* String[] payload = client.lrange("non_exiting_key", 0, 2).get()
* assert payload.equals(new String[] {})
* </pre>
*/
CompletableFuture<String[]> lrange(String key, long start, long end);

/**
* Trims an existing list so that it will contain only the specified range of elements specified.
* <br>
* The offsets <code>start</code> and <code>end</code> are zero-based indexes, with 0 being the
* first element of the list, 1 being the next element and so on.<br>
* These offsets can also be negative numbers indicating offsets starting at the end of the list,
* with -1 being the last element of the list, -2 being the penultimate, and so on.
*
* @see <a href="https://redis.io/commands/ltrim/">redis.io</a> for details.
* @param key The key of the list.
* @param start The starting point of the range.
* @param end The end of the range.
* @return Always <code>OK</code>.<br>
* If <code>start</code> exceeds the end of the list, or if <code>start</code> is greater than
* <code>end</code>, the result will be an empty list (which causes key to be removed).<br>
* If <code>end</code> exceeds the actual end of the list, it will be treated like the last
* element of the list.<br>
* If <code>key</code> does not exist, OK will be returned without changes to the database.
* @example
* <pre>
* String payload = client.ltrim("my_list", 0, 1).get()
* assert payload.equals("OK")
* </pre>
*/
CompletableFuture<String> ltrim(String key, long start, long end);

/**
* Inserts all the specified values at the tail of the list stored at <code>key</code>.<br>
* <code>elements</code> are inserted one after the other to the tail of the list, from the
155 changes: 104 additions & 51 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LPop;
import static redis_request.RedisRequestOuterClass.RequestType.LPush;
import static redis_request.RedisRequestOuterClass.RequestType.LRange;
import static redis_request.RedisRequestOuterClass.RequestType.LTrim;
import static redis_request.RedisRequestOuterClass.RequestType.MGet;
import static redis_request.RedisRequestOuterClass.RequestType.MSet;
import static redis_request.RedisRequestOuterClass.RequestType.PExpire;
@@ -378,57 +380,6 @@ public T hdel(@NonNull String key, @NonNull String[] fields) {
return getThis();
}

/**
* Inserts all the specified values at the tail of the list stored at <code>key</code>.<br>
* <code>elements</code> are inserted one after the other to the tail of the list, from the
* leftmost element to the rightmost element. If <code>key</code> does not exist, it is created as
* an empty list before performing the push operations.
*
* @see <a href="https://redis.io/commands/rpush/">redis.io</a> for details.
* @param key The key of the list.
* @param elements The elements to insert at the tail of the list stored at <code>key</code>.
* @return Command Response - The length of the list after the push operations.
*/
public T rpush(@NonNull String key, @NonNull String[] elements) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(elements, key));

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

/**
* Removes and returns the last elements of the list stored at <code>key</code>.<br>
* The command pops a single element from the end of the list.
*
* @see <a href="https://redis.io/commands/rpop/">redis.io</a> for details.
* @param key The key of the list.
* @return Command Response - The value of the last element.<br>
* If <code>key</code> does not exist, null will be returned.<br>
*/
public T rpop(@NonNull String key) {
ArgsArray commandArgs = buildArgs(key);

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

/**
* Removes and returns up to <code>count</code> elements from the list stored at <code>key</code>,
* depending on the list's length.
*
* @see <a href="https://redis.io/commands/rpop/">redis.io</a> for details.
* @param count The count of the elements to pop from the list.
* @returns Command Response - An array of popped elements will be returned depending on the
* list's length.<br>
* If <code>key</code> does not exist, null will be returned.<br>
*/
public T rpopCount(@NonNull String key, long count) {
ArgsArray commandArgs = buildArgs(key, Long.toString(count));

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

/**
* Returns the values associated with the specified fields in the hash stored at <code>key</code>.
*
@@ -578,6 +529,108 @@ public T lpopCount(@NonNull String key, long count) {
return getThis();
}

/**
* Returns the specified elements of the list stored at <code>key</code>.<br>
* The offsets <code>start</code> and <code>end</code> are zero-based indexes, with 0 being the
* first element of the list, 1 being the next element and so on. These offsets can also be
* negative numbers indicating offsets starting at the end of the list, with -1 being the last
* element of the list, -2 being the penultimate, and so on.
*
* @see <a href="https://redis.io/commands/lrange/">redis.io</a> for details.
* @param key The key of the list.
* @param start The starting point of the range.
* @param end The end of the range.
* @return Command Response - Array of elements in the specified range.<br>
* If <code>start</code> exceeds the end of the list, or if <code>start</code> is greater than
* <code>end</code>, an empty array will be returned.<br>
* If <code>end</code> exceeds the actual end of the list, the range will stop at the actual
* end of the list.<br>
* If <code>key</code> does not exist an empty array will be returned.<br>
*/
public T lrange(@NonNull String key, long start, long end) {
ArgsArray commandArgs = buildArgs(key, Long.toString(start), Long.toString(end));

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

/**
* Trims an existing list so that it will contain only the specified range of elements specified.
* <br>
* The offsets <code>start</code> and <code>end</code> are zero-based indexes, with 0 being the
* first element of the list, 1 being the next element and so on.<br>
* These offsets can also be negative numbers indicating offsets starting at the end of the list,
* with -1 being the last element of the list, -2 being the penultimate, and so on.
*
* @see <a href="https://redis.io/commands/ltrim/">redis.io</a> for details.
* @param key The key of the list.
* @param start The starting point of the range.
* @param end The end of the range.
* @return Command Response - Always <code>OK</code>. <br>
* If <code>start</code> exceeds the end of the list, or if <code>start</code> is greater than
* <code>end</code>, the result will be an empty list (which causes key to be removed).<br>
* If <code>end</code> exceeds the actual end of the list, it will be treated like the last
* element of the list.<br>
* If <code>key</code> does not exist, OK will be returned without changes to the database.
*/
public T ltrim(@NonNull String key, long start, long end) {
ArgsArray commandArgs = buildArgs(key, Long.toString(start), Long.toString(end));

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

/**
* Inserts all the specified values at the tail of the list stored at <code>key</code>.<br>
* <code>elements</code> are inserted one after the other to the tail of the list, from the
* leftmost element to the rightmost element. If <code>key</code> does not exist, it is created as
* an empty list before performing the push operations.
*
* @see <a href="https://redis.io/commands/rpush/">redis.io</a> for details.
* @param key The key of the list.
* @param elements The elements to insert at the tail of the list stored at <code>key</code>.
* @return Command Response - The length of the list after the push operations.
*/
public T rpush(@NonNull String key, @NonNull String[] elements) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(elements, key));

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

/**
* Removes and returns the last elements of the list stored at <code>key</code>.<br>
* The command pops a single element from the end of the list.
*
* @see <a href="https://redis.io/commands/rpop/">redis.io</a> for details.
* @param key The key of the list.
* @return Command Response - The value of the last element.<br>
* If <code>key</code> does not exist, null will be returned.<br>
*/
public T rpop(@NonNull String key) {
ArgsArray commandArgs = buildArgs(key);

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

/**
* Removes and returns up to <code>count</code> elements from the list stored at <code>key</code>,
* depending on the list's length.
*
* @see <a href="https://redis.io/commands/rpop/">redis.io</a> for details.
* @param count The count of the elements to pop from the list.
* @return Command Response - An array of popped elements will be returned depending on the list's
* length.<br>
* If <code>key</code> does not exist, null will be returned.<br>
*/
public T rpopCount(@NonNull String key, long count) {
ArgsArray commandArgs = buildArgs(key, Long.toString(count));

protobufTransaction.addCommands(buildCommand(RPop, 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.
53 changes: 53 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LPop;
import static redis_request.RedisRequestOuterClass.RequestType.LPush;
import static redis_request.RedisRequestOuterClass.RequestType.LRange;
import static redis_request.RedisRequestOuterClass.RequestType.LTrim;
import static redis_request.RedisRequestOuterClass.RequestType.MGet;
import static redis_request.RedisRequestOuterClass.RequestType.MSet;
import static redis_request.RedisRequestOuterClass.RequestType.PExpire;
@@ -1040,6 +1042,57 @@ public void lpopCount_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void lrange_returns_success() {
// setup
String key = "testKey";
long start = 2L;
long end = 4L;
String[] args = new String[] {key, Long.toString(start), Long.toString(end)};
String[] value = new String[] {"value1", "value2"};

CompletableFuture<String[]> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(value);

// match on protobuf request
when(commandManager.<String[]>submitNewCommand(eq(LRange), eq(args), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String[]> response = service.lrange(key, start, end);
String[] payload = response.get();

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

@SneakyThrows
@Test
public void ltrim_returns_success() {
// setup
String key = "testKey";
long start = 2L;
long end = 2L;
String[] args = new String[] {key, Long.toString(end), Long.toString(start)};

CompletableFuture<String> testResponse = mock(CompletableFuture.class);
when(testResponse.get()).thenReturn(OK);

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

// exercise
CompletableFuture<String> response = service.ltrim(key, start, end);
String payload = response.get();

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

@SneakyThrows
@Test
public void rpush_returns_success() {
10 changes: 10 additions & 0 deletions java/client/src/test/java/glide/api/models/TransactionTests.java
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LPop;
import static redis_request.RedisRequestOuterClass.RequestType.LPush;
import static redis_request.RedisRequestOuterClass.RequestType.LRange;
import static redis_request.RedisRequestOuterClass.RequestType.LTrim;
import static redis_request.RedisRequestOuterClass.RequestType.MGet;
import static redis_request.RedisRequestOuterClass.RequestType.MSet;
import static redis_request.RedisRequestOuterClass.RequestType.PExpire;
@@ -165,6 +167,14 @@ public void transaction_builds_protobuf_request(BaseTransaction<?> transaction)
transaction.lpopCount("key", 2);
results.add(Pair.of(LPop, ArgsArray.newBuilder().addArgs("key").addArgs("2").build()));

transaction.lrange("key", 1, 2);
results.add(
Pair.of(LRange, ArgsArray.newBuilder().addArgs("key").addArgs("1").addArgs("2").build()));

transaction.ltrim("key", 1, 2);
results.add(
Pair.of(LTrim, ArgsArray.newBuilder().addArgs("key").addArgs("1").addArgs("2").build()));

transaction.rpush("key", new String[] {"element"});
results.add(Pair.of(RPush, ArgsArray.newBuilder().addArgs("key").addArgs("element").build()));

Loading

0 comments on commit 5f687a3

Please sign in to comment.