Skip to content

Commit

Permalink
Java: Add SORT and SORT_RO commands (valkey-io#1611)
Browse files Browse the repository at this point in the history
* Java: Add `SORT` and `SORT_RO` commands (#363)

Co-authored-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
GumpacG and Yury-Fridlyand authored Jun 22, 2024
1 parent 7e32e1b commit 2438d8b
Show file tree
Hide file tree
Showing 25 changed files with 1,707 additions and 3 deletions.
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 @@ -233,6 +233,7 @@ enum RequestType {
GetEx = 192;
Dump = 193;
Restore = 194;
SortReadOnly = 195;
}

message Command {
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 @@ -203,6 +203,7 @@ pub enum RequestType {
GetEx = 192,
Dump = 193,
Restore = 194,
SortReadOnly = 195,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -409,6 +410,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::GetEx => RequestType::GetEx,
ProtobufRequestType::Dump => RequestType::Dump,
ProtobufRequestType::Restore => RequestType::Restore,
ProtobufRequestType::SortReadOnly => RequestType::SortReadOnly,
}
}
}
Expand Down Expand Up @@ -613,6 +615,7 @@ impl RequestType {
RequestType::GetEx => Some(cmd("GETEX")),
RequestType::Dump => Some(cmd("DUMP")),
RequestType::Restore => Some(cmd("RESTORE")),
RequestType::SortReadOnly => Some(cmd("SORT_RO")),
}
}
}
26 changes: 26 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package glide.api;

import static glide.api.models.GlideString.gs;
import static glide.api.models.commands.SortBaseOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.SortOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.bitmap.BitFieldOptions.BitFieldReadOnlySubCommands;
import static glide.api.models.commands.bitmap.BitFieldOptions.BitFieldSubCommands;
import static glide.api.models.commands.bitmap.BitFieldOptions.createBitFieldArgs;
Expand Down Expand Up @@ -118,6 +120,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Set;
import static redis_request.RedisRequestOuterClass.RequestType.SetBit;
import static redis_request.RedisRequestOuterClass.RequestType.SetRange;
import static redis_request.RedisRequestOuterClass.RequestType.Sort;
import static redis_request.RedisRequestOuterClass.RequestType.SortReadOnly;
import static redis_request.RedisRequestOuterClass.RequestType.Strlen;
import static redis_request.RedisRequestOuterClass.RequestType.TTL;
import static redis_request.RedisRequestOuterClass.RequestType.Touch;
Expand Down Expand Up @@ -2080,4 +2084,26 @@ public CompletableFuture<String> restore(
GlideString[] arguments = restoreOptions.toArgs(key, ttl, value);
return commandManager.submitNewCommand(Restore, arguments, this::handleStringResponse);
}

@Override
public CompletableFuture<String[]> sort(@NonNull String key) {
return commandManager.submitNewCommand(
Sort,
new String[] {key},
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<String[]> sortReadOnly(@NonNull String key) {
return commandManager.submitNewCommand(
SortReadOnly,
new String[] {key},
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<Long> sortStore(@NonNull String key, @NonNull String destination) {
return commandManager.submitNewCommand(
Sort, new String[] {key, STORE_COMMAND_STRING, destination}, this::handleLongResponse);
}
}
31 changes: 31 additions & 0 deletions java/client/src/main/java/glide/api/RedisClient.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api;

import static glide.api.models.commands.SortBaseOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.SortOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.function.FunctionListOptions.LIBRARY_NAME_REDIS_API;
import static glide.api.models.commands.function.FunctionListOptions.WITH_CODE_REDIS_API;
import static glide.api.models.commands.function.FunctionLoadOptions.REPLACE;
Expand Down Expand Up @@ -32,6 +34,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.RandomKey;
import static redis_request.RedisRequestOuterClass.RequestType.Select;
import static redis_request.RedisRequestOuterClass.RequestType.Sort;
import static redis_request.RedisRequestOuterClass.RequestType.SortReadOnly;
import static redis_request.RedisRequestOuterClass.RequestType.Time;
import static redis_request.RedisRequestOuterClass.RequestType.UnWatch;

Expand All @@ -43,6 +47,7 @@
import glide.api.models.Transaction;
import glide.api.models.commands.FlushMode;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.SortOptions;
import glide.api.models.configuration.RedisClientConfiguration;
import glide.managers.CommandManager;
import glide.managers.ConnectionManager;
Expand Down Expand Up @@ -324,4 +329,30 @@ public CompletableFuture<String> randomKey() {
return commandManager.submitNewCommand(
RandomKey, new String[0], this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<String[]> sort(@NonNull String key, @NonNull SortOptions sortOptions) {
String[] arguments = ArrayUtils.addFirst(sortOptions.toArgs(), key);
return commandManager.submitNewCommand(
Sort, arguments, response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<String[]> sortReadOnly(
@NonNull String key, @NonNull SortOptions sortOptions) {
String[] arguments = ArrayUtils.addFirst(sortOptions.toArgs(), key);
return commandManager.submitNewCommand(
SortReadOnly,
arguments,
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<Long> sortStore(
@NonNull String key, @NonNull String destination, @NonNull SortOptions sortOptions) {
String[] storeArguments = new String[] {STORE_COMMAND_STRING, destination};
String[] arguments =
concatenateArrays(new String[] {key}, sortOptions.toArgs(), storeArguments);
return commandManager.submitNewCommand(Sort, arguments, this::handleLongResponse);
}
}
34 changes: 34 additions & 0 deletions java/client/src/main/java/glide/api/RedisClusterClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package glide.api;

import static glide.api.commands.ServerManagementCommands.VERSION_REDIS_API;
import static glide.api.models.commands.SortBaseOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.function.FunctionListOptions.LIBRARY_NAME_REDIS_API;
import static glide.api.models.commands.function.FunctionListOptions.WITH_CODE_REDIS_API;
import static glide.api.models.commands.function.FunctionLoadOptions.REPLACE;
Expand Down Expand Up @@ -33,6 +34,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Lolwut;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
import static redis_request.RedisRequestOuterClass.RequestType.RandomKey;
import static redis_request.RedisRequestOuterClass.RequestType.Sort;
import static redis_request.RedisRequestOuterClass.RequestType.SortReadOnly;
import static redis_request.RedisRequestOuterClass.RequestType.Time;
import static redis_request.RedisRequestOuterClass.RequestType.UnWatch;

Expand All @@ -45,6 +48,7 @@
import glide.api.models.ClusterValue;
import glide.api.models.commands.FlushMode;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.SortClusterOptions;
import glide.api.models.configuration.RedisClusterClientConfiguration;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import glide.api.models.configuration.RequestRoutingConfiguration.SingleNodeRoute;
Expand All @@ -56,6 +60,7 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import lombok.NonNull;
import org.apache.commons.lang3.ArrayUtils;
import response.ResponseOuterClass.Response;

/**
Expand Down Expand Up @@ -701,4 +706,33 @@ public CompletableFuture<String> randomKey() {
return commandManager.submitNewCommand(
RandomKey, new String[0], this::handleStringOrNullResponse);
}

@Override
public CompletableFuture<String[]> sort(
@NonNull String key, @NonNull SortClusterOptions sortClusterOptions) {
String[] arguments = ArrayUtils.addFirst(sortClusterOptions.toArgs(), key);
return commandManager.submitNewCommand(
Sort, arguments, response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<String[]> sortReadOnly(
@NonNull String key, @NonNull SortClusterOptions sortClusterOptions) {
String[] arguments = ArrayUtils.addFirst(sortClusterOptions.toArgs(), key);
return commandManager.submitNewCommand(
SortReadOnly,
arguments,
response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<Long> sortStore(
@NonNull String key,
@NonNull String destination,
@NonNull SortClusterOptions sortClusterOptions) {
String[] storeArguments = new String[] {STORE_COMMAND_STRING, destination};
String[] arguments =
concatenateArrays(new String[] {key}, sortClusterOptions.toArgs(), storeArguments);
return commandManager.submitNewCommand(Sort, arguments, this::handleLongResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import glide.api.models.commands.ExpireOptions;
import glide.api.models.commands.RestoreOptions;
import glide.api.models.commands.ScriptOptions;
import glide.api.models.configuration.ReadFrom;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -652,4 +653,63 @@ CompletableFuture<Boolean> pexpireAt(
*/
CompletableFuture<String> restore(
GlideString key, long ttl, byte[] value, RestoreOptions restoreOptions);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and returns the result.
* <br>
* The <code>sort</code> command can be used to sort elements based on different criteria and
* apply transformations on sorted elements.<br>
* To store the result into a new key, see {@link #sortStore(String, String)}.<br>
*
* @param key The key of the list, set, or sorted set to be sorted.
* @return An <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2"}).get();
* assertArrayEquals(new String[] {"1", "2", "3"}, client.sort("mylist").get()); // List is sorted in ascending order
* }</pre>
*/
CompletableFuture<String[]> sort(String key);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and returns the result.
* <br>
* The <code>sortReadOnly</code> command can be used to sort elements based on different criteria
* and apply transformations on sorted elements.<br>
* This command is routed depending on the client's {@link ReadFrom} strategy.
*
* @since Redis 7.0 and above.
* @param key The key of the list, set, or sorted set to be sorted.
* @return An <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2"}).get();
* assertArrayEquals(new String[] {"1", "2", "3"}, client.sortReadOnly("mylist").get()); // List is sorted in ascending order
* }</pre>
*/
CompletableFuture<String[]> sortReadOnly(String key);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and stores the result in
* <code>destination</code>. The <code>sort</code> command can be used to sort elements based on
* different criteria, apply transformations on sorted elements, and store the result in a new
* key.<br>
* To get the sort result without storing it into a key, see {@link #sort(String)} or {@link
* #sortReadOnly(String)}.
*
* @apiNote When in cluster mode, <code>key</code> and <code>destination</code> must map to the
* same hash slot.
* @param key The key of the list, set, or sorted set to be sorted.
* @param destination The key where the sorted result will be stored.
* @return The number of elements in the sorted key stored at <code>destination</code>.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2"}).get();
* assert client.sortStore("mylist", "destination").get() == 3;
* assertArrayEquals(
* new String[] {"1", "2", "3"},
* client.lrange("destination", 0, -1).get()); // Sorted list is stored in `destination`
* }</pre>
*/
CompletableFuture<Long> sortStore(String key, String destination);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import glide.api.models.ClusterTransaction;
import glide.api.models.ClusterValue;
import glide.api.models.Transaction;
import glide.api.models.commands.SortClusterOptions;
import glide.api.models.configuration.ReadFrom;
import glide.api.models.configuration.RequestRoutingConfiguration.Route;
import glide.api.models.configuration.RequestRoutingConfiguration.SingleNodeRoute;
import java.util.concurrent.CompletableFuture;
Expand Down Expand Up @@ -148,4 +150,74 @@ public interface GenericClusterCommands {
* }</pre>
*/
CompletableFuture<String> randomKey();

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and returns the result.
* <br>
* The <code>sort</code> command can be used to sort elements based on different criteria and
* apply transformations on sorted elements.<br>
* To store the result into a new key, see {@link #sortStore(String, String, SortClusterOptions)}.
*
* @param key The key of the list, set, or sorted set to be sorted.
* @param sortClusterOptions The {@link SortClusterOptions}.
* @return An <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2", "a"}).get();
* String[] payload = client.sort("mylist", SortClusterOptions.builder().alpha()
* .orderBy(DESC).limit(new SortBaseOptions.Limit(0L, 3L)).build()).get();
* assertArrayEquals(new String[] {"a", "3", "2"}, payload); // List is sorted in descending order lexicographically starting
* }</pre>
*/
CompletableFuture<String[]> sort(String key, SortClusterOptions sortClusterOptions);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and returns the result.
* <br>
* The <code>sortReadOnly</code> command can be used to sort elements based on different criteria
* and apply transformations on sorted elements.<br>
* This command is routed depending on the client's {@link ReadFrom} strategy.
*
* @since Redis 7.0 and above.
* @param key The key of the list, set, or sorted set to be sorted.
* @param sortClusterOptions The {@link SortClusterOptions}.
* @return An <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2", "a"}).get();
* String[] payload = client.sortReadOnly("mylist", SortClusterOptions.builder().alpha()
* .orderBy(DESC).limit(new SortBaseOptions.Limit(0L, 3L)).build()).get();
* assertArrayEquals(new String[] {"a", "3", "2"}, payload); // List is sorted in descending order lexicographically starting
* }</pre>
*/
CompletableFuture<String[]> sortReadOnly(String key, SortClusterOptions sortClusterOptions);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and stores the result in
* <code>destination</code>. The <code>sort</code> command can be used to sort elements based on
* different criteria, apply transformations on sorted elements, and store the result in a new
* key.<br>
* To get the sort result without storing it into a key, see {@link #sort(String,
* SortClusterOptions)} or {@link #sortReadOnly(String, SortClusterOptions)}.
*
* @apiNote When in cluster mode, <code>key</code> and <code>destination</code> must map to the
* same hash slot.
* @param key The key of the list, set, or sorted set to be sorted.
* @param destination The key where the sorted result will be stored.
* @param sortClusterOptions The {@link SortClusterOptions}.
* @return The number of elements in the sorted key stored at <code>destination</code>.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2", "a"}).get();
* Long payload = client.sortStore("mylist", "destination",
* SortClusterOptions.builder().alpha().orderBy(DESC)
* .limit(new SortBaseOptions.Limit(0L, 3L))build()).get();
* assertEquals(3, payload);
* assertArrayEquals(
* new String[] {"a", "3", "2"},
* client.lrange("destination", 0, -1).get()); // Sorted list is stored in "destination"
* }</pre>
*/
CompletableFuture<Long> sortStore(
String key, String destination, SortClusterOptions sortClusterOptions);
}
Loading

0 comments on commit 2438d8b

Please sign in to comment.