Skip to content

Commit 4c7fe40

Browse files
committed
Add mset, msetnx, mget, incr, incrby, incrbyfloat, decr, and decrby convenience methods with unit tests
1 parent 55fbbcc commit 4c7fe40

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

Sources/NIORedis/Commands/BasicCommands.swift

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ extension RedisCommandExecutor {
8787
.map { return $0.string }
8888
}
8989

90+
/// Returns the values of all specified keys, using `.null` to represent non-existant values.
91+
///
92+
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
93+
public func mget(_ keys: [String]) -> EventLoopFuture<[RESPValue]> {
94+
assert(keys.count > 0, "At least 1 key should be provided.")
95+
96+
return send(command: "MGET", with: keys)
97+
.mapFromRESP()
98+
}
99+
90100
/// Set key to hold the string value.
91101
/// If key already holds a value, it is overwritten, regardless of its type.
92102
/// Any previous time to live associated with the key is discarded on successful SET operation.
@@ -96,4 +106,86 @@ extension RedisCommandExecutor {
96106
return send(command: "SET", with: [key, value])
97107
.map { _ in return () }
98108
}
109+
110+
/// Sets each key to the respective new value, overwriting existing values.
111+
///
112+
/// - Note: Use `msetnx` if you don't want to overwrite values.
113+
///
114+
/// See [https://redis.io/commands/mset](https://redis.io/commands/mset)
115+
public func mset(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Void> {
116+
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
117+
118+
let args = _convertMSET(operations)
119+
return send(command: "MSET", with: args)
120+
.map { _ in return () }
121+
}
122+
123+
/// If every key does not exist, sets each key to the respective new value.
124+
///
125+
/// See [https://redis.io/commands/msetnx](https://redis.io/commands/msetnx)
126+
public func msetnx(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Bool> {
127+
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
128+
129+
let args = _convertMSET(operations)
130+
return send(command: "MSETNX", with: args)
131+
.mapFromRESP(to: Int.self)
132+
.map { return $0 == 1 }
133+
}
134+
135+
@inline(__always)
136+
private func _convertMSET(_ source: [String: RESPValueConvertible]) -> [RESPValueConvertible] {
137+
return source.reduce(into: [RESPValueConvertible](), { (result, element) in
138+
result.append(element.key)
139+
result.append(element.value)
140+
})
141+
}
142+
}
143+
144+
extension RedisCommandExecutor {
145+
/// Increments the stored value by 1 and returns the new value.
146+
///
147+
/// See [https://redis.io/commands/incr](https://redis.io/commands/incr)
148+
/// - Returns: The new value after the operation.
149+
public func increment(_ key: String) -> EventLoopFuture<Int> {
150+
return send(command: "INCR", with: [key])
151+
.mapFromRESP()
152+
}
153+
154+
/// Increments the stored value by the amount desired and returns the new value.
155+
///
156+
/// See [https://redis.io/commands/incrby](https://redis.io/commands/incrby)
157+
/// - Returns: The new value after the operation.
158+
public func increment(_ key: String, by count: Int) -> EventLoopFuture<Int> {
159+
return send(command: "INCRBY", with: [key, count])
160+
.mapFromRESP()
161+
}
162+
163+
/// Increments the stored value by the amount desired and returns the new value.
164+
///
165+
/// See [https://redis.io/commands/incrbyfloat](https://redis.io/commands/incrbyfloat)
166+
/// - Returns: The new value after the operation.
167+
public func increment<T: BinaryFloatingPoint>(_ key: String, by count: T) -> EventLoopFuture<T>
168+
where T: RESPValueConvertible
169+
{
170+
return send(command: "INCRBYFLOAT", with: [key, count])
171+
.mapFromRESP()
172+
}
173+
174+
/// Decrements the stored value by 1 and returns the new value.
175+
///
176+
/// See [https://redis.io/commands/decr](https://redis.io/commands/decr)
177+
/// - Returns: The new value after the operation.
178+
public func decrement(_ key: String) -> EventLoopFuture<Int> {
179+
return send(command: "DECR", with: [key])
180+
.mapFromRESP()
181+
}
182+
183+
/// Decrements the stored valye by the amount desired and returns the new value.
184+
///
185+
/// See [https://redis.io/commands/decrby](https://redis.io/commands/decrby)
186+
/// - Returns: The new value after the operation.
187+
public func decrement(_ key: String, by count: Int) -> EventLoopFuture<Int> {
188+
return send(command: "DECRBY", with: [key, count])
189+
.mapFromRESP()
190+
}
99191
}

Tests/NIORedisTests/Commands/BasicCommandsTests.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,95 @@ final class BasicCommandsTests: XCTestCase {
100100
XCTAssertEqual(first, "100")
101101
}
102102

103+
func test_increment() throws {
104+
var result = try connection?.increment(#function).wait()
105+
XCTAssertEqual(result, 1)
106+
result = try connection?.increment(#function).wait()
107+
XCTAssertEqual(result, 2)
108+
}
109+
110+
func test_incrementBy() throws {
111+
var result = try connection?.increment(#function, by: 10).wait()
112+
XCTAssertEqual(result, 10)
113+
result = try connection?.increment(#function, by: -3).wait()
114+
XCTAssertEqual(result, 7)
115+
result = try connection?.increment(#function, by: 0).wait()
116+
XCTAssertEqual(result, 7)
117+
}
118+
119+
func test_incrementByFloat() throws {
120+
var float = try connection?.increment(#function, by: Float(3.0)).wait()
121+
XCTAssertEqual(float, 3.0)
122+
float = try connection?.increment(#function, by: Float(-10.135901)).wait()
123+
XCTAssertEqual(float, -7.135901)
124+
125+
var double = try connection?.increment(#function, by: Double(10.2839)).wait()
126+
XCTAssertEqual(double, 3.147999)
127+
double = try connection?.increment(#function, by: Double(15.2938)).wait()
128+
XCTAssertEqual(double, 18.441799)
129+
}
130+
131+
func test_decrement() throws {
132+
var result = try connection?.decrement(#function).wait()
133+
XCTAssertEqual(result, -1)
134+
result = try connection?.decrement(#function).wait()
135+
XCTAssertEqual(result, -2)
136+
}
137+
138+
func test_decrementBy() throws {
139+
var result = try connection?.decrement(#function, by: -10).wait()
140+
XCTAssertEqual(result, 10)
141+
result = try connection?.decrement(#function, by: 3).wait()
142+
XCTAssertEqual(result, 7)
143+
result = try connection?.decrement(#function, by: 0).wait()
144+
XCTAssertEqual(result, 7)
145+
}
146+
147+
func test_mget() throws {
148+
let keys = ["one", "two"]
149+
try keys.forEach { _ = try connection?.set($0, to: $0).wait() }
150+
151+
let values = try connection?.mget(keys + ["empty"]).wait()
152+
XCTAssertEqual(values?.count, 3)
153+
XCTAssertEqual(values?[0].string, "one")
154+
XCTAssertEqual(values?[1].string, "two")
155+
XCTAssertEqual(values?[2].isNull, true)
156+
157+
XCTAssertEqual(try connection?.mget(["empty", #function]).wait().count, 2)
158+
}
159+
160+
func test_mset() throws {
161+
let data = [
162+
"first": 1,
163+
"second": 2
164+
]
165+
XCTAssertNoThrow(try connection?.mset(data).wait())
166+
let values = try connection?.mget(["first", "second"]).wait().compactMap { $0.string }
167+
XCTAssertEqual(values?.count, 2)
168+
XCTAssertEqual(values?[0], "1")
169+
XCTAssertEqual(values?[1], "2")
170+
171+
XCTAssertNoThrow(try connection?.mset(["first": 10]).wait())
172+
let val = try connection?.get("first").wait()
173+
XCTAssertEqual(val, "10")
174+
}
175+
176+
func test_msetnx() throws {
177+
let data = [
178+
"first": 1,
179+
"second": 2
180+
]
181+
var success = try connection?.msetnx(data).wait()
182+
XCTAssertEqual(success, true)
183+
184+
success = try connection?.msetnx(["first": 10, "second": 20]).wait()
185+
XCTAssertEqual(success, false)
186+
187+
let values = try connection?.mget(["first", "second"]).wait().compactMap { $0.string }
188+
XCTAssertEqual(values?[0], "1")
189+
XCTAssertEqual(values?[1], "2")
190+
}
191+
103192
static var allTests = [
104193
("test_select", test_select),
105194
("test_set", test_set),
@@ -108,5 +197,14 @@ final class BasicCommandsTests: XCTestCase {
108197
("test_delete", test_delete),
109198
("test_ping", test_ping),
110199
("test_echo", test_echo),
200+
("test_swapdb", test_swapdb),
201+
("test_increment", test_increment),
202+
("test_incrementBy", test_incrementBy),
203+
("test_incrementByFloat", test_incrementByFloat),
204+
("test_decrement", test_decrement),
205+
("test_decrementBy", test_decrementBy),
206+
("test_mget", test_mget),
207+
("test_mset", test_mset),
208+
("test_msetnx", test_msetnx),
111209
]
112210
}

0 commit comments

Comments
 (0)