Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python: Adds Sort command #1439

Merged
merged 24 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7100a02
Adds sort command to python
GilboaAWS May 22, 2024
2007242
addressed `Run isort . --profile black --check --diff`
GilboaAWS May 26, 2024
6e767a5
Apply suggestions from code review
GilboaAWS May 26, 2024
ef8e5b1
Addressed comments from PR
GilboaAWS May 27, 2024
8cb47fd
Changed SortOrder to OrderBy
GilboaAWS May 27, 2024
e295566
Updated valkey path in links.
GilboaAWS May 27, 2024
0e65551
Removed `pass` from Transaction Class.
GilboaAWS May 27, 2024
19ec536
Fix `Limit` doc
GilboaAWS May 27, 2024
d1a5068
Updated sort args doc.
GilboaAWS May 29, 2024
d8bd446
Fix return from function
GilboaAWS May 29, 2024
216123a
Addressed comments. Renamed store to destination fixed standalone cli…
GilboaAWS May 29, 2024
4c86e52
Changed doc according to comments.
GilboaAWS May 29, 2024
7e44dd9
Apply suggestions from code review
GilboaAWS May 29, 2024
91f9157
Remove duplicated line
GilboaAWS May 29, 2024
158ea71
Remove unused import of Tuple
GilboaAWS May 29, 2024
0807065
Apply suggestions from code review
GilboaAWS May 29, 2024
49a2bd4
fixed tests.
GilboaAWS May 29, 2024
c3649d5
Fixed doc.
GilboaAWS Jun 2, 2024
911ebdf
Added tests that return None in a list.
GilboaAWS Jun 2, 2024
215140d
Added entry in the CHANGELOG for SORT in python.
GilboaAWS Jun 2, 2024
6f214f2
Update python/python/glide/async_commands/command_args.py
GilboaAWS Jun 3, 2024
952e0e0
Added entry in the CHANGELOG for SORT in python.
GilboaAWS Jun 2, 2024
dd4cac3
Update CHANGELOG.md
GilboaAWS Jun 4, 2024
f89eadc
Updated Cluster's Transaction's Sort response to non None value.
GilboaAWS Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
* Python: Added GETDEL command ([#1514](https://github.com/aws/glide-for-redis/pull/1514))
* Python: Added ZINTER, ZUNION commands ([#1478](https://github.com/aws/glide-for-redis/pull/1478))
* Python: Added SINTERCARD command ([#1511](https://github.com/aws/glide-for-redis/pull/1511))
* Python: Added SORT command ([#1439](https://github.com/aws/glide-for-redis/pull/1439))

## 0.4.1 (2024-02-06)

#### Fixes
* Node: Fix set command bug with expiry option ([#1508](https://github.com/aws/glide-for-redis/pull/1508))
* Python: Added SORT command ([#1439](https://github.com/aws/glide-for-redis/pull/1439))
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved

## 0.4.0 (2024-05-26)

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 @@ -195,6 +195,7 @@ enum RequestType {
PExpireTime = 157;
BLMPop = 158;
XLen = 159;
Sort = 160;
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
LSet = 165;
XDel = 166;
XRange = 167;
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 @@ -165,6 +165,7 @@ pub enum RequestType {
PExpireTime = 157,
BLMPop = 158,
XLen = 159,
Sort = 160,
LSet = 165,
XDel = 166,
XRange = 167,
Expand Down Expand Up @@ -349,6 +350,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::GetDel => RequestType::GetDel,
ProtobufRequestType::SRandMember => RequestType::SRandMember,
ProtobufRequestType::SInterCard => RequestType::SInterCard,
ProtobufRequestType::Sort => RequestType::Sort,
}
}
}
Expand Down Expand Up @@ -521,6 +523,7 @@ impl RequestType {
RequestType::GetDel => Some(cmd("GETDEL")),
RequestType::SRandMember => Some(cmd("SRANDMEMBER")),
RequestType::SInterCard => Some(cmd("SINTERCARD")),
RequestType::Sort => Some(cmd("SORT")),
}
}
}
3 changes: 2 additions & 1 deletion python/python/glide/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0

from glide.async_commands.command_args import Limit, OrderBy
from glide.async_commands.core import (
ConditionalChange,
ExpireOptions,
Expand All @@ -20,7 +21,6 @@
AggregationType,
InfBound,
LexBoundary,
Limit,
RangeByIndex,
RangeByLex,
RangeByScore,
Expand Down Expand Up @@ -102,6 +102,7 @@
"RangeByLex",
"RangeByScore",
"ScoreFilter",
"OrderBy",
"StreamAddOptions",
"StreamTrimOptions",
"TrimByMaxLen",
Expand Down
87 changes: 86 additions & 1 deletion python/python/glide/async_commands/cluster_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from typing import Dict, List, Mapping, Optional, cast

from glide.async_commands.core import CoreCommands, InfoSection
from glide.async_commands.command_args import Limit, OrderBy
from glide.async_commands.core import CoreCommands, InfoSection, _build_sort_args
from glide.async_commands.transaction import BaseTransaction, ClusterTransaction
from glide.constants import TOK, TClusterResponse, TResult, TSingleNodeRoute
from glide.protobuf.redis_request_pb2 import RequestType
Expand Down Expand Up @@ -367,3 +368,87 @@ async def lastsave(self, route: Optional[Route] = None) -> TClusterResponse[int]
TClusterResponse[int],
await self._execute_command(RequestType.LastSave, [], route),
)

async def sort(
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
self,
key: str,
limit: Optional[Limit] = None,
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
order: Optional[OrderBy] = None,
alpha: Optional[bool] = None,
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
) -> List[str]:
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
"""
Sorts the elements in the list, set, or sorted set at `key` and returns the result.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
To store the result into a new key, see `sort_store`.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved

By default, sorting is numeric, and elements are compared by their value interpreted as double precision floating point numbers.

See https://valkey.io/commands/sort for more details.

Args:
key (str): The key of the list, set, or sorted set to be sorted.
limit (Optional[Limit]): Limiting the range of the query by setting offset and result count. See `Limit` class for more information.
order (Optional[OrderBy]): Specifies the order to sort the elements.
Can be `OrderBy.ASC` (ascending) or `OrderBy.DESC` (descending).
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
alpha (Optional[bool]): When `True`, sorts elements lexicographically. When `False` (default), sorts elements numerically.
Use this when the list, set, or sorted set contains string values that cannot be converted into double precision floating point numbers.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved

Returns:
List[str]: A list of sorted elements.

Examples:
>>> await client.lpush("mylist", '3', '1', '2')
>>> await client.sort("mylist")
['1', '2', '3']

>>> await client.sort("mylist", order=OrderBy.DESC)
['3', '2', '1']

>>> await client.lpush("mylist", '2', '1', '2', '3', '3', '1')
>>> await client.sort("mylist", limit=Limit(2, 3))
['1', '2', '2']

>>> await client.lpush("mylist", "a", "b", "c", "d")
>>> await client.sort("mylist", limit=Limit(2, 2), order=OrderBy.DESC, alpha=True)
['b', 'a']
"""
args = _build_sort_args(key, None, limit, None, order, alpha)
result = await self._execute_command(RequestType.Sort, args)
return cast(List[str], result)

GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
async def sort_store(
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
self,
key: str,
destination: str,
limit: Optional[Limit] = None,
order: Optional[OrderBy] = None,
alpha: Optional[bool] = None,
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
) -> int:
"""
Sorts the elements in the list, set, or sorted set at `key` and stores the result in `store`.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
When in cluster mode, `key` and `store` must map to the same hash slot.
To get the sort result without storing it into a key, see `sort`.

See https://valkey.io/commands/sort for more details.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved

Args:
key (str): The key of the list, set, or sorted set to be sorted.
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
destination (str): The key where the sorted result will be stored.
limit (Optional[Limit]): Limiting the range of the query by setting offset and result count. See `Limit` class for more information.
order (Optional[OrderBy]): Specifies the order to sort the elements.
Can be `OrderBy.ASC` (ascending) or `OrderBy.DESC` (descending).
alpha (Optional[bool]): When `True`, sorts elements lexicographically. When `False` (default), sorts elements numerically.
Use this when the list, set, or sorted set contains string values that cannot be converted into double precision floating point numbers.

Returns:
int: The number of elements in the sorted key stored at `store`.

Examples:
>>> await client.lpush("mylist", 3, 1, 2)
>>> await client.sort_store("mylist", "sorted_list")
3 # Indicates that the sorted list "sorted_list" contains three elements.
>>> await client.lrange("sorted_list", 0, -1)
['1', '2', '3']
"""
args = _build_sort_args(key, None, limit, None, order, alpha, store=destination)
result = await self._execute_command(RequestType.Sort, args)
return cast(int, result)
45 changes: 45 additions & 0 deletions python/python/glide/async_commands/command_args.py
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0

from enum import Enum
from typing import List, Optional, Union


class Limit:
"""
Represents a limit argument for range queries in various Redis commands.

The `LIMIT` argument is commonly used to specify a subset of results from the matching elements,
similar to the `LIMIT` clause in SQL (e.g., `SELECT LIMIT offset, count`).

This class can be utilized in multiple Redis commands that support limit options,
such as [ZRANGE](https://valkey.io/commands/zrange), [SORT](https://valkey.io/commands/sort/), and others.

Args:
offset (int): The starting position of the range, zero based.
count (int): The maximum number of elements to include in the range.
A negative count returns all elements from the offset.

Examples:
>>> limit = Limit(0, 10) # Fetch the first 10 elements
>>> limit = Limit(5, -1) # Fetch all elements starting from the 5th element
"""

def __init__(self, offset: int, count: int):
self.offset = offset
self.count = count


class OrderBy(Enum):
"""
SORT order options: options for sorting elements.
"""

ASC = "ASC"
"""
ASC: Sort in ascending order.
"""

DESC = "DESC"
"""
DESC: Sort in descending order.
"""
34 changes: 34 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
get_args,
)

from glide.async_commands.command_args import Limit, OrderBy
from glide.async_commands.sorted_set import (
AggregationType,
InfBound,
Expand Down Expand Up @@ -361,6 +362,39 @@ class InsertPosition(Enum):
AFTER = "AFTER"


def _build_sort_args(
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
key: str,
by_pattern: Optional[str] = None,
limit: Optional[Limit] = None,
get_patterns: Optional[List[str]] = None,
order: Optional[OrderBy] = None,
alpha: Optional[bool] = None,
store: Optional[str] = None,
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
) -> List[str]:
args = [key]

if by_pattern:
args.extend(["BY", by_pattern])

if limit:
args.extend(["LIMIT", str(limit.offset), str(limit.count)])

if get_patterns:
for pattern in get_patterns:
args.extend(["GET", pattern])

if order:
args.append(order.value)

if alpha:
args.append("ALPHA")

if store:
args.extend(["STORE", store])

return args


class CoreCommands(Protocol):
async def _execute_command(
self,
Expand Down
19 changes: 2 additions & 17 deletions python/python/glide/async_commands/sorted_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from enum import Enum
from typing import List, Optional, Tuple, Union

from glide.async_commands.command_args import Limit


class InfBound(Enum):
"""
Expand Down Expand Up @@ -88,23 +90,6 @@ def __init__(self, value: str, is_inclusive: bool = True):
self.value = f"[{value}" if is_inclusive else f"({value}"


class Limit:
GilboaAWS marked this conversation as resolved.
Show resolved Hide resolved
"""
Represents a limit argument for a range query in a sorted set to be used in [ZRANGE](https://redis.io/commands/zrange) command.

The optional LIMIT argument can be used to obtain a sub-range from the matching elements
(similar to SELECT LIMIT offset, count in SQL).
Args:
offset (int): The offset from the start of the range.
count (int): The number of elements to include in the range.
A negative count returns all elements from the offset.
"""

def __init__(self, offset: int, count: int):
self.offset = offset
self.count = count


class RangeByIndex:
"""
Represents a range by index (rank) in a sorted set.
Expand Down
Loading
Loading