mirror of
https://github.com/swift-server/RediStack.git
synced 2026-06-02 07:37:33 +00:00
Add mset, msetnx, mget, incr, incrby, incrbyfloat, decr, and decrby convenience methods with unit tests
This commit is contained in:
@@ -87,6 +87,16 @@ extension RedisCommandExecutor {
|
||||
.map { return $0.string }
|
||||
}
|
||||
|
||||
/// Returns the values of all specified keys, using `.null` to represent non-existant values.
|
||||
///
|
||||
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
|
||||
public func mget(_ keys: [String]) -> EventLoopFuture<[RESPValue]> {
|
||||
assert(keys.count > 0, "At least 1 key should be provided.")
|
||||
|
||||
return send(command: "MGET", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Set key to hold the string value.
|
||||
/// If key already holds a value, it is overwritten, regardless of its type.
|
||||
/// Any previous time to live associated with the key is discarded on successful SET operation.
|
||||
@@ -96,4 +106,86 @@ extension RedisCommandExecutor {
|
||||
return send(command: "SET", with: [key, value])
|
||||
.map { _ in return () }
|
||||
}
|
||||
|
||||
/// Sets each key to the respective new value, overwriting existing values.
|
||||
///
|
||||
/// - Note: Use `msetnx` if you don't want to overwrite values.
|
||||
///
|
||||
/// See [https://redis.io/commands/mset](https://redis.io/commands/mset)
|
||||
public func mset(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Void> {
|
||||
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
|
||||
|
||||
let args = _convertMSET(operations)
|
||||
return send(command: "MSET", with: args)
|
||||
.map { _ in return () }
|
||||
}
|
||||
|
||||
/// If every key does not exist, sets each key to the respective new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/msetnx](https://redis.io/commands/msetnx)
|
||||
public func msetnx(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Bool> {
|
||||
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
|
||||
|
||||
let args = _convertMSET(operations)
|
||||
return send(command: "MSETNX", with: args)
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func _convertMSET(_ source: [String: RESPValueConvertible]) -> [RESPValueConvertible] {
|
||||
return source.reduce(into: [RESPValueConvertible](), { (result, element) in
|
||||
result.append(element.key)
|
||||
result.append(element.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Increments the stored value by 1 and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/incr](https://redis.io/commands/incr)
|
||||
/// - Returns: The new value after the operation.
|
||||
public func increment(_ key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "INCR", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the stored value by the amount desired and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/incrby](https://redis.io/commands/incrby)
|
||||
/// - Returns: The new value after the operation.
|
||||
public func increment(_ key: String, by count: Int) -> EventLoopFuture<Int> {
|
||||
return send(command: "INCRBY", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the stored value by the amount desired and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/incrbyfloat](https://redis.io/commands/incrbyfloat)
|
||||
/// - Returns: The new value after the operation.
|
||||
public func increment<T: BinaryFloatingPoint>(_ key: String, by count: T) -> EventLoopFuture<T>
|
||||
where T: RESPValueConvertible
|
||||
{
|
||||
return send(command: "INCRBYFLOAT", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Decrements the stored value by 1 and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/decr](https://redis.io/commands/decr)
|
||||
/// - Returns: The new value after the operation.
|
||||
public func decrement(_ key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "DECR", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Decrements the stored valye by the amount desired and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/decrby](https://redis.io/commands/decrby)
|
||||
/// - Returns: The new value after the operation.
|
||||
public func decrement(_ key: String, by count: Int) -> EventLoopFuture<Int> {
|
||||
return send(command: "DECRBY", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,95 @@ final class BasicCommandsTests: XCTestCase {
|
||||
XCTAssertEqual(first, "100")
|
||||
}
|
||||
|
||||
func test_increment() throws {
|
||||
var result = try connection?.increment(#function).wait()
|
||||
XCTAssertEqual(result, 1)
|
||||
result = try connection?.increment(#function).wait()
|
||||
XCTAssertEqual(result, 2)
|
||||
}
|
||||
|
||||
func test_incrementBy() throws {
|
||||
var result = try connection?.increment(#function, by: 10).wait()
|
||||
XCTAssertEqual(result, 10)
|
||||
result = try connection?.increment(#function, by: -3).wait()
|
||||
XCTAssertEqual(result, 7)
|
||||
result = try connection?.increment(#function, by: 0).wait()
|
||||
XCTAssertEqual(result, 7)
|
||||
}
|
||||
|
||||
func test_incrementByFloat() throws {
|
||||
var float = try connection?.increment(#function, by: Float(3.0)).wait()
|
||||
XCTAssertEqual(float, 3.0)
|
||||
float = try connection?.increment(#function, by: Float(-10.135901)).wait()
|
||||
XCTAssertEqual(float, -7.135901)
|
||||
|
||||
var double = try connection?.increment(#function, by: Double(10.2839)).wait()
|
||||
XCTAssertEqual(double, 3.147999)
|
||||
double = try connection?.increment(#function, by: Double(15.2938)).wait()
|
||||
XCTAssertEqual(double, 18.441799)
|
||||
}
|
||||
|
||||
func test_decrement() throws {
|
||||
var result = try connection?.decrement(#function).wait()
|
||||
XCTAssertEqual(result, -1)
|
||||
result = try connection?.decrement(#function).wait()
|
||||
XCTAssertEqual(result, -2)
|
||||
}
|
||||
|
||||
func test_decrementBy() throws {
|
||||
var result = try connection?.decrement(#function, by: -10).wait()
|
||||
XCTAssertEqual(result, 10)
|
||||
result = try connection?.decrement(#function, by: 3).wait()
|
||||
XCTAssertEqual(result, 7)
|
||||
result = try connection?.decrement(#function, by: 0).wait()
|
||||
XCTAssertEqual(result, 7)
|
||||
}
|
||||
|
||||
func test_mget() throws {
|
||||
let keys = ["one", "two"]
|
||||
try keys.forEach { _ = try connection?.set($0, to: $0).wait() }
|
||||
|
||||
let values = try connection?.mget(keys + ["empty"]).wait()
|
||||
XCTAssertEqual(values?.count, 3)
|
||||
XCTAssertEqual(values?[0].string, "one")
|
||||
XCTAssertEqual(values?[1].string, "two")
|
||||
XCTAssertEqual(values?[2].isNull, true)
|
||||
|
||||
XCTAssertEqual(try connection?.mget(["empty", #function]).wait().count, 2)
|
||||
}
|
||||
|
||||
func test_mset() throws {
|
||||
let data = [
|
||||
"first": 1,
|
||||
"second": 2
|
||||
]
|
||||
XCTAssertNoThrow(try connection?.mset(data).wait())
|
||||
let values = try connection?.mget(["first", "second"]).wait().compactMap { $0.string }
|
||||
XCTAssertEqual(values?.count, 2)
|
||||
XCTAssertEqual(values?[0], "1")
|
||||
XCTAssertEqual(values?[1], "2")
|
||||
|
||||
XCTAssertNoThrow(try connection?.mset(["first": 10]).wait())
|
||||
let val = try connection?.get("first").wait()
|
||||
XCTAssertEqual(val, "10")
|
||||
}
|
||||
|
||||
func test_msetnx() throws {
|
||||
let data = [
|
||||
"first": 1,
|
||||
"second": 2
|
||||
]
|
||||
var success = try connection?.msetnx(data).wait()
|
||||
XCTAssertEqual(success, true)
|
||||
|
||||
success = try connection?.msetnx(["first": 10, "second": 20]).wait()
|
||||
XCTAssertEqual(success, false)
|
||||
|
||||
let values = try connection?.mget(["first", "second"]).wait().compactMap { $0.string }
|
||||
XCTAssertEqual(values?[0], "1")
|
||||
XCTAssertEqual(values?[1], "2")
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
("test_select", test_select),
|
||||
("test_set", test_set),
|
||||
@@ -108,5 +197,14 @@ final class BasicCommandsTests: XCTestCase {
|
||||
("test_delete", test_delete),
|
||||
("test_ping", test_ping),
|
||||
("test_echo", test_echo),
|
||||
("test_swapdb", test_swapdb),
|
||||
("test_increment", test_increment),
|
||||
("test_incrementBy", test_incrementBy),
|
||||
("test_incrementByFloat", test_incrementByFloat),
|
||||
("test_decrement", test_decrement),
|
||||
("test_decrementBy", test_decrementBy),
|
||||
("test_mget", test_mget),
|
||||
("test_mset", test_mset),
|
||||
("test_msetnx", test_msetnx),
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user