mirror of
https://github.com/swift-server/RediStack.git
synced 2026-05-03 07:32:28 +00:00
Rework SortedSet and List range APIs
Motivation: The SortedSet and List range commands (LTRIM, LRANGE, ZRANGE, etc.) are stringly-based and not flexible with Swift syntax. Modifications: - Add overloads of LTRIM that support the gambit of Range Standard Library types - Rework LRANGE to mirror LTRIM method signatures - Rework ZScore Range based commands to be more type-safe with `RedisZScoreBound` enum - Rework ZLex Range based commands to be more type-safe with `RedisZLexBound` enum - Rework ZCOUNT, ZLEXCOUNT, ZRANGE, ZREVRANGE, ZREMRANGEBYLEX, ZREMRANGEBYRANK, ZREMRANGEBYSCORE methods to be more type-safe and support Swift Range syntax Result: Working with SortedSet ranges should be much more type safe, and expressive with Swift's Range syntax.
This commit is contained in:
@@ -90,15 +90,19 @@ extension RedisClient {
|
||||
return send(command: "LREM", with: args)
|
||||
.convertFromRESPValue()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trims a list to only contain elements within the specified inclusive bounds of 0-based indices.
|
||||
// MARK: LTrim
|
||||
|
||||
extension RedisClient {
|
||||
/// Trims a List to only contain elements within the specified inclusive bounds of 0-based indices.
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the list to trim.
|
||||
/// - key: The key of the List to trim.
|
||||
/// - start: The index of the first element to keep.
|
||||
/// - stop: The index of the last element to keep.
|
||||
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, before start: Int, after stop: Int) -> EventLoopFuture<Void> {
|
||||
let args: [RESPValue] = [
|
||||
@@ -109,28 +113,295 @@ extension RedisClient {
|
||||
return send(command: "LTRIM", with: args)
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Trims a List to only contain elements within the specified inclusive bounds of 0-based indices.
|
||||
///
|
||||
/// To keep elements 4 through 7:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: 3...6)
|
||||
/// ```
|
||||
///
|
||||
/// To keep the last 4 through 7 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: (-7)...(-4))
|
||||
/// ```
|
||||
///
|
||||
/// To keep the first and last 4 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: (-4)...3)
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Warning: A `ClosedRange` cannot be created where `upperBound` is less than `lowerBound`; so while Redis may support `0...-1`,
|
||||
/// `ClosedRange` will trigger a precondition failure.
|
||||
///
|
||||
/// If you need such a range, use `ltrim(_:before:after:)` instead.
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to trim.
|
||||
/// - range: The range of indices that should be kept in the List.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, keepingIndices range: ClosedRange<Int>) -> EventLoopFuture<Void> {
|
||||
return self.ltrim(key, before: range.lowerBound, after: range.upperBound)
|
||||
}
|
||||
|
||||
/// Gets all elements from a list within the the specified inclusive bounds of 0-based indices.
|
||||
/// Trims a List to only contain elements starting from the specified index.
|
||||
///
|
||||
/// To keep all but the first 3 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: 3...)
|
||||
/// ```
|
||||
///
|
||||
/// To keep the last 4 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: (-4)...)
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to trim.
|
||||
/// - range: The range of indices that should be kept in the List.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, keepingIndices range: PartialRangeFrom<Int>) -> EventLoopFuture<Void> {
|
||||
return self.ltrim(key, before: range.lowerBound, after: -1)
|
||||
}
|
||||
|
||||
/// Trims a List to only contain elements before the specified index.
|
||||
///
|
||||
/// To keep the first 3 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: ..<3)
|
||||
/// ```
|
||||
///
|
||||
/// To keep all but the last 4 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: ..<(-4))
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to trim.
|
||||
/// - range: The range of indices that should be kept in the List.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, keepingIndices range: PartialRangeUpTo<Int>) -> EventLoopFuture<Void> {
|
||||
return self.ltrim(key, before: 0, after: range.upperBound - 1)
|
||||
}
|
||||
|
||||
/// Trims a List to only contain elements up to the specified index.
|
||||
///
|
||||
/// To keep the first 4 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: ...3)
|
||||
/// ```
|
||||
///
|
||||
/// To keep all but the last 3 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: ...(-4))
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to trim.
|
||||
/// - range: The range of indices that should be kept in the List.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, keepingIndices range: PartialRangeThrough<Int>) -> EventLoopFuture<Void> {
|
||||
return self.ltrim(key, before: 0, after: range.upperBound)
|
||||
}
|
||||
|
||||
/// Trims a List to only contain the elements from the specified index up to the index provided.
|
||||
///
|
||||
/// To keep the first 4 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: 0..<4)
|
||||
/// ```
|
||||
///
|
||||
/// To keep all but the last 3 elements:
|
||||
/// ```swift
|
||||
/// client.ltrim("myList", keepingIndices: 0..<(-3))
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// - Warning: A `Range` cannot be created where `upperBound` is less than `lowerBound`; so while Redis may support `0..<(-1)`,
|
||||
/// `Range` will trigger a precondition failure.
|
||||
///
|
||||
/// If you need such a range, use `ltrim(_:before:after:)` instead.
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to trim.
|
||||
/// - range: The range of indices that should be kept in the List.
|
||||
/// - Returns: A `NIO.EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: RedisKey, keepingIndices range: Range<Int>) -> EventLoopFuture<Void> {
|
||||
return self.ltrim(key, before: range.lowerBound, after: range.upperBound - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: LRange
|
||||
|
||||
extension RedisClient {
|
||||
/// Gets all elements from a List within the the specified inclusive bounds of 0-based indices.
|
||||
///
|
||||
/// See [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Parameters:
|
||||
/// - range: The range of inclusive indices of elements to get.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: A list of elements found within the range specified.
|
||||
/// - key: The key of the List.
|
||||
/// - firstIndex: The index of the first element to include in the range of elements returned.
|
||||
/// - lastIndex: The index of the last element to include in the range of elements returned.
|
||||
/// - Returns: An array of elements found within the range specified.
|
||||
@inlinable
|
||||
public func lrange(
|
||||
within range: (startIndex: Int, endIndex: Int),
|
||||
from key: RedisKey
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
public func lrange(from key: RedisKey, firstIndex: Int, lastIndex: Int) -> EventLoopFuture<[RESPValue]> {
|
||||
let args: [RESPValue] = [
|
||||
.init(bulk: key),
|
||||
.init(bulk: range.startIndex),
|
||||
.init(bulk: range.endIndex)
|
||||
.init(bulk: firstIndex),
|
||||
.init(bulk: lastIndex)
|
||||
]
|
||||
return send(command: "LRANGE", with: args)
|
||||
.convertFromRESPValue()
|
||||
}
|
||||
|
||||
/// Gets all elements from a List within the specified inclusive bounds of 0-based indices.
|
||||
///
|
||||
/// To get the elements at index 4 through 7:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: 4...7)
|
||||
/// ```
|
||||
///
|
||||
/// To get the last 4 elements:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)...(-1))
|
||||
/// ```
|
||||
///
|
||||
/// To get the first and last 4 elements:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)...3)
|
||||
/// ```
|
||||
///
|
||||
/// To get the first element, and the last 4:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)...0))
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Warning: A `ClosedRange` cannot be created where `upperBound` is less than `lowerBound`; so while Redis may support `0...-1`,
|
||||
/// `ClosedRange` will trigger a precondition failure.
|
||||
///
|
||||
/// If you need such a range, use `lrange(from:firstIndex:lastIndex:)` instead.
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to return elements from.
|
||||
/// - range: The range of inclusive indices of elements to get.
|
||||
/// - Returns: An array of elements found within the range specified.
|
||||
@inlinable
|
||||
public func lrange(from key: RedisKey, indices range: ClosedRange<Int>) -> EventLoopFuture<[RESPValue]> {
|
||||
return self.lrange(from: key, firstIndex: range.lowerBound, lastIndex: range.upperBound)
|
||||
}
|
||||
|
||||
/// Gets all the elements from a List starting with the first index bound up to, but not including, the element at the last index bound.
|
||||
///
|
||||
/// To get the elements at index 4 through 7:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: 4..<8)
|
||||
/// ```
|
||||
///
|
||||
/// To get the last 4 elements:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)..<0)
|
||||
/// ```
|
||||
///
|
||||
/// To get the first and last 4 elements:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)..<4)
|
||||
/// ```
|
||||
///
|
||||
/// To get the first element, and the last 4:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", indices: (-4)..<1)
|
||||
/// ```
|
||||
///
|
||||
/// See [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Warning: A `Range` cannot be created where `upperBound` is less than `lowerBound`; so while Redis may support `0..<(-1)`,
|
||||
/// `Range` will trigger a precondition failure.
|
||||
///
|
||||
/// If you need such a range, use `lrange(from:firstIndex:lastIndex:)` instead.
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to return elements from.
|
||||
/// - range: The range of indices (inclusive lower, exclusive upper) elements to get.
|
||||
/// - Returns: An array of elements found within the range specified.
|
||||
@inlinable
|
||||
public func lrange(from key: RedisKey, indices range: Range<Int>) -> EventLoopFuture<[RESPValue]> {
|
||||
return self.lrange(from: key, firstIndex: range.lowerBound, lastIndex: range.upperBound - 1)
|
||||
}
|
||||
|
||||
/// Gets all elements from the index specified to the end of a List.
|
||||
///
|
||||
/// To get all except the first 2 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", fromIndex: 2)
|
||||
/// ```
|
||||
///
|
||||
/// To get the last 4 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", fromIndex: -4)
|
||||
/// ```
|
||||
///
|
||||
/// See `lrange(from:indices:)`, `lrange(from:firstIndex:lastIndex:)`, and [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to return elements from.
|
||||
/// - index: The index of the first element that will be in the returned values.
|
||||
/// - Returns: An array of elements from the List between the index and the end.
|
||||
@inlinable
|
||||
public func lrange(from key: RedisKey, fromIndex index: Int) -> EventLoopFuture<[RESPValue]> {
|
||||
return self.lrange(from: key, firstIndex: index, lastIndex: -1)
|
||||
}
|
||||
|
||||
/// Gets all elements from the the start of a List up to, and including, the element at the index specified.
|
||||
///
|
||||
/// To get the first 3 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", throughIndex: 2)
|
||||
/// ```
|
||||
///
|
||||
/// To get all except the last 3 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", throughIndex: -4)
|
||||
/// ```
|
||||
///
|
||||
/// See `lrange(from:indices:)`, `lrange(from:firstIndex:lastIndex:)`, and [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to return elements from.
|
||||
/// - index: The index of the last element that will be in the returned values.
|
||||
/// - Returns: An array of elements from the start of a List to the index.
|
||||
@inlinable
|
||||
public func lrange(from key: RedisKey, throughIndex index: Int) -> EventLoopFuture<[RESPValue]> {
|
||||
return self.lrange(from: key, firstIndex: 0, lastIndex: index)
|
||||
}
|
||||
|
||||
/// Gets all elements from the the start of a List up to, but not including, the element at the index specified.
|
||||
///
|
||||
/// To get the first 3 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", upToIndex: 3)
|
||||
/// ```
|
||||
///
|
||||
/// To get all except the last 3 elements of a List:
|
||||
/// ```swift
|
||||
/// client.lrange(from: "myList", upToIndex: -3)
|
||||
/// ```
|
||||
///
|
||||
/// See `lrange(from:indices:)`, `lrange(from:firstIndex:lastIndex:)`, and [https://redis.io/commands/lrange](https://redis.io/commands/lrange)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the List to return elements from.
|
||||
/// - index: The index of the element to not include in the returned values.
|
||||
/// - Returns: An array of elements from the start of the List and up to the index.
|
||||
@inlinable
|
||||
public func lrange(from key: RedisKey, upToIndex index: Int) -> EventLoopFuture<[RESPValue]> {
|
||||
return self.lrange(from: key, firstIndex: 0, lastIndex: index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Pop & Push
|
||||
|
||||
extension RedisClient {
|
||||
/// Pops the last element from a source list and pushes it to a destination list.
|
||||
///
|
||||
/// See [https://redis.io/commands/rpoplpush](https://redis.io/commands/rpoplpush)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -53,23 +53,28 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_lrange() throws {
|
||||
var elements = try connection.lrange(within: (0, 10), from: #function).wait()
|
||||
var elements = try connection.lrange(from: #function, indices: 0...10).wait()
|
||||
XCTAssertEqual(elements.count, 0)
|
||||
|
||||
_ = try connection.lpush([5, 4, 3, 2, 1], into: #function).wait()
|
||||
|
||||
elements = try connection.lrange(within: (0, 4), from: #function).wait()
|
||||
elements = try connection.lrange(from: #function, throughIndex: 4).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
XCTAssertEqual(Int(fromRESP: elements[0]), 1)
|
||||
XCTAssertEqual(Int(fromRESP: elements[4]), 5)
|
||||
|
||||
elements = try connection.lrange(from: #function, fromIndex: 1).wait()
|
||||
XCTAssertEqual(elements.count, 4)
|
||||
elements = try connection.lrange(from: #function, fromIndex: -3).wait()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
|
||||
elements = try connection.lrange(within: (2, 0), from: #function).wait()
|
||||
elements = try connection.lrange(from: #function, firstIndex: 2, lastIndex: 0).wait()
|
||||
XCTAssertEqual(elements.count, 0)
|
||||
|
||||
elements = try connection.lrange(within: (4, 5), from: #function).wait()
|
||||
elements = try connection.lrange(from: #function, indices: 4...5).wait()
|
||||
XCTAssertEqual(elements.count, 1)
|
||||
|
||||
elements = try connection.lrange(within: (0, -4), from: #function).wait()
|
||||
elements = try connection.lrange(from: #function, upToIndex: -3).wait()
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
}
|
||||
|
||||
@@ -109,13 +114,13 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
|
||||
_ = try connection.lpush([10], into: #function).wait()
|
||||
|
||||
_ = try connection.linsert(20, into: #function, after: 10).wait()
|
||||
var elements = try connection.lrange(within: (0, 1), from: #function)
|
||||
var elements = try connection.lrange(from: #function, throughIndex: 1)
|
||||
.map { response in response.compactMap { Int(fromRESP: $0) } }
|
||||
.wait()
|
||||
XCTAssertEqual(elements, [10, 20])
|
||||
|
||||
_ = try connection.linsert(30, into: #function, before: 10).wait()
|
||||
elements = try connection.lrange(within: (0, 2), from: #function)
|
||||
elements = try connection.lrange(from: #function, throughIndex: 2)
|
||||
.map { response in response.compactMap { Int(fromRESP: $0) } }
|
||||
.wait()
|
||||
XCTAssertEqual(elements, [30, 10, 20])
|
||||
@@ -236,4 +241,36 @@ final class ListCommandsTests: RediStackIntegrationTestCase {
|
||||
.wait()
|
||||
XCTAssertEqual(element, 10)
|
||||
}
|
||||
|
||||
func test_ltrim() throws {
|
||||
let setup = {
|
||||
_ = try self.connection.delete(#function).wait()
|
||||
_ = try self.connection.lpush([5, 4, 3, 2, 1], into: #function).wait()
|
||||
}
|
||||
let getElements = { return try self.connection.lrange(from: #function, fromIndex: 0).wait() }
|
||||
|
||||
try setup()
|
||||
|
||||
XCTAssertNoThrow(try connection.ltrim(#function, before: 1, after: 3).wait())
|
||||
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: 0...1).wait())
|
||||
var elements = try getElements()
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
|
||||
try setup()
|
||||
|
||||
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: (-3)...).wait())
|
||||
elements = try getElements()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
|
||||
try setup()
|
||||
|
||||
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: ...(-4)).wait())
|
||||
elements = try getElements()
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
|
||||
try setup()
|
||||
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: ..<(-2)).wait())
|
||||
elements = try getElements()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,17 +126,41 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zcount() throws {
|
||||
var count = try connection.zcount(of: key, within: ("1", "3")).wait()
|
||||
var count = try connection.zcount(of: key, withScores: 1...3).wait()
|
||||
XCTAssertEqual(count, 3)
|
||||
count = try connection.zcount(of: key, within: ("(1", "(3")).wait()
|
||||
|
||||
count = try connection.zcount(of: key, withScoresBetween: (.exclusive(1), .exclusive(3))).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
|
||||
count = try connection.zcount(of: key, withScores: 3..<8).wait()
|
||||
XCTAssertEqual(count, 5)
|
||||
|
||||
count = try connection.zcount(of: key, withMinimumScoreOf: .exclusive(7)).wait()
|
||||
XCTAssertEqual(count, 3)
|
||||
|
||||
count = try connection.zcount(of: key, withMaximumScoreOf: 10).wait()
|
||||
XCTAssertEqual(count, 10)
|
||||
|
||||
count = try connection.zcount(of: key, withScoresBetween: (3, 0)).wait()
|
||||
XCTAssertEqual(count, 0)
|
||||
}
|
||||
|
||||
func test_zlexcount() throws {
|
||||
var count = try connection.zlexcount(of: key, within: ("[1", "[3")).wait()
|
||||
for i in 1...10 {
|
||||
_ = try connection.zadd((i, 1), to: #function).wait()
|
||||
}
|
||||
|
||||
var count = try connection.zlexcount(of: #function, withValuesBetween: (.inclusive(1), .inclusive(3))).wait()
|
||||
XCTAssertEqual(count, 4)
|
||||
|
||||
count = try connection.zlexcount(of: #function, withValuesBetween: (.exclusive(1), .exclusive(3))).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zlexcount(of: #function, withMinimumValueOf: .inclusive(2)).wait()
|
||||
XCTAssertEqual(count, 8)
|
||||
|
||||
count = try connection.zlexcount(of: #function, withMaximumValueOf: .exclusive(3)).wait()
|
||||
XCTAssertEqual(count, 3)
|
||||
count = try connection.zlexcount(of: key, within: ("(1", "(3")).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
|
||||
func test_zpopmin() throws {
|
||||
@@ -254,9 +278,22 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zrange() throws {
|
||||
var elements = try connection.zrange(within: (1, 3), from: key).wait()
|
||||
var elements = try connection.zrange(from: key, indices: 1...3).wait()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
elements = try connection.zrange(within: (1, 3), from: key, withScores: true).wait()
|
||||
|
||||
elements = try connection.zrange(from: key, indices: 3..<9).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
elements = try connection.zrange(from: key, upToIndex: 4).wait()
|
||||
XCTAssertEqual(elements.count, 4)
|
||||
|
||||
elements = try connection.zrange(from: key, throughIndex: 4).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrange(from: key, fromIndex: 7).wait()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
|
||||
elements = try connection.zrange(from: key, firstIndex: 1, lastIndex: 3, includeScoresInResponse: true).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
let values = try RedisConnection._mapSortedSetResponse(elements, scoreIsFirst: false)
|
||||
@@ -268,9 +305,22 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zrevrange() throws {
|
||||
var elements = try connection.zrevrange(within: (1, 3), from: key).wait()
|
||||
var elements = try connection.zrevrange(from: key, indices: 1...3).wait()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
elements = try connection.zrevrange(within: (1, 3), from: key, withScores: true).wait()
|
||||
|
||||
elements = try connection.zrevrange(from: key, indices: 3..<9).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
elements = try connection.zrevrange(from: key, upToIndex: 4).wait()
|
||||
XCTAssertEqual(elements.count, 4)
|
||||
|
||||
elements = try connection.zrevrange(from: key, throughIndex: 4).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrevrange(from: key, fromIndex: 7).wait()
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
|
||||
elements = try connection.zrevrange(from: key, firstIndex: 1, lastIndex: 3, includeScoresInResponse: true).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
let values = try RedisConnection._mapSortedSetResponse(elements, scoreIsFirst: false)
|
||||
@@ -282,9 +332,19 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zrangebyscore() throws {
|
||||
var elements = try connection.zrangebyscore(within: ("(1", "3"), from: key).wait()
|
||||
var elements = try connection.zrangebyscore(from: key, withScoresBetween: (.exclusive(1), 3)).wait()
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
elements = try connection.zrangebyscore(within: ("1", "3"), from: key, withScores: true).wait()
|
||||
|
||||
elements = try connection.zrangebyscore(from: key, withScores: 7..<10, limitBy: (offset: 2, count: 3)).wait()
|
||||
XCTAssertEqual(elements.count, 1)
|
||||
|
||||
elements = try connection.zrangebyscore(from: key, withMinimumScoreOf: .exclusive(5)).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrangebyscore(from: key, withMaximumScoreOf: 5).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrangebyscore(from: key, withScores: 1...3, includeScoresInResponse: true).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
let values = try RedisConnection._mapSortedSetResponse(elements, scoreIsFirst: false)
|
||||
@@ -296,9 +356,19 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zrevrangebyscore() throws {
|
||||
var elements = try connection.zrevrangebyscore(within: ("(1", "3"), from: key).wait()
|
||||
var elements = try connection.zrevrangebyscore(from: key, withScoresBetween: (.exclusive(1), 3)).wait()
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
elements = try connection.zrevrangebyscore(within: ("1", "3"), from: key, withScores: true).wait()
|
||||
|
||||
elements = try connection.zrevrangebyscore(from: key, withScores: 7..<10, limitBy: (offset: 2, count: 3)).wait()
|
||||
XCTAssertEqual(elements.count, 1)
|
||||
|
||||
elements = try connection.zrevrangebyscore(from: key, withMinimumScoreOf: .exclusive(5)).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrevrangebyscore(from: key, withMaximumScoreOf: 5).wait()
|
||||
XCTAssertEqual(elements.count, 5)
|
||||
|
||||
elements = try connection.zrevrangebyscore(from: key, withScores: 1...3, includeScoresInResponse: true).wait()
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
let values = try RedisConnection._mapSortedSetResponse(elements, scoreIsFirst: false)
|
||||
@@ -310,35 +380,74 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zrangebylex() throws {
|
||||
_ = try connection.zadd([(1, 0), (2, 0), (3, 0)], to: #function).wait()
|
||||
|
||||
var elements = try connection.zrangebylex(within: ("[1", "[2"), from: #function)
|
||||
for i in 1...10 {
|
||||
_ = try connection.zadd((i, 1), to: #function).wait()
|
||||
}
|
||||
|
||||
var elements = try connection.zrangebylex(from: #function, withMinimumValueOf: .exclusive(10))
|
||||
.wait()
|
||||
.map { Int(fromRESP: $0) }
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 8)
|
||||
|
||||
elements = try connection.zrangebylex(from: #function, withMaximumValueOf: .inclusive(5))
|
||||
.wait()
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 6)
|
||||
|
||||
elements = try connection.zrangebylex(from: #function, withValuesBetween: (.inclusive(1), .inclusive(2)))
|
||||
.wait()
|
||||
.map(Int.init(fromRESP:))
|
||||
|
||||
XCTAssertEqual(elements.count, 3)
|
||||
XCTAssertEqual(elements[0], 1)
|
||||
XCTAssertEqual(elements[1], 2)
|
||||
XCTAssertEqual(elements[1], 10)
|
||||
XCTAssertEqual(elements[2], 2)
|
||||
|
||||
elements = try connection.zrangebylex(within: ("[1", "(4"), from: #function, limitBy: (offset: 1, count: 1))
|
||||
elements = try connection
|
||||
.zrangebylex(
|
||||
from: #function,
|
||||
withValuesBetween: (.inclusive(1), .exclusive(4)),
|
||||
limitBy: (offset: 1, count: 1)
|
||||
)
|
||||
.wait()
|
||||
.map { Int(fromRESP: $0) }
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 1)
|
||||
XCTAssertEqual(elements[0], 2)
|
||||
XCTAssertEqual(elements[0], 10)
|
||||
}
|
||||
|
||||
func test_zrevrangebylex() throws {
|
||||
_ = try connection.zadd([(1, 0), (2, 0), (3, 0), (4, 0)], to: #function).wait()
|
||||
|
||||
var elements = try connection.zrevrangebylex(within: ("(2", "[4"), from: #function)
|
||||
for i in 1...10 {
|
||||
_ = try connection.zadd((i, 1), to: #function).wait()
|
||||
}
|
||||
|
||||
var elements = try connection.zrevrangebylex(from: #function, withMinimumValueOf: .inclusive(1))
|
||||
.wait()
|
||||
.map { Int(fromRESP: $0) }
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 10)
|
||||
XCTAssertEqual(elements[0], 9)
|
||||
XCTAssertEqual(elements[9], 1)
|
||||
|
||||
elements = try connection.zrevrangebylex(from: #function, withMaximumValueOf: .exclusive(2))
|
||||
.wait()
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
XCTAssertEqual(elements[0], 10)
|
||||
|
||||
elements = try connection.zrevrangebylex(from: #function, withValuesBetween: (.exclusive(2), .inclusive(4)))
|
||||
.wait()
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
XCTAssertEqual(elements[0], 4)
|
||||
XCTAssertEqual(elements[1], 3)
|
||||
|
||||
elements = try connection.zrevrangebylex(within: ("[1", "(4"), from: #function, limitBy: (offset: 1, count: 2))
|
||||
elements = try connection
|
||||
.zrevrangebylex(
|
||||
from: #function,
|
||||
withValuesBetween: (.inclusive(1), .exclusive(4)),
|
||||
limitBy: (offset: 1, count: 2)
|
||||
)
|
||||
.wait()
|
||||
.map { Int(fromRESP: $0) }
|
||||
.map(Int.init(fromRESP:))
|
||||
XCTAssertEqual(elements.count, 2)
|
||||
XCTAssertEqual(elements[0], 2)
|
||||
}
|
||||
@@ -356,31 +465,54 @@ final class SortedSetCommandsTests: RediStackIntegrationTestCase {
|
||||
}
|
||||
|
||||
func test_zremrangebylex() throws {
|
||||
_ = try connection.zadd([("bar", 0), ("car", 0), ("tar", 0)], to: #function).wait()
|
||||
for value in ["bar", "car", "tar"] {
|
||||
_ = try connection.zadd((value, 0), to: #function).wait()
|
||||
}
|
||||
|
||||
var count = try connection.zremrangebylex(within: ("(a", "[t"), from: #function).wait()
|
||||
var count = try connection.zremrangebylex(from: #function, withValuesBetween: (.exclusive("a"), .inclusive("t"))).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
count = try connection.zremrangebylex(within: ("-", "[t"), from: #function).wait()
|
||||
|
||||
count = try connection.zremrangebylex(from: #function, withMaximumValueOf: .inclusive("t")).wait()
|
||||
XCTAssertEqual(count, 0)
|
||||
count = try connection.zremrangebylex(within: ("[t", "+"), from: #function).wait()
|
||||
|
||||
count = try connection.zremrangebylex(from: #function, withMinimumValueOf: .inclusive("t")).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
|
||||
func test_zremrangebyrank() throws {
|
||||
var count = try connection.zremrangebyrank(within: (0, 3), from: key).wait()
|
||||
XCTAssertEqual(count, 4)
|
||||
count = try connection.zremrangebyrank(within: (0, 10), from: key).wait()
|
||||
XCTAssertEqual(count, 6)
|
||||
count = try connection.zremrangebyrank(within: (0, 3), from: key).wait()
|
||||
XCTAssertEqual(count, 0)
|
||||
var count = try connection.zremrangebyrank(from: key, fromIndex: 9).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
|
||||
count = try connection.zremrangebyrank(from: key, indices: 0...1).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zremrangebyrank(from: key, indices: 0..<2).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zremrangebyrank(from: key, upToIndex: 1).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
|
||||
count = try connection.zremrangebyrank(from: key, throughIndex: 1).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zremrangebyrank(from: key, upToIndex: 0).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
}
|
||||
|
||||
func test_zremrangebyscore() throws {
|
||||
var count = try connection.zremrangebyscore(within: ("(8", "10"), from: key).wait()
|
||||
var count = try connection.zremrangebyscore(from: key, withScoresBetween: (.exclusive(8), 10)).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
count = try connection.zremrangebyscore(within: ("4", "(7"), from: key).wait()
|
||||
|
||||
count = try connection.zremrangebyscore(from: key, withScores: 4..<7).wait()
|
||||
XCTAssertEqual(count, 3)
|
||||
count = try connection.zremrangebyscore(within: ("-inf", "+inf"), from: key).wait()
|
||||
XCTAssertEqual(count, 5)
|
||||
|
||||
count = try connection.zremrangebyscore(from: key, withScores: 2...3).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zremrangebyscore(from: key, withMinimumScoreOf: .exclusive(1)).wait()
|
||||
XCTAssertEqual(count, 2)
|
||||
|
||||
count = try connection.zremrangebyscore(from: key, withMaximumScoreOf: .inclusive(1)).wait()
|
||||
XCTAssertEqual(count, 1)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user