diff --git a/python/python/glide/__init__.py b/python/python/glide/__init__.py index b9e587c0e4..3fd31c5d22 100644 --- a/python/python/glide/__init__.py +++ b/python/python/glide/__init__.py @@ -1,5 +1,6 @@ # Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( ConditionalChange, ExpireOptions, @@ -9,7 +10,6 @@ GeoUnit, InfoSection, InsertPosition, - SortOrder, StreamAddOptions, StreamTrimOptions, TrimByMaxLen, @@ -21,7 +21,6 @@ AggregationType, InfBound, LexBoundary, - Limit, RangeByIndex, RangeByLex, RangeByScore, diff --git a/python/python/glide/async_commands/cluster_commands.py b/python/python/glide/async_commands/cluster_commands.py index 2a546f303b..bf6c76a730 100644 --- a/python/python/glide/async_commands/cluster_commands.py +++ b/python/python/glide/async_commands/cluster_commands.py @@ -4,10 +4,10 @@ from typing import Dict, List, Mapping, Optional, Tuple, cast +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( CoreCommands, InfoSection, - SortOrder, _build_sort_args, ) from glide.async_commands.transaction import BaseTransaction, ClusterTransaction @@ -349,34 +349,47 @@ async def time(self, route: Optional[Route] = None) -> TClusterResponse[List[str async def sort( self, key: str, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, - ) -> List[Optional[str]]: + ) -> List[str]: """ Sorts the elements in the list, set, or sorted set at `key` and returns the result. To store the result into a new key, see `sort_store`. + By default, sorting is numeric, and elements are compared by their value interpreted as double precision floating point numbers. + See https://valkey-io.github.io/commands/sort/ for more details. Args: key (str): The key of the list, set, or sorted set to be sorted. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. - order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). - alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. + limit (Optional[Limit]): The limit argument for a range query. Defaults to None. See `Limit` class for more information. + A tuple specifying the offset and count for limiting the number of results returned. + The `limit` parameter takes a tuple `(offset, count)` where `offset` specifies the starting position and `count` specifies the maximum number of elements to return. + If `offset` exceeds the length of the returned result, an empty list is returned. + order (Optional[SortOrder]): Specifies the order to sort the elements. + Can be `SortOrder.ASC` (ascending) or `SortOrder.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: - List[Optional[str]]: Returns a list of sorted elements. + List[str]: A list of sorted elements. Examples: - >>> await client.lpush("mylist", 3, 1, 2) + >>> await client.lpush("mylist", '3', '1', '2') >>> await client.sort("mylist") ['1', '2', '3'] + >>> await client.sort("mylist", order=SortOrder.DESC) ['3', '2', '1'] - >>> await client.lpush("mylist", 2, 1, 2, 3, 3, 1) + + >>> await client.lpush("mylist", '2', '1', '2', '3', '3', '1') >>> await client.sort("mylist", limit=(2, 3)) ['2', '2', '3'] + + >>> await client.lpush("mylist", "a", "b", "c", "d") + >>> await client.sort("mylist", limit=(3, 2), order=SortOrder.DESC, alpha=True) + ['b', 'a'] """ args = _build_sort_args(key, None, limit, None, order, alpha) result = await self._execute_command(RequestType.Sort, args) diff --git a/python/python/glide/async_commands/command_args.py b/python/python/glide/async_commands/command_args.py new file mode 100644 index 0000000000..6505fd2eae --- /dev/null +++ b/python/python/glide/async_commands/command_args.py @@ -0,0 +1,47 @@ +# Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 + +from enum import Enum +from typing import List, Optional, Tuple, 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. + 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 + + self.count = count + + +class SortOrder(Enum): + """ + SORT order options: options for sorting elements. + """ + + ASC = "ASC" + """ + ASC: Sort in ascending order. + """ + + DESC = "DESC" + """ + DESC: Sort in descending order. + """ diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py index 345856e6f3..00c62635fa 100644 --- a/python/python/glide/async_commands/core.py +++ b/python/python/glide/async_commands/core.py @@ -16,6 +16,7 @@ get_args, ) +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.sorted_set import ( AggregationType, InfBound, @@ -136,22 +137,6 @@ class UpdateOptions(Enum): GREATER_THAN = "GT" -class SortOrder(Enum): - """ - SORT order options: options for sorting elements. - """ - - ASC = "ASC" - """ - ASC: Sort in ascending order. - """ - - DESC = "DESC" - """ - DESC: Sort in descending order. - """ - - class GeospatialData: def __init__(self, longitude: float, latitude: float): """ @@ -381,7 +366,7 @@ class InsertPosition(Enum): def _build_sort_args( key: str, by_pattern: Optional[str] = None, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, get_patterns: Optional[List[str]] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, @@ -393,7 +378,7 @@ def _build_sort_args( args.extend(["BY", by_pattern]) if limit: - args.extend(["LIMIT", str(limit[0]), str(limit[1])]) + args.extend(["LIMIT", str(limit.offset), str(limit.count)]) if get_patterns: for pattern in get_patterns: diff --git a/python/python/glide/async_commands/sorted_set.py b/python/python/glide/async_commands/sorted_set.py index 69670fb59b..d5883c25c6 100644 --- a/python/python/glide/async_commands/sorted_set.py +++ b/python/python/glide/async_commands/sorted_set.py @@ -1,6 +1,7 @@ # Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 from enum import Enum +from glide.async_commands.command_args import Limit from typing import List, Optional, Tuple, Union @@ -88,23 +89,6 @@ def __init__(self, value: str, is_inclusive: bool = True): self.value = f"[{value}" if is_inclusive else f"({value}" -class Limit: - """ - 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. diff --git a/python/python/glide/async_commands/standalone_commands.py b/python/python/glide/async_commands/standalone_commands.py index 471fe3cd17..063fe9f5cc 100644 --- a/python/python/glide/async_commands/standalone_commands.py +++ b/python/python/glide/async_commands/standalone_commands.py @@ -4,10 +4,10 @@ from typing import Dict, List, Mapping, Optional, Tuple, cast +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( CoreCommands, InfoSection, - SortOrder, _build_sort_args, ) from glide.async_commands.transaction import BaseTransaction, Transaction @@ -256,7 +256,7 @@ async def sort( self, key: str, by_pattern: Optional[str] = None, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, get_patterns: Optional[List[str]] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, @@ -271,7 +271,7 @@ async def sort( Args: key (str): The key of the list, set, or sorted set to be sorted. by_pattern (Optional[str]): A pattern to sort by. If not provided, elements are sorted by their value. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. get_patterns (Optional[List[str]]): One or more patterns to extract values to return. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. @@ -303,7 +303,7 @@ async def sort_store( key: str, store: str, by_pattern: Optional[str] = None, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, get_patterns: Optional[List[str]] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, @@ -319,7 +319,7 @@ async def sort_store( key (str): The key of the list, set, or sorted set to be sorted. store (str): The key where the sorted result will be stored. by_pattern (Optional[str]): A pattern to sort by. If not provided, elements are sorted by their value. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. get_patterns (Optional[List[str]]): One or more patterns to extract values to return. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 61193f0666..26183ac996 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -3,6 +3,7 @@ import threading from typing import List, Mapping, Optional, Tuple, TypeVar, Union +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( ConditionalChange, ExpireOptions, @@ -11,7 +12,6 @@ GeoUnit, InfoSection, InsertPosition, - SortOrder, StreamAddOptions, StreamTrimOptions, UpdateOptions, @@ -2500,7 +2500,7 @@ def sort( self: TTransaction, key: str, by_pattern: Optional[str] = None, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, get_patterns: Optional[List[str]] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, @@ -2515,7 +2515,7 @@ def sort( Args: key (str): The key of the list, set, or sorted set to be sorted. by_pattern (Optional[str]): A pattern to sort by. If not provided, elements are sorted by their value. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. get_patterns (Optional[List[str]]): One or more patterns to extract values to return. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. @@ -2531,7 +2531,7 @@ def sort_store( key: str, store: str, by_pattern: Optional[str] = None, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, get_patterns: Optional[List[str]] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, @@ -2547,7 +2547,7 @@ def sort_store( key (str): The key of the list, set, or sorted set to be sorted. store (str): The key where the sorted result will be stored. by_pattern (Optional[str]): A pattern to sort by. If not provided, elements are sorted by their value. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. get_patterns (Optional[List[str]]): One or more patterns to extract values to return. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. @@ -2573,7 +2573,7 @@ class ClusterTransaction(BaseTransaction): def sort( self: TTransaction, key: str, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, ) -> TTransaction: @@ -2585,7 +2585,7 @@ def sort( Args: key (str): The key of the list, set, or sorted set to be sorted. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. @@ -2599,7 +2599,7 @@ def sort_store( self: TTransaction, key: str, store: str, - limit: Optional[Tuple[int, int]] = None, + limit: Optional[Limit] = None, order: Optional[SortOrder] = None, alpha: Optional[bool] = None, ) -> TTransaction: @@ -2613,7 +2613,7 @@ def sort_store( Args: key (str): The key of the list, set, or sorted set to be sorted. store (str): The key where the sorted result will be stored. - limit (Optional[Tuple[int, int]]): A tuple specifying the offset and count for limiting the number of results. + limit (Optional[Limit]): A tuple specifying the offset and count for limiting the number of results. order (Optional[SortOrder]): Specifies the order to sort the elements. Can be `SortOrder.ASC` (ascending) or `SortOrder.DESC` (descending). alpha (Optional[bool]): Whether to sort elements lexicographically. If `False`, elements are sorted numerically. diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index dd84a2beaa..08d82dafdd 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -11,6 +11,7 @@ import pytest from glide import ClosingError, RequestError, Script +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( ConditionalChange, ExpireOptions, @@ -21,7 +22,6 @@ InfBound, InfoSection, InsertPosition, - SortOrder, StreamAddOptions, TrimByMaxLen, TrimByMinId, @@ -3232,7 +3232,7 @@ async def test_sort_and_sort_store_with_get_or_by_args( assert await redis_client.lpush(key, ["3", "1", "2"]) == 3 result = await redis_client.sort( key, - limit=(0, 2), + limit=Limit(0, 2), get_patterns=["user:*->name"], order=SortOrder.ASC, alpha=True, @@ -3243,7 +3243,7 @@ async def test_sort_and_sort_store_with_get_or_by_args( sort_store_result = await redis_client.sort_store( key, store, - limit=(0, 2), + limit=Limit(0, 2), get_patterns=["user:*->name"], order=SortOrder.ASC, alpha=True, @@ -3287,7 +3287,7 @@ async def test_sort_and_sort_store_without_get_or_by_args( assert result == ["1", "2", "3", "4", "5"] # limit argument - result = await redis_client.sort(key, limit=(1, 3)) + result = await redis_client.sort(key, limit=Limit(1, 3)) assert result == ["2", "3", "4"] # order argument @@ -3306,13 +3306,13 @@ async def test_sort_and_sort_store_without_get_or_by_args( # Combining multiple arguments result = await redis_client.sort( - key, limit=(1, 3), order=SortOrder.DESC, alpha=True + key, limit=Limit(1, 3), order=SortOrder.DESC, alpha=True ) assert result == ["5", "4", "3"] # Test sort_store with combined arguments sort_store_result = await redis_client.sort_store( - key, store, limit=(1, 3), order=SortOrder.DESC, alpha=True + key, store, limit=Limit(1, 3), order=SortOrder.DESC, alpha=True ) assert sort_store_result == 3 sorted_list = await redis_client.lrange(store, 0, -1) diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index e8115f1829..e183998562 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -5,10 +5,10 @@ import pytest from glide import RequestError +from glide.async_commands.command_args import Limit, SortOrder from glide.async_commands.core import ( GeospatialData, InsertPosition, - SortOrder, StreamAddOptions, TrimByMinId, ) @@ -339,7 +339,7 @@ async def transaction_test( args.append(5) transaction.sort( key17, - limit=(1, 4), + limit=Limit(1, 4), order=SortOrder.ASC, alpha=True, ) @@ -347,7 +347,7 @@ async def transaction_test( transaction.sort_store( key17, key18, - limit=(1, 4), + limit=Limit(1, 4), order=SortOrder.ASC, alpha=True, )