Skip to content

Commit

Permalink
Java: Add SORT and SORT_RO commands (#363)
Browse files Browse the repository at this point in the history
* Added test

* Added child classes

* Needs cleaning up and store needs separate functions

* Simplified sort apis

* Separated sortOptions and sortStandAloneOptions

* Added tests

* Added IT and UT

* Revert to Builder from SuperBuilder

* Added javadocs

* Added transaction tests

* Addressed comments

* Spotless

* Added SortBaseOptions

* Addressed docs comments

* Fixed comment

* Refactor sortWithStore to sortStore

* Addressed comments

* Update java/client/src/main/java/glide/api/commands/GenericBaseCommands.java

Co-authored-by: Yury-Fridlyand <[email protected]>

---------

Co-authored-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
GumpacG and Yury-Fridlyand authored Jun 19, 2024
1 parent 4c6c081 commit d227806
Show file tree
Hide file tree
Showing 19 changed files with 1,794 additions and 2 deletions.
2 changes: 2 additions & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ enum RequestType {
BitFieldReadOnly = 173;
Move = 174;
SInterCard = 175;
Sort = 176;
SortReadOnly = 177;
}

message Command {
Expand Down
6 changes: 6 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ pub enum RequestType {
BitFieldReadOnly = 173,
Move = 174,
SInterCard = 175,
Sort = 176,
SortReadOnly = 177,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -355,6 +357,8 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::BitFieldReadOnly => RequestType::BitFieldReadOnly,
ProtobufRequestType::Move => RequestType::Move,
ProtobufRequestType::SInterCard => RequestType::SInterCard,
ProtobufRequestType::Sort => RequestType::Sort,
ProtobufRequestType::SortReadOnly => RequestType::SortReadOnly,
}
}
}
Expand Down Expand Up @@ -530,6 +534,8 @@ impl RequestType {
RequestType::BitFieldReadOnly => Some(cmd("BITFIELD_RO")),
RequestType::Move => Some(cmd("MOVE")),
RequestType::SInterCard => Some(cmd("SINTERCARD")),
RequestType::Sort => Some(cmd("SORT")),
RequestType::SortReadOnly => Some(cmd("SORT_RO")),
}
}
}
53 changes: 53 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api;

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 @@ -105,6 +106,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 @@ -164,6 +167,7 @@
import glide.api.models.commands.ScoreFilter;
import glide.api.models.commands.ScriptOptions;
import glide.api.models.commands.SetOptions;
import glide.api.models.commands.SortBaseOptions;
import glide.api.models.commands.WeightAggregateOptions.Aggregate;
import glide.api.models.commands.WeightAggregateOptions.KeysOrWeightedKeys;
import glide.api.models.commands.ZAddOptions;
Expand Down Expand Up @@ -1677,4 +1681,53 @@ public CompletableFuture<Long> sintercard(@NonNull String[] keys, long limit) {
new String[] {SET_LIMIT_REDIS_API, Long.toString(limit)});
return commandManager.submitNewCommand(SInterCard, arguments, this::handleLongResponse);
}

@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[]> sort(
@NonNull String key, @NonNull SortBaseOptions sortBaseOptions) {
String[] arguments = ArrayUtils.addFirst(sortBaseOptions.toArgs(), key);
return commandManager.submitNewCommand(
Sort, arguments, 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<String[]> sortReadOnly(
@NonNull String key, @NonNull SortBaseOptions sortBaseOptions) {
String[] arguments = ArrayUtils.addFirst(sortBaseOptions.toArgs(), key);
return commandManager.submitNewCommand(
SortReadOnly,
arguments,
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);
}

@Override
public CompletableFuture<Long> sortStore(
@NonNull String key, @NonNull String destination, @NonNull SortBaseOptions sortBaseOptions) {
String[] storeArguments = new String[] {STORE_COMMAND_STRING, destination};
String[] arguments =
ArrayUtils.addFirst(concatenateArrays(storeArguments, sortBaseOptions.toArgs()), key);
return commandManager.submitNewCommand(Sort, arguments, this::handleLongResponse);
}
}
34 changes: 34 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,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api;

import static glide.api.models.commands.SortOptions.STORE_COMMAND_STRING;
import static glide.api.models.commands.function.FunctionLoadOptions.REPLACE;
import static glide.utils.ArrayTransformUtils.castArray;
import static glide.utils.ArrayTransformUtils.concatenateArrays;
Expand All @@ -21,6 +22,8 @@
import static redis_request.RedisRequestOuterClass.RequestType.Move;
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
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 glide.api.commands.ConnectionManagementCommands;
Expand All @@ -30,13 +33,15 @@
import glide.api.models.Transaction;
import glide.api.models.commands.FlushMode;
import glide.api.models.commands.InfoOptions;
import glide.api.models.commands.SortStandaloneOptions;
import glide.api.models.configuration.RedisClientConfiguration;
import glide.managers.CommandManager;
import glide.managers.ConnectionManager;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import lombok.NonNull;
import org.apache.commons.lang3.ArrayUtils;

/**
* Async (non-blocking) client for Redis in Standalone mode. Use {@link #CreateClient} to request a
Expand Down Expand Up @@ -203,4 +208,33 @@ public CompletableFuture<Boolean> move(@NonNull String key, long dbIndex) {
return commandManager.submitNewCommand(
Move, new String[] {key, Long.toString(dbIndex)}, this::handleBooleanResponse);
}

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

@Override
public CompletableFuture<String[]> sortReadOnly(
@NonNull String key, @NonNull SortStandaloneOptions sortStandaloneOptions) {
String[] arguments = ArrayUtils.addFirst(sortStandaloneOptions.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 SortStandaloneOptions sortStandaloneOptions) {
String[] storeArguments = new String[] {STORE_COMMAND_STRING, destination};
String[] arguments =
ArrayUtils.addFirst(concatenateArrays(storeArguments, sortStandaloneOptions.toArgs()), key);
return commandManager.submitNewCommand(Sort, arguments, this::handleLongResponse);
}
}
148 changes: 148 additions & 0 deletions java/client/src/main/java/glide/api/commands/GenericBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import glide.api.models.Script;
import glide.api.models.commands.ExpireOptions;
import glide.api.models.commands.ScriptOptions;
import glide.api.models.commands.SortBaseOptions;
import glide.api.models.configuration.ReadFrom;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -542,4 +544,150 @@ CompletableFuture<Boolean> pexpireAt(
* }</pre>
*/
CompletableFuture<Long> touch(String[] keys);

/**
* Sorts the elements in the list, set, or sorted set at <code>key</code> and returns the result.
* 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)}.
*
* @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.
* 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, SortBaseOptions)}.
*
* @param key The key of the list, set, or sorted set to be sorted.
* @param sortBaseOptions The {@link SortBaseOptions}.
* @return A <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2", "a"}).get();
* String[] payload = client.sort(
* "mylist",
* SortBaseOptions.builder()
* .alpha(true)
* .orderBy(DESC)
* .limit(new SortOptions.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, SortBaseOptions sortBaseOptions);

/**
* 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 A <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 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 sortBaseOptions The {@link SortBaseOptions}.
* @return A <code>Array</code> of sorted elements.
* @example
* <pre>{@code
* client.lpush("mylist", new String[] {"3", "1", "2", "a"}).get();
* String[] payload = client.sortReadOnly(
* "mylist",
* SortBaseOptions.builder()
* .alpha(true)
* .orderBy(DESC)
* .limit(new SortOptions.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, SortBaseOptions sortBaseOptions);

/**
* 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)} and {@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);

/**
* 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,
* SortBaseOptions)} and {@link #sortReadOnly(String, SortBaseOptions)}.
*
* @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 sortBaseOptions The {@link SortBaseOptions}.
* @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",
* SortBaseOptions.builder()
* .alpha(true)
* .orderBy(DESC)
* .limit(new SortOptions.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, SortBaseOptions sortBaseOptions);
}
Loading

0 comments on commit d227806

Please sign in to comment.