Skip to content

Commit

Permalink
Java: add xrevrange command (#1534)
Browse files Browse the repository at this point in the history
* Java: add `xrevrange` command (#341)

* Init commit for XREVRANGE

Signed-off-by: Andrew Carbonetto <[email protected]>

* Updates to xrevrange

Signed-off-by: Andrew Carbonetto <[email protected]>

* Add XREVRANGE to rust

Signed-off-by: Andrew Carbonetto <[email protected]>

* Revert set changes

Signed-off-by: Andrew Carbonetto <[email protected]>

* Java: Add XREVRANGE command

Signed-off-by: Andrew Carbonetto <[email protected]>

* Documentation updates for xrevrange

Signed-off-by: Andrew Carbonetto <[email protected]>

* Revert small change

Signed-off-by: Andrew Carbonetto <[email protected]>

---------

Signed-off-by: Andrew Carbonetto <[email protected]>

* Change XRANGE XREVRANGE to use array of pairs

Signed-off-by: Andrew Carbonetto <[email protected]>

* Update XRANGE XREVRANGE java client to use pairings return

Signed-off-by: Andrew Carbonetto <[email protected]>

* Update XRANGE XREVRANGE it tests

Signed-off-by: Andrew Carbonetto <[email protected]>

* SPOTLESS

Signed-off-by: Andrew Carbonetto <[email protected]>

* Update return

Signed-off-by: Andrew Carbonetto <[email protected]>

* cargo fmt

Signed-off-by: Andrew Carbonetto <[email protected]>

---------

Signed-off-by: Andrew Carbonetto <[email protected]>
  • Loading branch information
acarbonetto authored Jun 11, 2024
1 parent 92089bb commit 529a3a3
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 46 deletions.
32 changes: 26 additions & 6 deletions glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,12 +683,14 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {

// TODO use enum to avoid mistakes
match command.as_slice() {
b"HGETALL" | b"CONFIG GET" | b"FT.CONFIG GET" | b"HELLO" | b"XRANGE" => {
Some(ExpectedReturnType::Map {
key_type: &None,
value_type: &None,
})
}
b"HGETALL" | b"CONFIG GET" | b"FT.CONFIG GET" | b"HELLO" => Some(ExpectedReturnType::Map {
key_type: &None,
value_type: &None,
}),
b"XRANGE" | b"XREVRANGE" => Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::ArrayOfPairs),
}),
b"XREAD" => Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::Map {
Expand Down Expand Up @@ -988,6 +990,24 @@ mod tests {
assert!(converted_4.is_err());
}

#[test]
fn convert_xrange_xrevrange() {
assert!(matches!(
expected_type_for_cmd(redis::cmd("XRANGE").arg("key").arg("start").arg("end")),
Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::ArrayOfPairs),
})
));
assert!(matches!(
expected_type_for_cmd(redis::cmd("XREVRANGE").arg("key").arg("end").arg("start")),
Some(ExpectedReturnType::Map {
key_type: &Some(ExpectedReturnType::BulkString),
value_type: &Some(ExpectedReturnType::ArrayOfPairs),
})
));
}

#[test]
fn convert_xread() {
assert!(matches!(
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ enum RequestType {
BitFieldReadOnly = 173;
Move = 174;
SInterCard = 175;
XRevRange = 176;
Copy = 178;
}

Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ pub enum RequestType {
BitFieldReadOnly = 173,
Move = 174,
SInterCard = 175,
XRevRange = 176,
Copy = 178,
}

Expand Down Expand Up @@ -365,6 +366,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::SInterCard => RequestType::SInterCard,
ProtobufRequestType::Copy => RequestType::Copy,
ProtobufRequestType::Sort => RequestType::Sort,
ProtobufRequestType::XRevRange => RequestType::XRevRange,
}
}
}
Expand Down Expand Up @@ -545,6 +547,7 @@ impl RequestType {
RequestType::SInterCard => Some(cmd("SINTERCARD")),
RequestType::Copy => Some(cmd("COPY")),
RequestType::Sort => Some(cmd("SORT")),
RequestType::XRevRange => Some(cmd("XREVRANGE")),
}
}
}
29 changes: 25 additions & 4 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XRange;
import static redis_request.RedisRequestOuterClass.RequestType.XRead;
import static redis_request.RedisRequestOuterClass.RequestType.XRevRange;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -1322,19 +1323,39 @@ public CompletableFuture<Long> xdel(@NonNull String key, @NonNull String[] ids)
}

@Override
public CompletableFuture<Map<String, String[]>> xrange(
public CompletableFuture<Map<String, String[][]>> xrange(
@NonNull String key, @NonNull StreamRange start, @NonNull StreamRange end) {
String[] arguments = ArrayUtils.addFirst(StreamRange.toArgs(start, end), key);
return commandManager.submitNewCommand(
XRange, arguments, response -> castMapOfArrays(handleMapResponse(response), String.class));
XRange, arguments, response -> castMapOf2DArray(handleMapResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, String[]>> xrange(
public CompletableFuture<Map<String, String[][]>> xrange(
@NonNull String key, @NonNull StreamRange start, @NonNull StreamRange end, long count) {
String[] arguments = ArrayUtils.addFirst(StreamRange.toArgs(start, end, count), key);
return commandManager.submitNewCommand(
XRange, arguments, response -> castMapOfArrays(handleMapResponse(response), String.class));
XRange, arguments, response -> castMapOf2DArray(handleMapResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, String[][]>> xrevrange(
@NonNull String key, @NonNull StreamRange end, @NonNull StreamRange start) {
String[] arguments = ArrayUtils.addFirst(StreamRange.toArgs(end, start), key);
return commandManager.submitNewCommand(
XRevRange,
arguments,
response -> castMapOf2DArray(handleMapResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, String[][]>> xrevrange(
@NonNull String key, @NonNull StreamRange end, @NonNull StreamRange start, long count) {
String[] arguments = ArrayUtils.addFirst(StreamRange.toArgs(end, start, count), key);
return commandManager.submitNewCommand(
XRevRange,
arguments,
response -> castMapOf2DArray(handleMapResponse(response), String.class));
}

@Override
Expand Down
100 changes: 87 additions & 13 deletions java/client/src/main/java/glide/api/commands/StreamBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,24 +184,23 @@ CompletableFuture<Map<String, Map<String, String[][]>>> xread(
* <li>Use {@link InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @return @return A <code>Map</code> of key to stream entry data, where entry data is an array of
* item pairings.
* @return A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
* @example
* <pre>{@code
* // Retrieve all stream entries
* Map<String, String[]> result = client.xrange("key", InfRangeBound.MIN, InfRangeBound.MAX).get();
* Map<String, String[][]> result = client.xrange("key", InfRangeBound.MIN, InfRangeBound.MAX).get();
* result.forEach((k, v) -> {
* System.out.println("Stream ID: " + k);
* for (int i = 0; i < v.length;) {
* System.out.println(v[i++] + ": " + v[i++]);
* for (int i = 0; i < v.length; i++) {
* System.out.println(v[i][0] + ": " + v[i][1]);
* }
* });
* // Retrieve exactly one stream entry by id
* Map<String, String[]> result = client.xrange("key", IdBound.of(streamId), IdBound.of(streamId)).get();
* Map<String, String[][]> result = client.xrange("key", IdBound.of(streamId), IdBound.of(streamId)).get();
* System.out.println("Stream ID: " + streamid + " -> " + Arrays.toString(result.get(streamid)));
* }</pre>
*/
CompletableFuture<Map<String, String[]>> xrange(String key, StreamRange start, StreamRange end);
CompletableFuture<Map<String, String[][]>> xrange(String key, StreamRange start, StreamRange end);

/**
* Returns stream entries matching a given range of IDs.
Expand All @@ -222,20 +221,95 @@ CompletableFuture<Map<String, Map<String, String[][]>>> xread(
* </ul>
*
* @param count Maximum count of stream entries to return.
* @return A <code>Map</code> of key to stream entry data, where entry data is an array of item
* pairings.
* @return A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
* @example
* <pre>{@code
* // Retrieve the first 2 stream entries
* Map<String, String[]> result = client.xrange("key", InfRangeBound.MIN, InfRangeBound.MAX, 2).get();
* Map<String, String[][]> result = client.xrange("key", InfRangeBound.MIN, InfRangeBound.MAX, 2).get();
* result.forEach((k, v) -> {
* System.out.println("Stream ID: " + k);
* for (int i = 0; i < v.length;) {
* System.out.println(v[i++] + ": " + v[i++]);
* for (int i = 0; i < v.length; i++) {
* System.out.println(v[i][0] + ": " + v[i][1]);
* }
* });
* }</pre>
*/
CompletableFuture<Map<String, String[]>> xrange(
CompletableFuture<Map<String, String[][]>> xrange(
String key, StreamRange start, StreamRange end, long count);

/**
* Returns stream entries matching a given range of IDs in reverse order.<br>
* Equivalent to {@link #xrange(String, StreamRange, StreamRange)} but returns the entries in
* reverse order.
*
* @param key The key of the stream.
* @param end Ending stream ID bound for range.
* <ul>
* <li>Use {@link IdBound#of} to specify a stream ID.
* <li>Use {@link IdBound#ofExclusive} to specify an exclusive bounded stream ID.
* <li>Use {@link InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @param start Starting stream ID bound for range.
* <ul>
* <li>Use {@link IdBound#of} to specify a stream ID.
* <li>Use {@link IdBound#ofExclusive} to specify an exclusive bounded stream ID.
* <li>Use {@link InfRangeBound#MIN} to start with the minimum available ID.
* </ul>
*
* @return A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
* @example
* <pre>{@code
* // Retrieve all stream entries
* Map<String, String[][]> result = client.xrevrange("key", InfRangeBound.MAX, InfRangeBound.MIN).get();
* result.forEach((k, v) -> {
* System.out.println("Stream ID: " + k);
* for (int i = 0; i < v.length; i++) {
* System.out.println(v[i][0] + ": " + v[i][1]);
* }
* });
* // Retrieve exactly one stream entry by id
* Map<String, String[][]> result = client.xrevrange("key", IdBound.of(streamId), IdBound.of(streamId)).get();
* System.out.println("Stream ID: " + streamid + " -> " + Arrays.toString(result.get(streamid)));
* }</pre>
*/
CompletableFuture<Map<String, String[][]>> xrevrange(
String key, StreamRange end, StreamRange start);

/**
* Returns stream entries matching a given range of IDs in reverse order.<br>
* Equivalent to {@link #xrange(String, StreamRange, StreamRange, long)} but returns the entries
* in reverse order.
*
* @param key The key of the stream.
* @param end Ending stream ID bound for range.
* <ul>
* <li>Use {@link IdBound#of} to specify a stream ID.
* <li>Use {@link IdBound#ofExclusive} to specify an exclusive bounded stream ID.
* <li>Use {@link InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @param start Starting stream ID bound for range.
* <ul>
* <li>Use {@link IdBound#of} to specify a stream ID.
* <li>Use {@link IdBound#ofExclusive} to specify an exclusive bounded stream ID.
* <li>Use {@link InfRangeBound#MIN} to start with the minimum available ID.
* </ul>
*
* @param count Maximum count of stream entries to return.
* @return A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
* @example
* <pre>{@code
* // Retrieve the first 2 stream entries
* Map<String, String[][]> result = client.xrange("key", InfRangeBound.MAX, InfRangeBound.MIN, 2).get();
* result.forEach((k, v) -> {
* System.out.println("Stream ID: " + k);
* for (int i = 0; i < v.length; i++) {
* System.out.println(v[i][0] + ": " + v[i][1]);
* }
* });
* }</pre>
*/
CompletableFuture<Map<String, String[][]>> xrevrange(
String key, StreamRange end, StreamRange start, long count);
}
70 changes: 66 additions & 4 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.XLen;
import static redis_request.RedisRequestOuterClass.RequestType.XRange;
import static redis_request.RedisRequestOuterClass.RequestType.XRead;
import static redis_request.RedisRequestOuterClass.RequestType.XRevRange;
import static redis_request.RedisRequestOuterClass.RequestType.XTrim;
import static redis_request.RedisRequestOuterClass.RequestType.ZAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZCard;
Expand Down Expand Up @@ -2742,8 +2743,7 @@ public T xdel(@NonNull String key, @NonNull String[] ids) {
* <li>Use {@link StreamRange.InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is
* an array of item pairings.
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
*/
public T xrange(@NonNull String key, @NonNull StreamRange start, @NonNull StreamRange end) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(StreamRange.toArgs(start, end), key));
Expand Down Expand Up @@ -2772,8 +2772,7 @@ public T xrange(@NonNull String key, @NonNull StreamRange start, @NonNull Stream
* </ul>
*
* @param count Maximum count of stream entries to return.
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is
* an array of item pairings.
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
*/
public T xrange(
@NonNull String key, @NonNull StreamRange start, @NonNull StreamRange end, long count) {
Expand All @@ -2783,6 +2782,69 @@ public T xrange(
return getThis();
}

/**
* Returns stream entries matching a given range of IDs in reverse order.<br>
* Equivalent to {@link #xrange(String, StreamRange, StreamRange)} but returns the entries in
* reverse order.
*
* @param key The key of the stream.
* @param end Ending stream ID bound for range.
* <ul>
* <li>Use {@link StreamRange.IdBound#of} to specify a stream ID.
* <li>Use {@link StreamRange.IdBound#ofExclusive} to specify an exclusive bounded stream
* ID.
* <li>Use {@link StreamRange.InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @param start Starting stream ID bound for range.
* <ul>
* <li>Use {@link StreamRange.IdBound#of} to specify a stream ID.
* <li>Use {@link StreamRange.IdBound#ofExclusive} to specify an exclusive bounded stream
* ID.
* <li>Use {@link StreamRange.InfRangeBound#MIN} to start with the minimum available ID.
* </ul>
*
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
*/
public T xrevrange(@NonNull String key, @NonNull StreamRange end, @NonNull StreamRange start) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(StreamRange.toArgs(end, start), key));
protobufTransaction.addCommands(buildCommand(XRevRange, commandArgs));
return getThis();
}

/**
* Returns stream entries matching a given range of IDs in reverse order.<br>
* Equivalent to {@link #xrange(String, StreamRange, StreamRange, long)} but returns the entries
* in reverse order.
*
* @param key The key of the stream.
* @param start Starting stream ID bound for range.
* <ul>
* <li>Use {@link StreamRange.IdBound#of} to specify a stream ID.
* <li>Use {@link StreamRange.IdBound#ofExclusive} to specify an exclusive bounded stream
* ID.
* <li>Use {@link StreamRange.InfRangeBound#MIN} to start with the minimum available ID.
* </ul>
*
* @param end Ending stream ID bound for range.
* <ul>
* <li>Use {@link StreamRange.IdBound#of} to specify a stream ID.
* <li>Use {@link StreamRange.IdBound#ofExclusive} to specify an exclusive bounded stream
* ID.
* <li>Use {@link StreamRange.InfRangeBound#MAX} to end with the maximum available ID.
* </ul>
*
* @param count Maximum count of stream entries to return.
* @return Command Response - A <code>Map</code> of key to stream entry data, where entry data is an array of pairings with format <code>[[field, entry], [field, entry], ...]<code>.
*/
public T xrevrange(
@NonNull String key, @NonNull StreamRange end, @NonNull StreamRange start, long count) {
ArgsArray commandArgs =
buildArgs(ArrayUtils.addFirst(StreamRange.toArgs(end, start, count), key));
protobufTransaction.addCommands(buildCommand(XRevRange, commandArgs));
return getThis();
}

/**
* Returns the remaining time to live of <code>key</code> that has a timeout, in milliseconds.
*
Expand Down
Loading

0 comments on commit 529a3a3

Please sign in to comment.