mirror of
https://github.com/swift-server/RediStack.git
synced 2026-05-03 07:32:28 +00:00
Audit convenience commands
Motivation: Performance, clarity, and uniformity with code documentation, Swift API, and data types.
This commit is contained in:
@@ -7,6 +7,7 @@ extension RedisCommandExecutor {
|
||||
/// See [https://redis.io/commands/echo](https://redis.io/commands/echo)
|
||||
/// - Parameter message: The message to echo.
|
||||
/// - Returns: The message sent with the command.
|
||||
@inlinable
|
||||
public func echo(_ message: String) -> EventLoopFuture<String> {
|
||||
return send(command: "ECHO", with: [message])
|
||||
.mapFromRESP()
|
||||
@@ -15,8 +16,9 @@ extension RedisCommandExecutor {
|
||||
/// Pings the server, which will respond with a message.
|
||||
///
|
||||
/// See [https://redis.io/commands/ping](https://redis.io/commands/ping)
|
||||
/// - Parameter with: The optional message that the server should respond with.
|
||||
/// - Parameter message: The optional message that the server should respond with.
|
||||
/// - Returns: The provided message or Redis' default response of `"PONG"`.
|
||||
@inlinable
|
||||
public func ping(with message: String? = nil) -> EventLoopFuture<String> {
|
||||
let arg = message != nil ? [message] : []
|
||||
return send(command: "PING", with: arg)
|
||||
@@ -26,196 +28,102 @@ extension RedisCommandExecutor {
|
||||
/// Request for authentication in a password-protected Redis server.
|
||||
///
|
||||
/// [https://redis.io/commands/auth](https://redis.io/commands/auth)
|
||||
/// - Parameter password: The password being used to access the Redis server.
|
||||
/// - Returns: An `EventLoopFuture` that resolves when the connection has been authorized, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func authorize(with password: String) -> EventLoopFuture<Void> {
|
||||
return send(command: "AUTH", with: [password])
|
||||
.map { _ in return () }
|
||||
}
|
||||
|
||||
/// Select the Redis logical database having the specified zero-based numeric index.
|
||||
/// New connections always use the database `0`.
|
||||
/// - Note: New connections always use the database `0`.
|
||||
///
|
||||
/// [https://redis.io/commands/select](https://redis.io/commands/select)
|
||||
public func select(database id: Int) -> EventLoopFuture<Void> {
|
||||
return send(command: "SELECT", with: [id.description])
|
||||
/// - Parameter index: The 0-based index of the database that will receive later commands.
|
||||
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func select(database index: Int) -> EventLoopFuture<Void> {
|
||||
return send(command: "SELECT", with: [index])
|
||||
.map { _ in return () }
|
||||
}
|
||||
|
||||
/// Swaps the data of two Redis database by their index ID.
|
||||
/// Swaps the data of two Redis databases by their index IDs.
|
||||
///
|
||||
/// See [https://redis.io/commands/swapdb](https://redis.io/commands/swapdb)
|
||||
/// - Parameters:
|
||||
/// - firstIndex: The index of the first database.
|
||||
/// - secondIndex: The index of the second database.
|
||||
/// - first: The index of the first database.
|
||||
/// - second: The index of the second database.
|
||||
/// - Returns: `true` if the swap was successful.
|
||||
public func swapdb(firstIndex: Int, secondIndex: Int) -> EventLoopFuture<Bool> {
|
||||
return send(command: "SWAPDB", with: [firstIndex, secondIndex])
|
||||
@inlinable
|
||||
public func swapDatabase(_ first: Int, with second: Int) -> EventLoopFuture<Bool> {
|
||||
/// connection.swapDatabase(index: 0, withIndex: 10)
|
||||
return send(command: "SWAPDB", with: [first, second])
|
||||
.mapFromRESP(to: String.self)
|
||||
.map { return $0 == "OK" }
|
||||
}
|
||||
}
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes the specified keys. A key is ignored if it does not exist.
|
||||
///
|
||||
/// [https://redis.io/commands/del](https://redis.io/commands/del)
|
||||
/// - Returns: A future number of keys that were removed.
|
||||
public func delete(_ keys: String...) -> EventLoopFuture<Int> {
|
||||
/// - Parameter keys: A list of keys to delete from the database.
|
||||
/// - Returns: The number of keys deleted from the database.
|
||||
@inlinable
|
||||
public func delete(_ keys: [String]) -> EventLoopFuture<Int> {
|
||||
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
||||
|
||||
return send(command: "DEL", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Set a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
||||
/// A key with an associated timeout is often said to be volatile in Redis terminology.
|
||||
/// Sets a timeout on key. After the timeout has expired, the key will automatically be deleted.
|
||||
/// - Note: A key with an associated timeout is often said to be "volatile" in Redis terminology.
|
||||
///
|
||||
/// [https://redis.io/commands/expire](https://redis.io/commands/expire)
|
||||
/// - Parameters:
|
||||
/// - after: The lifetime (in seconds) the key will expirate at.
|
||||
/// - Returns: A future bool indicating if the expiration was set or not.
|
||||
public func expire(_ key: String, after deadline: Int) -> EventLoopFuture<Bool> {
|
||||
return send(command: "EXPIRE", with: [key, deadline.description])
|
||||
/// - key: The key to set the expiration on.
|
||||
/// - deadline: The time from now the key will expire at.
|
||||
/// - Returns: `true` if the expiration was set.
|
||||
@inlinable
|
||||
public func expire(_ key: String, after deadline: TimeAmount) -> EventLoopFuture<Bool> {
|
||||
let amount = deadline.nanoseconds / 1_000_000_000
|
||||
return send(command: "EXPIRE", with: [key, amount])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Get the value of a key.
|
||||
/// If the key does not exist the value will be `nil`.
|
||||
/// An error is resolved if the value stored at key is not a string, because GET only handles string values.
|
||||
///
|
||||
/// [https://redis.io/commands/get](https://redis.io/commands/get)
|
||||
public func get(_ key: String) -> EventLoopFuture<String?> {
|
||||
return send(command: "GET", with: [key])
|
||||
.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.
|
||||
///
|
||||
/// [https://redis.io/commands/set](https://redis.io/commands/set)
|
||||
public func set(_ key: String, to value: String) -> EventLoopFuture<Void> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
// MARK: Scan
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Incrementally iterates over all keys in the currently selected database.
|
||||
///
|
||||
/// [https://redis.io/commands/scan](https://redis.io/commands/scan)
|
||||
/// - Parameters:
|
||||
/// - startingFrom: The cursor position to start from.
|
||||
/// - position: The cursor position to start from.
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - matching: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of keys stored in the database.
|
||||
/// - match: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of keys found in the database.
|
||||
@inlinable
|
||||
public func scan(
|
||||
startingFrom pos: Int = 0,
|
||||
startingFrom position: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil) -> EventLoopFuture<(Int, [String])>
|
||||
{
|
||||
return _scan(command: "SCAN", resultType: [String].self, nil, pos, count, match)
|
||||
matching match: String? = nil
|
||||
) -> EventLoopFuture<(Int, [String])> {
|
||||
return _scan(command: "SCAN", nil, position, count, match)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@usableFromInline func _scan<T: RESPValueConvertible>(
|
||||
@usableFromInline
|
||||
func _scan<T>(
|
||||
command: String,
|
||||
resultType: T.Type,
|
||||
resultType: T.Type = T.self,
|
||||
_ key: String?,
|
||||
_ pos: Int,
|
||||
_ count: Int?,
|
||||
_ match: String?) -> EventLoopFuture<(Int, T)>
|
||||
_ match: String?
|
||||
) -> EventLoopFuture<(Int, T)>
|
||||
where
|
||||
T: RESPValueConvertible
|
||||
{
|
||||
var args: [RESPValueConvertible] = [pos]
|
||||
|
||||
@@ -237,7 +145,12 @@ extension RedisCommandExecutor {
|
||||
guard
|
||||
let value = result[0].string,
|
||||
let position = Int(value)
|
||||
else { throw RedisError(identifier: #function, reason: "Unexpected value in response: \(result[0])") }
|
||||
else {
|
||||
throw RedisError(
|
||||
identifier: #function,
|
||||
reason: "Unexpected value in response: \(result[0])"
|
||||
)
|
||||
}
|
||||
return position
|
||||
}
|
||||
let elements = response
|
||||
|
||||
@@ -1,185 +1,10 @@
|
||||
import NIO
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Sets the hash field stored at the provided key with the value specified.
|
||||
///
|
||||
/// See [https://redis.io/commands/hset](https://redis.io/commands/hset)
|
||||
/// - Returns: `true` if the hash was created, `false` if it was updated.
|
||||
@inlinable
|
||||
public func hset(_ key: String, field: String, to value: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HSET", with: [key, field, value])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Sets the specified fields to the values provided, overwriting existing values.
|
||||
///
|
||||
/// See [https://redis.io/commands/hmset](https://redis.io/commands/hmset)
|
||||
@inlinable
|
||||
public func hmset(_ key: String, to fields: [String: String]) -> EventLoopFuture<Void> {
|
||||
assert(fields.count > 0, "At least 1 key-value pair should be specified")
|
||||
|
||||
let args: [RESPValueConvertible] = fields.reduce(into: [], { (result, element) in
|
||||
result.append(element.key)
|
||||
result.append(element.value)
|
||||
})
|
||||
|
||||
return send(command: "HMSET", with: [key] + args)
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Sets the specified hash field to the value provided only if the field does not exist.
|
||||
///
|
||||
/// See [https://redis.io/commands/hsetnx](https://redis.io/commands/hsetnx)
|
||||
/// - Returns: The success of setting the field's value.
|
||||
@inlinable
|
||||
public func hsetnx(_ key: String, field: String, to value: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HSETNX", with: [key, field, value])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Gets the value stored in the hash field at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hget](https://redis.io/commands/hget)
|
||||
@inlinable
|
||||
public func hget(_ key: String, field: String) -> EventLoopFuture<String?> {
|
||||
return send(command: "HGET", with: [key, field])
|
||||
.map { return String($0) }
|
||||
}
|
||||
|
||||
/// Returns the values stored in the fields specified at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hmget](https://redis.io/commands/hmget)
|
||||
/// - Returns: A list of values in the same order as the `fields` argument.
|
||||
@inlinable
|
||||
public func hmget(_ key: String, fields: [String]) -> EventLoopFuture<[String?]> {
|
||||
assert(fields.count > 0, "At least 1 field should be specified")
|
||||
|
||||
return send(command: "HMGET", with: [key] + fields)
|
||||
.mapFromRESP(to: [RESPValue].self)
|
||||
.map { return $0.map(String.init) }
|
||||
}
|
||||
|
||||
/// Returns all the fields and values stored at the provided key.
|
||||
///
|
||||
/// See [https://redis.io/commands/hgetall](https://redis.io/commands/hgetall)
|
||||
/// - Returns: A key-value pair list of fields and their values.
|
||||
@inlinable
|
||||
public func hgetall(from key: String) -> EventLoopFuture<[String: String]> {
|
||||
return send(command: "HGETALL", with: [key])
|
||||
.mapFromRESP(to: [String].self)
|
||||
.map(Self.mapHashResponseToDictionary)
|
||||
}
|
||||
|
||||
/// Removes the specified fields from the hash stored at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hdel](https://redis.io/commands/hdel)
|
||||
/// - Returns: The number of fields that were deleted.
|
||||
@inlinable
|
||||
public func hdel(_ key: String, fields: [String]) -> EventLoopFuture<Int> {
|
||||
assert(fields.count > 0, "At least 1 field should be specified")
|
||||
|
||||
return send(command: "HDEL", with: [key] + fields)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Checks if the provided key and field exist.
|
||||
///
|
||||
/// See [https://redis.io/commands/hexists](https://redis.io/commands/hexists)
|
||||
@inlinable
|
||||
public func hexists(_ key: String, field: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HEXISTS", with: [key, field])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Returns the number of fields contained in the hash stored at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hlen](https://redis.io/commands/hlen)
|
||||
/// - Returns: The number of fields in the hash, or 0 if the key doesn't exist.
|
||||
@inlinable
|
||||
public func hlen(of key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "HLEN", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns hash field's value length as a string, stored at the provided key.
|
||||
///
|
||||
/// See [https://redis.io/commands/hstrlen](https://redis.io/commands/hstrlen)
|
||||
@inlinable
|
||||
public func hstrlen(of key: String, field: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "HSTRLEN", with: [key, field])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns all field names in the hash stored at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hkeys](https://redis.io/commands/hkeys)
|
||||
/// - Returns: An array of field names, or an empty array.
|
||||
@inlinable
|
||||
public func hkeys(storedAt key: String) -> EventLoopFuture<[String]> {
|
||||
return send(command: "HKEYS", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns all of the field values stored in hash at the key provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hvals](https://redis.io/commands/hvals)
|
||||
@inlinable
|
||||
public func hvals(storedAt key: String) -> EventLoopFuture<[String]> {
|
||||
return send(command: "HVALS", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the field value stored at the key provided, and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hincrby](https://redis.io/commands/hincrby)
|
||||
@inlinable
|
||||
public func hincrby(_ key: String, field: String, by amount: Int) -> EventLoopFuture<Int> {
|
||||
return send(command: "HINCRBY", with: [key, field, amount])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the field value stored at the key provided, and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hincrbyfloat](https://redis.io/commands/hincrbyfloat)
|
||||
@inlinable
|
||||
public func hincrbyfloat<T: BinaryFloatingPoint>(_ key: String, field: String, by amount: T) -> EventLoopFuture<T>
|
||||
where T: RESPValueConvertible
|
||||
{
|
||||
return send(command: "HINCRBYFLOAT", with: [key, field, amount])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Incrementally iterates over all fields in the hash stored at the key provided.
|
||||
///
|
||||
/// [https://redis.io/commands/scan](https://redis.io/commands/scan)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the hash.
|
||||
/// - atPosition: The position to start the scan from.
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - matching: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of values stored at the keys.
|
||||
@inlinable
|
||||
public func hscan(
|
||||
_ key: String,
|
||||
atPosition pos: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil) -> EventLoopFuture<(Int, [String: String])>
|
||||
{
|
||||
return _scan(command: "HSCAN", resultType: [String].self, key, pos, count, match)
|
||||
.map {
|
||||
let values = Self.mapHashResponseToDictionary($0.1)
|
||||
return ($0.0, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
// MARK: Static Helpers
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
@inline(__always)
|
||||
@usableFromInline
|
||||
static func mapHashResponseToDictionary(_ values: [String]) -> [String: String] {
|
||||
static func _mapHashResponse(_ values: [String]) -> [String: String] {
|
||||
guard values.count > 0 else { return [:] }
|
||||
|
||||
var result: [String: String] = [:]
|
||||
@@ -195,3 +20,254 @@ extension RedisCommandExecutor {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: General
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes the specified fields from a hash.
|
||||
///
|
||||
/// See [https://redis.io/commands/hdel](https://redis.io/commands/hdel)
|
||||
/// - Parameters:
|
||||
/// - fields: The list of field names that should be removed from the hash.
|
||||
/// - key: The key of the hash to delete from.
|
||||
/// - Returns: The number of fields that were deleted.
|
||||
@inlinable
|
||||
public func hdel(_ fields: [String], from key: String) -> EventLoopFuture<Int> {
|
||||
guard fields.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
||||
|
||||
return send(command: "HDEL", with: [key] + fields)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Checks if a hash contains the field specified.
|
||||
///
|
||||
/// See [https://redis.io/commands/hexists](https://redis.io/commands/hexists)
|
||||
/// - Parameters:
|
||||
/// - field: The field name to look for.
|
||||
/// - key: The key of the hash to look within.
|
||||
/// - Returns: `true` if the hash contains the field, `false` if either the key or field do not exist.
|
||||
@inlinable
|
||||
public func hexists(_ field: String, in key: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HEXISTS", with: [key, field])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Gets the number of fields contained in a hash.
|
||||
///
|
||||
/// See [https://redis.io/commands/hlen](https://redis.io/commands/hlen)
|
||||
/// - Parameter key: The key of the hash to get field count of.
|
||||
/// - Returns: The number of fields in the hash, or `0` if the key doesn't exist.
|
||||
@inlinable
|
||||
public func hlen(of key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "HLEN", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Gets the string length of a hash field's value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hstrlen](https://redis.io/commands/hstrlen)
|
||||
/// - Parameters:
|
||||
/// - field: The field name whose value is being accessed.
|
||||
/// - key: The key of the hash.
|
||||
/// - Returns: The string length of the hash field's value, or `0` if the field or hash do not exist.
|
||||
@inlinable
|
||||
public func hstrlen(of field: String, in key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "HSTRLEN", with: [key, field])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Gets all field names in a hash.
|
||||
///
|
||||
/// See [https://redis.io/commands/hkeys](https://redis.io/commands/hkeys)
|
||||
/// - Parameter key: The key of the hash.
|
||||
/// - Returns: A list of field names stored within the hash.
|
||||
@inlinable
|
||||
public func hkeys(in key: String) -> EventLoopFuture<[String]> {
|
||||
return send(command: "HKEYS", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Gets all values stored in a hash.
|
||||
///
|
||||
/// See [https://redis.io/commands/hvals](https://redis.io/commands/hvals)
|
||||
/// - Parameter key: The key of the hash.
|
||||
/// - Returns: A list of all values stored in a hash.
|
||||
@inlinable
|
||||
public func hvals(in key: String) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "HVALS", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Incrementally iterates over all fields in a hash.
|
||||
///
|
||||
/// [https://redis.io/commands/scan](https://redis.io/commands/scan)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the hash.
|
||||
/// - position: The position to start the scan from.
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - match: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of found fields and their values.
|
||||
@inlinable
|
||||
public func hscan(
|
||||
_ key: String,
|
||||
startingFrom position: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil
|
||||
) -> EventLoopFuture<(Int, [String: String])> {
|
||||
return _scan(command: "HSCAN", resultType: [String].self, key, position, count, match)
|
||||
.map {
|
||||
let values = Self._mapHashResponse($0.1)
|
||||
return ($0.0, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Set
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Sets a hash field to the value specified.
|
||||
/// - Note: If you do not want to overwrite existing values, use `hsetnx(_:field:to:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/hset](https://redis.io/commands/hset)
|
||||
/// - Parameters:
|
||||
/// - field: The name of the field in the hash being set.
|
||||
/// - value: The value the hash field should be set to.
|
||||
/// - key: The key that holds the hash.
|
||||
/// - Returns: `true` if the hash was created, `false` if it was updated.
|
||||
@inlinable
|
||||
public func hset(
|
||||
_ field: String,
|
||||
to value: RESPValueConvertible,
|
||||
in key: String
|
||||
) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HSET", with: [key, field, value])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Sets a hash field to the value specified only if the field does not currently exist.
|
||||
/// - Note: If you do not care about overwriting existing values, use `hset(_:field:to:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/hsetnx](https://redis.io/commands/hsetnx)
|
||||
/// - Parameters:
|
||||
/// - field: The name of the field in the hash being set.
|
||||
/// - value: The value the hash field should be set to.
|
||||
/// - key: The key that holds the hash.
|
||||
/// - Returns: `true` if the hash was created.
|
||||
@inlinable
|
||||
public func hsetnx(
|
||||
_ field: String,
|
||||
to value: RESPValueConvertible,
|
||||
in key: String
|
||||
) -> EventLoopFuture<Bool> {
|
||||
return send(command: "HSETNX", with: [key, field, value])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Sets the fields in a hash to the respective values provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/hmset](https://redis.io/commands/hmset)
|
||||
/// - Parameters:
|
||||
/// - fields: The key-value pair of field names and their respective values to set.
|
||||
/// - key: The key that holds the hash.
|
||||
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func hmset(
|
||||
_ fields: [String: RESPValueConvertible],
|
||||
in key: String
|
||||
) -> EventLoopFuture<Void> {
|
||||
assert(fields.count > 0, "At least 1 key-value pair should be specified")
|
||||
|
||||
let args: [RESPValueConvertible] = fields.reduce(into: [], { (result, element) in
|
||||
result.append(element.key)
|
||||
result.append(element.value)
|
||||
})
|
||||
|
||||
return send(command: "HMSET", with: [key] + args)
|
||||
.map { _ in () }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Get
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Gets a hash field's value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hget](https://redis.io/commands/hget)
|
||||
/// - Parameters:
|
||||
/// - field: The name of the field whose value is being accessed.
|
||||
/// - key: The key of the hash being accessed.
|
||||
/// - Returns: The value of the hash field, or `nil` if either the key or field does not exist.
|
||||
@inlinable
|
||||
public func hget(_ field: String, from key: String) -> EventLoopFuture<String?> {
|
||||
return send(command: "HGET", with: [key, field])
|
||||
.map { return String($0) }
|
||||
}
|
||||
|
||||
/// Gets the values of a hash for the fields specified.
|
||||
///
|
||||
/// See [https://redis.io/commands/hmget](https://redis.io/commands/hmget)
|
||||
/// - Parameters:
|
||||
/// - fields: A list of field names to get values for.
|
||||
/// - key: The key of the hash being accessed.
|
||||
/// - Returns: A list of values in the same order as the `fields` argument. Non-existent fields return `nil` values.
|
||||
@inlinable
|
||||
public func hmget(_ fields: [String], from key: String) -> EventLoopFuture<[String?]> {
|
||||
guard fields.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "HMGET", with: [key] + fields)
|
||||
.mapFromRESP(to: [RESPValue].self)
|
||||
.map { return $0.map(String.init) }
|
||||
}
|
||||
|
||||
/// Returns all the fields and values stored in a hash.
|
||||
///
|
||||
/// See [https://redis.io/commands/hgetall](https://redis.io/commands/hgetall)
|
||||
/// - Parameter key: The key of the hash to pull from.
|
||||
/// - Returns: A key-value pair list of fields and their values.
|
||||
@inlinable
|
||||
public func hgetall(from key: String) -> EventLoopFuture<[String: String]> {
|
||||
return send(command: "HGETALL", with: [key])
|
||||
.mapFromRESP(to: [String].self)
|
||||
.map(Self._mapHashResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Increment
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Increments a hash field's value and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hincrby](https://redis.io/commands/hincrby)
|
||||
/// - Parameters:
|
||||
/// - amount: The amount to increment the value stored in the field by.
|
||||
/// - field: The name of the field whose value should be incremented.
|
||||
/// - key: The key of the hash the field is stored in.
|
||||
/// - Returns: The new value of the hash field.
|
||||
@inlinable
|
||||
public func hincrby(_ amount: Int, field: String, in key: String) -> EventLoopFuture<Int> {
|
||||
/// connection.hincrby(20, field: "foo", in: "key")
|
||||
return send(command: "HINCRBY", with: [key, field, amount])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments a hash field's value and returns the new value.
|
||||
///
|
||||
/// See [https://redis.io/commands/hincrbyfloat](https://redis.io/commands/hincrbyfloat)
|
||||
/// - Parameters:
|
||||
/// - amount: The amount to increment the value stored in the field by.
|
||||
/// - field: The name of the field whose value should be incremented.
|
||||
/// - key: The key of the hash the field is stored in.
|
||||
/// - Returns: The new value of the hash field.
|
||||
@inlinable
|
||||
public func hincrbyfloat<T>(_ amount: T, field: String, in key: String) -> EventLoopFuture<T>
|
||||
where
|
||||
T: BinaryFloatingPoint,
|
||||
T: RESPValueConvertible
|
||||
{
|
||||
return send(command: "HINCRBYFLOAT", with: [key, field, amount])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,103 @@
|
||||
import NIO
|
||||
|
||||
// MARK: General
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns the length of the list stored at the key provided.
|
||||
/// Gets the length of a list.
|
||||
///
|
||||
/// See [https://redis.io/commands/llen](https://redis.io/commands/llen)
|
||||
/// - Parameter key: The key of the list.
|
||||
/// - Returns: The number of elements in the list.
|
||||
@inlinable
|
||||
public func llen(of key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "LLEN", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns the element at the specified index stored at the key provided.
|
||||
/// Gets the element from a list stored at the provided index position.
|
||||
///
|
||||
/// See [https://redis.io/commands/llen](https://redis.io/commands/llen)
|
||||
/// See [https://redis.io/commands/lindex](https://redis.io/commands/lindex)
|
||||
/// - Parameters:
|
||||
/// - index: The 0-based index of the element to get.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: The element stored at index, or `.null` if out of bounds.
|
||||
@inlinable
|
||||
public func lindex(_ key: String, index: Int) -> EventLoopFuture<RESPValue> {
|
||||
public func lindex(_ index: Int, from key: String) -> EventLoopFuture<RESPValue> {
|
||||
return send(command: "LINDEX", with: [key, index])
|
||||
.flatMapThrowing { response in
|
||||
guard response.isNull else { return response }
|
||||
throw RedisError(identifier: #function, reason: "Index out of bounds.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value at the specified index stored at the key provided.
|
||||
/// Sets the value of an element in a list at the provided index position.
|
||||
///
|
||||
/// See [https://redis.io/commands/lset](https://redis.io/commands/lset)
|
||||
/// - Parameters:
|
||||
/// - index: The 0-based index of the element to set.
|
||||
/// - value: The new value the element should be.
|
||||
/// - key: The key of the list to update.
|
||||
/// - Returns: An `EventLoopFuture` that resolves when the operation has succeeded, or fails with a `RedisError`.
|
||||
@inlinable
|
||||
public func lset(_ key: String, index: Int, to value: RESPValueConvertible) -> EventLoopFuture<Void> {
|
||||
public func lset(
|
||||
index: Int,
|
||||
to value: RESPValueConvertible,
|
||||
in key: String
|
||||
) -> EventLoopFuture<Void> {
|
||||
return send(command: "LSET", with: [key, index, value])
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Removes elements from the list matching the value provided, up to the count specified.
|
||||
/// Removes elements from a list matching the value provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/lrem](https://redis.io/commands/lrem)
|
||||
/// - Returns: The number of elements removed.
|
||||
/// - Parameters:
|
||||
/// - value: The value to delete from the list.
|
||||
/// - key: The key of the list to remove from.
|
||||
/// - count: The max number of elements to remove matching the value. See Redis' documentation for more info.
|
||||
/// - Returns: The number of elements removed from the list.
|
||||
@inlinable
|
||||
public func lrem(_ value: RESPValueConvertible, from key: String, count: Int) -> EventLoopFuture<Int> {
|
||||
public func lrem(
|
||||
_ value: RESPValueConvertible,
|
||||
from key: String,
|
||||
count: Int = 0
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "LREM", with: [key, count, value])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Trims the list stored at the key provided to contain elements within the bounds of indexes specified.
|
||||
/// 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.
|
||||
/// - 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`.
|
||||
@inlinable
|
||||
public func ltrim(_ key: String, startIndex start: Int, endIndex end: Int) -> EventLoopFuture<Void> {
|
||||
return send(command: "LTRIM", with: [key, start, end])
|
||||
public func ltrim(_ key: String, before start: Int, after stop: Int) -> EventLoopFuture<Void> {
|
||||
return send(command: "LTRIM", with: [key, start, stop])
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Returns the elements within the range bounds provided.
|
||||
/// Gets all elements from a list within the the specified inclusive bounds of 0-based indices.
|
||||
///
|
||||
/// See [https://redis.io/commands/ltrim](https://redis.io/commands/ltrim)
|
||||
/// 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.
|
||||
@inlinable
|
||||
public func lrange(of key: String, startIndex start: Int, endIndex end: Int) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "LRANGE", with: [key, start, end])
|
||||
public func lrange(
|
||||
within range: (startIndex: Int, endIndex: Int),
|
||||
from key: String
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "LRANGE", with: [key, range.startIndex, range.endIndex])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Pops the last element from the source list and pushes it to the destination list.
|
||||
/// 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)
|
||||
/// - Parameters:
|
||||
/// - source: The key of the list to pop from.
|
||||
/// - dest: The key of the list to push to.
|
||||
/// - Returns: The element that was moved.
|
||||
@inlinable
|
||||
public func rpoplpush(from source: String, to dest: String) -> EventLoopFuture<RESPValue> {
|
||||
@@ -72,36 +108,44 @@ extension RedisCommandExecutor {
|
||||
// MARK: Insert
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Inserts the value before the first element matching the pivot value provided.
|
||||
/// Inserts the element before the first element matching the "pivot" value specified.
|
||||
///
|
||||
/// See [https://redis.io/commands/linsert](https://redis.io/commands/linsert)
|
||||
/// - Parameters:
|
||||
/// - element: The value to insert into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - pivot: The value of the element to insert before.
|
||||
/// - Returns: The size of the list after the insert, or -1 if an element matching the pivot value was not found.
|
||||
@inlinable
|
||||
public func linsert<T: RESPValueConvertible>(
|
||||
_ value: T,
|
||||
into key: String,
|
||||
before pivot: T) -> EventLoopFuture<Int>
|
||||
public func linsert<T>(_ element: T, into key: String, before pivot: T) -> EventLoopFuture<Int>
|
||||
where T: RESPValueConvertible
|
||||
{
|
||||
return _linsert(pivotKeyword: "BEFORE", value, key, pivot)
|
||||
return _linsert(pivotKeyword: "BEFORE", element, key, pivot)
|
||||
}
|
||||
|
||||
/// Inserts the value after the first element matching the pivot value provided.
|
||||
/// Inserts the element after the first element matching the "pivot" value provided.
|
||||
///
|
||||
/// See [https://redis.io/commands/linsert](https://redis.io/commands/linsert)
|
||||
/// - Parameters:
|
||||
/// - element: The value to insert into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - pivot: The value of the element to insert after.
|
||||
/// - Returns: The size of the list after the insert, or -1 if an element matching the pivot value was not found.
|
||||
@inlinable
|
||||
public func linsert<T: RESPValueConvertible>(
|
||||
_ value: T,
|
||||
into key: String,
|
||||
after pivot: T) -> EventLoopFuture<Int>
|
||||
public func linsert<T>(_ element: T, into key: String, after pivot: T) -> EventLoopFuture<Int>
|
||||
where T: RESPValueConvertible
|
||||
{
|
||||
return _linsert(pivotKeyword: "AFTER", value, key, pivot)
|
||||
return _linsert(pivotKeyword: "AFTER", element, key, pivot)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
@usableFromInline
|
||||
func _linsert(pivotKeyword: StaticString, _ value: RESPValueConvertible, _ key: String, _ pivot: RESPValueConvertible) -> EventLoopFuture<Int> {
|
||||
return send(command: "LINSERT", with: [key, pivotKeyword.description, pivot, value])
|
||||
func _linsert(
|
||||
pivotKeyword: String,
|
||||
_ element: RESPValueConvertible,
|
||||
_ key: String,
|
||||
_ pivot: RESPValueConvertible
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "LINSERT", with: [key, pivotKeyword, pivot, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
@@ -109,34 +153,43 @@ extension RedisCommandExecutor {
|
||||
// MARK: Head Operations
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes the first element in the list and returns it.
|
||||
/// Removes the first element of a list.
|
||||
///
|
||||
/// See [https://redis.io/commands/lpop](https://redis.io/commands/lpop)
|
||||
/// - Parameter key: The key of the list to pop from.
|
||||
/// - Returns: The element that was popped from the list, or `.null`.
|
||||
@inlinable
|
||||
public func lpop(from key: String) -> EventLoopFuture<RESPValue?> {
|
||||
public func lpop(from key: String) -> EventLoopFuture<RESPValue> {
|
||||
return send(command: "LPOP", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Inserts all values provided into the list stored at the key specified.
|
||||
/// - Note: This inserts the values at the head of the list, for the tail see `rpush(_:to:)`.
|
||||
/// Pushes all of the provided elements into a list.
|
||||
/// - Note: This inserts the elements at the head of the list; for the tail see `rpush(_:into:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/lpush](https://redis.io/commands/lpush)
|
||||
/// - Parameters:
|
||||
/// - elements: The values to push into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: The length of the list after adding the new elements.
|
||||
@inlinable
|
||||
public func lpush(_ values: [RESPValueConvertible], to key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "LPUSH", with: [key] + values)
|
||||
public func lpush(_ elements: [RESPValueConvertible], into key: String) -> EventLoopFuture<Int> {
|
||||
assert(elements.count > 0, "At least 1 element should be provided.")
|
||||
|
||||
return send(command: "LPUSH", with: [key] + elements)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Inserts the value at the head of the list only if the key exists and holds a list.
|
||||
/// - Note: This inserts the values at the head of the list, for the tail see `rpushx(_:to:)`.
|
||||
/// Pushes an element into a list, but only if the key exists and holds a list.
|
||||
/// - Note: This inserts the element at the head of the list, for the tail see `rpushx(_:into:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/lpushx](https://redis.io/commands/lpushx)
|
||||
/// - Parameters:
|
||||
/// - element: The value to try and push into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: The length of the list after adding the new elements.
|
||||
@inlinable
|
||||
public func lpushx(_ value: RESPValueConvertible, to key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "LPUSHX", with: [key, value])
|
||||
public func lpushx(_ element: RESPValueConvertible, into key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "LPUSHX", with: [key, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
@@ -144,34 +197,42 @@ extension RedisCommandExecutor {
|
||||
// MARK: Tail Operations
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes the last element in the list and returns it.
|
||||
/// Removes the last element a list.
|
||||
///
|
||||
/// See [https://redis.io/commands/rpop](https://redis.io/commands/rpop)
|
||||
/// - Parameter key: The key of the list to pop from.
|
||||
/// - Returns: The element that was popped from the list, else `.null`.
|
||||
@inlinable
|
||||
public func rpop(from key: String) -> EventLoopFuture<RESPValue?> {
|
||||
public func rpop(from key: String) -> EventLoopFuture<RESPValue> {
|
||||
return send(command: "RPOP", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Inserts all values provided into the list stored at the key specified.
|
||||
/// - Note: This inserts the values at the tail of the list, for the head see `lpush(_:to:)`.
|
||||
/// Pushes all of the provided elements into a list.
|
||||
/// - Note: This inserts the elements at the tail of the list; for the head see `lpush(_:into:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/rpush](https://redis.io/commands/rpush)
|
||||
/// - Returns: The size of the list after adding the new elements.
|
||||
/// - elements: The values to push into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: The length of the list after adding the new elements.
|
||||
@inlinable
|
||||
public func rpush(_ values: [RESPValueConvertible], to key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "RPUSH", with: [key] + values)
|
||||
public func rpush(_ elements: [RESPValueConvertible], into key: String) -> EventLoopFuture<Int> {
|
||||
assert(elements.count > 0, "At least 1 element should be provided.")
|
||||
|
||||
return send(command: "RPUSH", with: [key] + elements)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Inserts the value at the head of the list only if the key exists and holds a list.
|
||||
/// - Note: This inserts the values at the tail of the list, for the head see `lpushx(_:to:)`.
|
||||
/// Pushes an element into a list, but only if the key exists and holds a list.
|
||||
/// - Note: This inserts the element at the tail of the list; for the head see `lpushx(_:into:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/rpushx](https://redis.io/commands/rpushx)
|
||||
/// - Parameters:
|
||||
/// - element: The value to try and push into the list.
|
||||
/// - key: The key of the list.
|
||||
/// - Returns: The length of the list after adding the new elements.
|
||||
@inlinable
|
||||
public func rpushx(_ value: RESPValueConvertible, to key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "RPUSHX", with: [key, value])
|
||||
public func rpushx(_ element: RESPValueConvertible, into key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "RPUSHX", with: [key, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,156 +1,250 @@
|
||||
import Foundation
|
||||
import NIO
|
||||
|
||||
// MARK: General
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns the all of the elements of the set stored at key.
|
||||
///
|
||||
/// Ordering of results are stable between multiple calls of this method to the same set.
|
||||
/// Gets all of the elements contained in a set.
|
||||
/// - Note: Ordering of results are stable between multiple calls of this method to the same set.
|
||||
///
|
||||
/// Results are **UNSTABLE** in regards to the ordering of insertions through the `sadd` command and this method.
|
||||
///
|
||||
/// [https://redis.io/commands/smembers](https://redis.io/commands/smembers)
|
||||
public func smembers(_ key: String) -> EventLoopFuture<RESPValue> {
|
||||
/// See [https://redis.io/commands/smembers](https://redis.io/commands/smembers)
|
||||
/// - Parameter key: The key of the set.
|
||||
/// - Returns: A list of elements found within the set.
|
||||
@inlinable
|
||||
public func smembers(of key: String) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "SMEMBERS", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Checks if the provided item is included in the set stored at key.
|
||||
/// Checks if the element is included in a set.
|
||||
///
|
||||
/// https://redis.io/commands/sismember
|
||||
/// - Parameter item: The element to look in the set for, stored as a `bulkString`.
|
||||
public func sismember(_ key: String, item: RESPValueConvertible) -> EventLoopFuture<Bool> {
|
||||
return send(command: "SISMEMBER", with: [key, item])
|
||||
/// See [https://redis.io/commands/sismember](https://redis.io/commands/sismember)
|
||||
/// - Parameters:
|
||||
/// - element: The element to look for in the set.
|
||||
/// - key: The key of the set to look in.
|
||||
/// - Returns: `true` if the element is in the set.
|
||||
@inlinable
|
||||
public func sismember(_ element: RESPValueConvertible, of key: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "SISMEMBER", with: [key, element])
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Returns the total count of elements in the set stored at key.
|
||||
/// Gets the total count of elements within a set.
|
||||
///
|
||||
/// [https://redis.io/commands/scard](https://redis.io/commands/scard)
|
||||
public func scard(_ key: String) -> EventLoopFuture<Int> {
|
||||
/// See [https://redis.io/commands/scard](https://redis.io/commands/scard)
|
||||
/// - Parameter key: The key of the set.
|
||||
/// - Returns: The total count of elements in the set.
|
||||
@inlinable
|
||||
public func scard(of key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "SCARD", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Adds the provided items to the set stored at key, returning the count of items added.
|
||||
/// Adds elements to a set.
|
||||
///
|
||||
/// [https://redis.io/commands/sadd](https://redis.io/commands/sadd)
|
||||
/// - Parameter items: The elements to add to the set, stored as `bulkString`s.
|
||||
public func sadd(_ key: String, items: [RESPValueConvertible]) -> EventLoopFuture<Int> {
|
||||
assert(items.count > 0, "There must be at least 1 item to add.")
|
||||
/// See [https://redis.io/commands/sadd](https://redis.io/commands/sadd)
|
||||
/// - Parameters:
|
||||
/// - elements: The values to add to the set.
|
||||
/// - key: The key of the set to insert into.
|
||||
/// - Returns: The number of elements that were added to the set.
|
||||
@inlinable
|
||||
public func sadd(_ elements: [RESPValueConvertible], to key: String) -> EventLoopFuture<Int> {
|
||||
guard elements.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
||||
|
||||
return send(command: "SADD", with: [key] + items)
|
||||
return send(command: "SADD", with: [key] + elements)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Removes the provided items from the set stored at key, returning the count of items removed.
|
||||
/// Removes elements from a set.
|
||||
///
|
||||
/// [https://redis.io/commands/srem](https://redis.io/commands/srem)
|
||||
/// - Parameter items: The elemnts to remove from the set, stored as `bulkString`s.
|
||||
public func srem(_ key: String, items: [RESPValueConvertible]) -> EventLoopFuture<Int> {
|
||||
assert(items.count > 0, "There must be at least 1 item listed to remove.")
|
||||
/// See [https://redis.io/commands/srem](https://redis.io/commands/srem)
|
||||
/// - Parameters:
|
||||
/// - elements: The values to remove from the set.
|
||||
/// - key: The key of the set to remove from.
|
||||
/// - Returns: The number of elements that were removed from the set.
|
||||
@inlinable
|
||||
public func srem(_ elements: [RESPValueConvertible], from key: String) -> EventLoopFuture<Int> {
|
||||
guard elements.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
||||
|
||||
return send(command: "SREM", with: [key] + items)
|
||||
return send(command: "SREM", with: [key] + elements)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Randomly selects an item from the set stored at key, and removes it.
|
||||
/// Randomly selects and removes one or more elements in a set.
|
||||
///
|
||||
/// [https://redis.io/commands/spop](https://redis.io/commands/spop)
|
||||
public func spop(_ key: String) -> EventLoopFuture<RESPValue> {
|
||||
return send(command: "SPOP", with: [key])
|
||||
/// See [https://redis.io/commands/spop](https://redis.io/commands/spop)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the set.
|
||||
/// - count: The max number of elements to pop from the set.
|
||||
/// - Returns: The element that was popped from the set.
|
||||
@inlinable
|
||||
public func spop(from key: String, max count: Int = 1) -> EventLoopFuture<[RESPValue]> {
|
||||
assert(count >= 0, "A negative max count is nonsense.")
|
||||
|
||||
guard count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "SPOP", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Randomly selects elements from the set stored at string, up to the `count` provided.
|
||||
/// Use the `RESPValue.array` property to access the underlying values.
|
||||
/// Randomly selects one or more elements in a set.
|
||||
///
|
||||
/// connection.srandmember("my_key") // pulls just one random element
|
||||
/// connection.srandmember("my_key", max: -3) // pulls up to 3 elements, allowing duplicates
|
||||
/// connection.srandmember("my_key", max: 3) // pulls up to 3 elements, guaranteed unique
|
||||
///
|
||||
/// [https://redis.io/commands/srandmember](https://redis.io/commands/srandmember)
|
||||
public func srandmember(_ key: String, max count: Int = 1) -> EventLoopFuture<RESPValue> {
|
||||
assert(count != 0, "A count of zero is a noop for selecting a random element.")
|
||||
/// See [https://redis.io/commands/srandmember](https://redis.io/commands/srandmember)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the set.
|
||||
/// - count: The max number of elements to select from the set.
|
||||
/// - Returns: The elements randomly selected from the set.
|
||||
@inlinable
|
||||
public func srandmember(from key: String, max count: Int = 1) -> EventLoopFuture<[RESPValue]> {
|
||||
guard count != 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "SRANDMEMBER", with: [key, count.description])
|
||||
}
|
||||
|
||||
/// Returns the members of the set resulting from the difference between the first set and all the successive sets.
|
||||
///
|
||||
/// [https://redis.io/commands/sdiff](https://redis.io/commands/sdiff)
|
||||
public func sdiff(_ keys: String...) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "SDIFF", with: keys)
|
||||
return send(command: "SRANDMEMBER", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Functionally equivalent to `sdiff`, but instead stores the resulting set at the `destination` key
|
||||
/// and returns the count of elements in the result set.
|
||||
/// Moves an element from one set to another.
|
||||
///
|
||||
/// [https://redis.io/commands/sdiffstore](https://redis.io/commands/sdiffstore)
|
||||
/// - Important: If the `destination` key already exists, it is overwritten.
|
||||
public func sdiffstore(destination dest: String, _ keys: String...) -> EventLoopFuture<Int> {
|
||||
return send(command: "SDIFFSTORE", with: [dest] + keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
/// See [https://redis.io/commands/smove](https://redis.io/commands/smove)
|
||||
/// - Parameters:
|
||||
/// - element: The value to move from the source.
|
||||
/// - sourceKey: The key of the source set.
|
||||
/// - destKey: The key of the destination set.
|
||||
/// - Returns: `true` if the element was successfully removed from the source set.
|
||||
@inlinable
|
||||
public func smove(
|
||||
_ element: RESPValueConvertible,
|
||||
from sourceKey: String,
|
||||
to destKey: String
|
||||
) -> EventLoopFuture<Bool> {
|
||||
guard sourceKey != destKey else { return self.eventLoop.makeSucceededFuture(true) }
|
||||
|
||||
/// Returns the members of the set resulting from the intersection of all the given sets.
|
||||
///
|
||||
/// [https://redis.io/commands/sinter](https://redis.io/commands/sinter)
|
||||
public func sinter(_ keys: String...) -> EventLoopFuture<[RESPValue]> {
|
||||
return send(command: "SINTER", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Functionally equivalent to `sinter`, but instead stores the resulting set at the `destination` key
|
||||
/// and returns the count of elements in the result set.
|
||||
///
|
||||
/// [https://redis.io/commands/sinterstore](https://redis.io/commands/sinterstore)
|
||||
/// - Important: If the `destination` key already exists, it is overwritten.
|
||||
public func sinterstore(destination dest: String, _ keys: String...) -> EventLoopFuture<Int> {
|
||||
return send(command: "SINTERSTORE", with: [dest] + keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Moves the `item` from the source key to the destination key.
|
||||
///
|
||||
/// [https://redis.io/commands/smove](https://redis.io/commands/smove)
|
||||
/// - Important: This will resolve to `true` as long as it was successfully removed from the `source` key.
|
||||
public func smove(item: RESPValueConvertible, fromKey source: String, toKey dest: String) -> EventLoopFuture<Bool> {
|
||||
return send(command: "SMOVE", with: [source, dest, item])
|
||||
return send(command: "SMOVE", with: [sourceKey, destKey, element])
|
||||
.mapFromRESP()
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Returns the members of the set resulting from the union of all the given keys.
|
||||
/// Incrementally iterates over all values in a set.
|
||||
///
|
||||
/// [https://redis.io/commands/sunion](https://redis.io/commands/sunion)
|
||||
public func sunion(_ keys: String...) -> EventLoopFuture<[RESPValue]> {
|
||||
/// See [https://redis.io/commands/sscan](https://redis.io/commands/sscan)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the set.
|
||||
/// - position: The position to start the scan from.
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - match: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of elements found in the set.
|
||||
@inlinable
|
||||
public func sscan(
|
||||
_ key: String,
|
||||
startingFrom position: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil
|
||||
) -> EventLoopFuture<(Int, [RESPValue])> {
|
||||
return _scan(command: "SSCAN", key, position, count, match)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Diff
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Calculates the difference between two or more sets.
|
||||
///
|
||||
/// See [https://redis.io/commands/sdiff](https://redis.io/commands/sdiff)
|
||||
/// - Parameter keys: The source sets to calculate the difference of.
|
||||
/// - Returns: A list of elements resulting from the difference.
|
||||
@inlinable
|
||||
public func sdiff(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
|
||||
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "SDIFF", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Calculates the difference between two or more sets and stores the result.
|
||||
/// - Important: If the destination key already exists, it is overwritten.
|
||||
///
|
||||
/// See [https://redis.io/commands/sdiffstore](https://redis.io/commands/sdiffstore)
|
||||
/// - Parameters:
|
||||
/// - destination: The key of the new set from the result.
|
||||
/// - sources: The list of source sets to calculate the difference of.
|
||||
/// - Returns: The number of elements in the difference result.
|
||||
@inlinable
|
||||
public func sdiffstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
|
||||
assert(keys.count > 0, "At least 1 key should be provided.")
|
||||
|
||||
return send(command: "SDIFFSTORE", with: [destination] + keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Intersect
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Calculates the intersection of two or more sets.
|
||||
///
|
||||
/// See [https://redis.io/commands/sinter](https://redis.io/commands/sinter)
|
||||
/// - Parameter keys: The source sets to calculate the intersection of.
|
||||
/// - Returns: A list of elements resulting from the intersection.
|
||||
@inlinable
|
||||
public func sinter(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
|
||||
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "SINTER", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Calculates the intersetion of two or more sets and stores the result.
|
||||
/// - Important: If the destination key already exists, it is overwritten.
|
||||
///
|
||||
/// See [https://redis.io/commands/sinterstore](https://redis.io/commands/sinterstore)
|
||||
/// - Parameters:
|
||||
/// - destination: The key of the new set from the result.
|
||||
/// - sources: A list of source sets to calculate the intersection of.
|
||||
/// - Returns: The number of elements in the intersection result.
|
||||
@inlinable
|
||||
public func sinterstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
|
||||
assert(keys.count > 0, "At least 1 key should be provided.")
|
||||
|
||||
return send(command: "SINTERSTORE", with: [destination] + keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Union
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Calculates the union of two or more sets.
|
||||
///
|
||||
/// See [https://redis.io/commands/sunion](https://redis.io/commands/sunion)
|
||||
/// - Parameter keys: The source sets to calculate the union of.
|
||||
/// - Returns: A list of elements resulting from the union.
|
||||
@inlinable
|
||||
public func sunion(of keys: [String]) -> EventLoopFuture<[RESPValue]> {
|
||||
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "SUNION", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Functionally equivalent to `sunion`, but instead stores the resulting set at the `destination` key
|
||||
/// and returns the count of elements in the result set.
|
||||
/// Calculates the union of two or more sets and stores the result.
|
||||
/// - Important: If the destination key already exists, it is overwritten.
|
||||
///
|
||||
/// [https://redis.io/commands/sunionstore](https://redis.io/commands/sunionstore)
|
||||
/// - Important: If the `destination` key already exists, it is overwritten.
|
||||
public func sunionstore(destination dest: String, _ keys: String...) -> EventLoopFuture<Int> {
|
||||
return send(command: "SUNIONSTORE", with: [dest] + keys)
|
||||
/// See [https://redis.io/commands/sunionstore](https://redis.io/commands/sunionstore)
|
||||
/// - Parameters:
|
||||
/// - destination: The key of the new set from the result.
|
||||
/// - sources: A list of source sets to calculate the union of.
|
||||
/// - Returns: The number of elements in the union result.
|
||||
@inlinable
|
||||
public func sunionstore(as destination: String, sources keys: [String]) -> EventLoopFuture<Int> {
|
||||
assert(keys.count > 0, "At least 1 key should be provided.")
|
||||
|
||||
return send(command: "SUNIONSTORE", with: [destination] + keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Incrementally iterates over all values in a set.
|
||||
///
|
||||
/// [https://redis.io/commands/sscan](https://redis.io/commands/sscan)
|
||||
/// - Parameters:
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - matching: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of values stored at the keys.
|
||||
public func sscan(
|
||||
_ key: String,
|
||||
atPosition pos: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil) -> EventLoopFuture<(Int, [RESPValue])>
|
||||
{
|
||||
return _scan(command: "SSCAN", resultType: [RESPValue].self, key, pos, count, match)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ import NIO
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
@usableFromInline
|
||||
static func _mapSortedSetResponse(_ response: [RESPValue], scoreIsFirst: Bool) throws -> [(RESPValue, Double)] {
|
||||
static func _mapSortedSetResponse(
|
||||
_ response: [RESPValue],
|
||||
scoreIsFirst: Bool
|
||||
) throws -> [(RESPValue, Double)] {
|
||||
guard response.count > 0 else { return [] }
|
||||
|
||||
var result: [(RESPValue, Double)] = []
|
||||
@@ -17,8 +20,8 @@ extension RedisCommandExecutor {
|
||||
throw RedisError(identifier: #function, reason: "Unexpected response \"\(scoreItem)\"")
|
||||
}
|
||||
|
||||
let memberIndex = scoreIsFirst ? index + 1 : index
|
||||
result.append((response[memberIndex], score))
|
||||
let elementIndex = scoreIsFirst ? index + 1 : index
|
||||
result.append((response[elementIndex], score))
|
||||
|
||||
index += 2
|
||||
} while (index < response.count)
|
||||
@@ -34,18 +37,21 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zadd](https://redis.io/commands/zadd)
|
||||
/// - Parameters:
|
||||
/// - items: A list of elements and their score to add to the sorted set.
|
||||
/// - to: The key of the sorted set.
|
||||
/// - elements: A list of elements and their score to add to the sorted set.
|
||||
/// - key: The key of the sorted set.
|
||||
/// - options: A set of options defined by Redis for this command to execute under.
|
||||
/// - Returns: The number of elements added to the sorted set.
|
||||
@inlinable
|
||||
public func zadd(
|
||||
_ items: [(element: RESPValueConvertible, score: Double)],
|
||||
_ elements: [(element: RESPValueConvertible, score: Double)],
|
||||
to key: String,
|
||||
options: Set<String> = []) -> EventLoopFuture<Int>
|
||||
{
|
||||
options: Set<String> = []
|
||||
) -> EventLoopFuture<Int> {
|
||||
guard !options.contains("INCR") else {
|
||||
return eventLoop.makeFailedFuture(RedisError(identifier: #function, reason: "INCR option is unsupported. Use zincrby(_:member:by:) instead."))
|
||||
return self.eventLoop.makeFailedFuture(RedisError(
|
||||
identifier: #function,
|
||||
reason: "INCR option is unsupported. Use zincrby(_:element:in:) instead."
|
||||
))
|
||||
}
|
||||
|
||||
assert(options.count <= 2, "Invalid number of options provided.")
|
||||
@@ -57,13 +63,8 @@ extension RedisCommandExecutor {
|
||||
|
||||
var args: [RESPValueConvertible] = [key] + options.map { $0 }
|
||||
|
||||
for (element, score) in items {
|
||||
switch score {
|
||||
case .infinity: args.append("+inf")
|
||||
case -.infinity: args.append("-inf")
|
||||
default: args.append(score)
|
||||
}
|
||||
|
||||
for (element, score) in elements {
|
||||
args.append(score)
|
||||
args.append(element)
|
||||
}
|
||||
|
||||
@@ -75,24 +76,24 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zadd](https://redis.io/commands/zadd)
|
||||
/// - Parameters:
|
||||
/// - item: The element and its score to add to the sorted set.
|
||||
/// - to: The key of the sorted set.
|
||||
/// - element: The element and its score to add to the sorted set.
|
||||
/// - key: The key of the sorted set.
|
||||
/// - options: A set of options defined by Redis for this command to execute under.
|
||||
/// - Returns: `true` if the element was added or score was updated in the sorted set.
|
||||
@inlinable
|
||||
public func zadd(
|
||||
_ item: (element: RESPValueConvertible, score: Double),
|
||||
_ element: (element: RESPValueConvertible, score: Double),
|
||||
to key: String,
|
||||
options: Set<String> = []) -> EventLoopFuture<Bool>
|
||||
{
|
||||
return zadd([item], to: key, options: options)
|
||||
options: Set<String> = []
|
||||
) -> EventLoopFuture<Bool> {
|
||||
return zadd([element], to: key, options: options)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
/// Returns the number of elements in a sorted set.
|
||||
/// Gets the number of elements in a sorted set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zcard](https://redis.io/commands/zcard)
|
||||
/// - Parameter of: The key of the sorted set.
|
||||
/// - Parameter key: The key of the sorted set.
|
||||
/// - Returns: The number of elements in the sorted set.
|
||||
@inlinable
|
||||
public func zcard(of key: String) -> EventLoopFuture<Int> {
|
||||
@@ -100,35 +101,35 @@ extension RedisCommandExecutor {
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns the score of the specified member in a stored set.
|
||||
/// Gets the score of the specified element in a stored set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zscore](https://redis.io/commands/zscore)
|
||||
/// - Parameters:
|
||||
/// - of: The element in the sorted set to get the score for.
|
||||
/// - storedAt: The key of the sorted set.
|
||||
/// - Returns: The score of the element provided.
|
||||
/// - element: The element in the sorted set to get the score for.
|
||||
/// - key: The key of the sorted set.
|
||||
/// - Returns: The score of the element provided, or `nil` if the element is not found in the set or the set does not exist.
|
||||
@inlinable
|
||||
public func zscore(of member: RESPValueConvertible, storedAt key: String) -> EventLoopFuture<Double?> {
|
||||
return send(command: "ZSCORE", with: [key, member])
|
||||
public func zscore(of element: RESPValueConvertible, in key: String) -> EventLoopFuture<Double?> {
|
||||
return send(command: "ZSCORE", with: [key, element])
|
||||
.map { return Double($0) }
|
||||
}
|
||||
|
||||
/// Incrementally iterates over all fields in a sorted set.
|
||||
/// Incrementally iterates over all elements in a sorted set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zscan](https://redis.io/commands/zscan)
|
||||
/// - Parameters:
|
||||
/// - key: The key identifying the sorted set.
|
||||
/// - startingFrom: The position to start the scan from.
|
||||
/// - position: The position to start the scan from.
|
||||
/// - count: The number of elements to advance by. Redis default is 10.
|
||||
/// - matching: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of values and their scores.
|
||||
/// - match: A glob-style pattern to filter values to be selected from the result set.
|
||||
/// - Returns: A cursor position for additional invocations with a limited collection of elements found in the sorted set with their scores.
|
||||
@inlinable
|
||||
public func zscan(
|
||||
_ key: String,
|
||||
startingFrom position: Int = 0,
|
||||
count: Int? = nil,
|
||||
matching match: String? = nil) -> EventLoopFuture<(Int, [(RESPValue, Double)])>
|
||||
{
|
||||
matching match: String? = nil
|
||||
) -> EventLoopFuture<(Int, [(RESPValue, Double)])> {
|
||||
return _scan(command: "ZSCAN", resultType: [RESPValue].self, key, position, count, match)
|
||||
.flatMapThrowing {
|
||||
let values = try Self._mapSortedSetResponse($0.1, scoreIsFirst: false)
|
||||
@@ -142,31 +143,31 @@ extension RedisCommandExecutor {
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns the rank (index) of the specified element in a sorted set.
|
||||
/// - Note: This treats the ordered set as ordered from low to high.
|
||||
/// For the inverse, see `zrevrank(of:storedAt:)`.
|
||||
/// For the inverse, see `zrevrank(of:in:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrank](https://redis.io/commands/zrank)
|
||||
/// - Parameters:
|
||||
/// - of: The element in the sorted set to search for.
|
||||
/// - storedAt: The key of the sorted set to search.
|
||||
/// - element: The element in the sorted set to search for.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - Returns: The index of the element, or `nil` if the key was not found.
|
||||
@inlinable
|
||||
public func zrank(of member: RESPValueConvertible, storedAt key: String) -> EventLoopFuture<Int?> {
|
||||
return send(command: "ZRANK", with: [key, member])
|
||||
public func zrank(of element: RESPValueConvertible, in key: String) -> EventLoopFuture<Int?> {
|
||||
return send(command: "ZRANK", with: [key, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Returns the rank (index) of the specified element in a sorted set.
|
||||
/// - Note: This treats the ordered set as ordered from high to low.
|
||||
/// For the inverse, see `zrank(of:storedAt:)`.
|
||||
/// For the inverse, see `zrank(of:in:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrevrank](https://redis.io/commands/zrevrank)
|
||||
/// - Parameters:
|
||||
/// - of: The element in the sorted set to search for.
|
||||
/// - storedAt: The key of the sorted set to search.
|
||||
/// - element: The element in the sorted set to search for.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - Returns: The index of the element, or `nil` if the key was not found.
|
||||
@inlinable
|
||||
public func zrevrank(of member: RESPValueConvertible, storedAt key: String) -> EventLoopFuture<Int?> {
|
||||
return send(command: "ZREVRANK", with: [key, member])
|
||||
public func zrevrank(of element: RESPValueConvertible, in key: String) -> EventLoopFuture<Int?> {
|
||||
return send(command: "ZREVRANK", with: [key, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
@@ -178,11 +179,14 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zcount](https://redis.io/commands/zcount)
|
||||
/// - Parameters:
|
||||
/// - of: The key of the sorted set to count.
|
||||
/// - within: The min and max range of scores to filter for.
|
||||
/// - key: The key of the sorted set to count.
|
||||
/// - range: The min and max range of scores to filter for.
|
||||
/// - Returns: The number of elements in the sorted set that fit within the score range.
|
||||
@inlinable
|
||||
public func zcount(of key: String, within range: (min: String, max: String)) -> EventLoopFuture<Int> {
|
||||
public func zcount(
|
||||
of key: String,
|
||||
within range: (min: String, max: String)
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZCOUNT", with: [key, range.min, range.max])
|
||||
.mapFromRESP()
|
||||
}
|
||||
@@ -192,11 +196,14 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zlexcount](https://redis.io/commands/zlexcount)
|
||||
/// - Parameters:
|
||||
/// - of: The key of the sorted set to count.
|
||||
/// - within: The min and max range of values to filter for.
|
||||
/// - key: The key of the sorted set to count.
|
||||
/// - range: The min and max range of values to filter for.
|
||||
/// - Returns: The number of elements in the sorted set that fit within the value range.
|
||||
@inlinable
|
||||
public func zlexcount(of key: String, within range: (min: String, max: String)) -> EventLoopFuture<Int> {
|
||||
public func zlexcount(
|
||||
of key: String,
|
||||
within range: (min: String, max: String)
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZLEXCOUNT", with: [key, range.min, range.max])
|
||||
.mapFromRESP()
|
||||
}
|
||||
@@ -205,23 +212,22 @@ extension RedisCommandExecutor {
|
||||
// MARK: Pop
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes members from a sorted set with the lowest scores.
|
||||
/// Removes elements from a sorted set with the lowest scores.
|
||||
///
|
||||
/// See [https://redis.io/commands/zpopmin](https://redis.io/commands/zpopmin)
|
||||
/// - Parameters:
|
||||
/// - key: The key identifying the sorted set in Redis.
|
||||
/// - count: The max number of elements to pop from the set.
|
||||
/// - from: The key identifying the sorted set in Redis.
|
||||
/// - Returns: A list of members popped from the sorted set with their associated score.
|
||||
/// - Returns: A list of elements popped from the sorted set with their associated score.
|
||||
@inlinable
|
||||
public func zpopmin(_ count: Int, from key: String) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
public func zpopmin(from key: String, max count: Int) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
return _zpop(command: "ZPOPMIN", count, key)
|
||||
}
|
||||
|
||||
/// Removes a member from a sorted set with the lowest score.
|
||||
/// Removes the element from a sorted set with the lowest score.
|
||||
///
|
||||
/// See [https://redis.io/commands/zpopmin](https://redis.io/commands/zpopmin)
|
||||
/// - Parameters:
|
||||
/// - from: The key identifying the sorted set in Redis.
|
||||
/// - Parameter key: The key identifying the sorted set in Redis.
|
||||
/// - Returns: The element and its associated score that was popped from the sorted set, or `nil` if set was empty.
|
||||
@inlinable
|
||||
public func zpopmin(from key: String) -> EventLoopFuture<(RESPValue, Double)?> {
|
||||
@@ -229,23 +235,22 @@ extension RedisCommandExecutor {
|
||||
.map { return $0.count > 0 ? $0[0] : nil }
|
||||
}
|
||||
|
||||
/// Removes members from a sorted set with the highest scores.
|
||||
/// Removes elements from a sorted set with the highest scores.
|
||||
///
|
||||
/// See [https://redis.io/commands/zpopmax](https://redis.io/commands/zpopmax)
|
||||
/// - Parameters:
|
||||
/// - key: The key identifying the sorted set in Redis.
|
||||
/// - count: The max number of elements to pop from the set.
|
||||
/// - from: The key identifying the sorted set in Redis.
|
||||
/// - Returns: A list of members popped from the sorted set with their associated score.
|
||||
/// - Returns: A list of elements popped from the sorted set with their associated score.
|
||||
@inlinable
|
||||
public func zpopmax(_ count: Int, from key: String) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
public func zpopmax(from key: String, max count: Int) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
return _zpop(command: "ZPOPMAX", count, key)
|
||||
}
|
||||
|
||||
/// Removes a member from a sorted set with the highest score.
|
||||
/// Removes the element from a sorted set with the highest score.
|
||||
///
|
||||
/// See [https://redis.io/commands/zpopmax](https://redis.io/commands/zpopmax)
|
||||
/// - Parameters:
|
||||
/// - from: The key identifying the sorted set in Redis.
|
||||
/// - Parameter key: The key identifying the sorted set in Redis.
|
||||
/// - Returns: The element and its associated score that was popped from the sorted set, or `nil` if set was empty.
|
||||
@inlinable
|
||||
public func zpopmax(from key: String) -> EventLoopFuture<(RESPValue, Double)?> {
|
||||
@@ -254,10 +259,18 @@ extension RedisCommandExecutor {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func _zpop(command: String, _ count: Int?, _ key: String) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
func _zpop(
|
||||
command: String,
|
||||
_ count: Int?,
|
||||
_ key: String
|
||||
) -> EventLoopFuture<[(RESPValue, Double)]> {
|
||||
var args: [RESPValueConvertible] = [key]
|
||||
|
||||
if let c = count { args.append(c) }
|
||||
if let c = count {
|
||||
guard c != 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
args.append(c)
|
||||
}
|
||||
|
||||
return send(command: command, with: args)
|
||||
.mapFromRESP(to: [RESPValue].self)
|
||||
@@ -268,31 +281,21 @@ extension RedisCommandExecutor {
|
||||
// MARK: Increment
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Increments the score of the specified member in a sorted set.
|
||||
/// Increments the score of the specified element in a sorted set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zincrby](https://redis.io/commands/zincrby)
|
||||
/// - Parameters:
|
||||
/// - amount: The amount to increment this element's score by.
|
||||
/// - element: The element to increment.
|
||||
/// - key: The key of the sorted set.
|
||||
/// - member: The element to increment.
|
||||
/// - by: The amount to increment this element's score by.
|
||||
/// - Returns: The new score of the member.
|
||||
/// - Returns: The new score of the element.
|
||||
@inlinable
|
||||
public func zincrby(_ key: String, member: RESPValueConvertible, by amount: Int) -> EventLoopFuture<Double> {
|
||||
return send(command: "ZINCRBY", with: [key, amount, member])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the score of the specified member in a sorted set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zincrby](https://redis.io/commands/zincrby)
|
||||
/// - Parameters:
|
||||
/// - key: The key of the sorted set.
|
||||
/// - member: The element to increment.
|
||||
/// - by: The amount to increment this element's score by.
|
||||
/// - Returns: The new score of the member.
|
||||
@inlinable
|
||||
public func zincrby(_ key: String, member: RESPValueConvertible, by amount: Double) -> EventLoopFuture<Double> {
|
||||
return send(command: "ZINCRBY", with: [key, amount, member])
|
||||
public func zincrby(
|
||||
_ amount: Double,
|
||||
element: RESPValueConvertible,
|
||||
in key: String
|
||||
) -> EventLoopFuture<Double> {
|
||||
return send(command: "ZINCRBY", with: [key, amount, element])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
@@ -300,43 +303,43 @@ extension RedisCommandExecutor {
|
||||
// MARK: Intersect and Union
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Computes a new sorted set as a union between all provided source sorted sets and stores the result at the key desired.
|
||||
/// Calculates the union of two or more sorted sets and stores the result.
|
||||
/// - Note: This operation overwrites any value stored at the destination key.
|
||||
///
|
||||
/// See [https://redis.io/commands/zunionstore](https://redis.io/commands/zunionstore)
|
||||
/// - Parameters:
|
||||
/// - destination: The key of the new sorted set from the result.
|
||||
/// - sources: The list of sorted set keys to treat as the source of the union.
|
||||
/// - to: The key to store the union sorted set at.
|
||||
/// - weights: The multiplying factor to apply to the corresponding `sources` key based on index of the two parameters.
|
||||
/// - aggregateMethod: The method of aggregating the values of the union. Supported values are "SUM", "MIN", and "MAX".
|
||||
/// - Returns: The number of members in the new sorted set.
|
||||
/// - Returns: The number of elements in the new sorted set.
|
||||
@inlinable
|
||||
public func zunionstore(
|
||||
_ sources: [String],
|
||||
to destination: String,
|
||||
as destination: String,
|
||||
sources: [String],
|
||||
weights: [Int]? = nil,
|
||||
aggregateMethod aggregate: String? = nil) -> EventLoopFuture<Int>
|
||||
{
|
||||
aggregateMethod aggregate: String? = nil
|
||||
) -> EventLoopFuture<Int> {
|
||||
return _zopstore(command: "ZUNIONSTORE", sources, destination, weights, aggregate)
|
||||
}
|
||||
|
||||
/// Computes a new sorted set as an intersection between all provided source sorted sets and stores the result at the key desired.
|
||||
/// Calculates the intersection of two or more sorted sets and stores the result.
|
||||
/// - Note: This operation overwrites any value stored at the destination key.
|
||||
///
|
||||
/// See [https://redis.io/commands/zinterstore](https://redis.io/commands/zinterstore)
|
||||
/// - Parameters:
|
||||
/// - destination: The key of the new sorted set from the result.
|
||||
/// - sources: The list of sorted set keys to treat as the source of the intersection.
|
||||
/// - to: The key to store the intersected sorted set at.
|
||||
/// - weights: The multiplying factor to apply to the corresponding `sources` key based on index of the two parameters.
|
||||
/// - aggregateMethod: The method of aggregating the values of the intersection. Supported values are "SUM", "MIN", and "MAX".
|
||||
/// - Returns: The number of members in the new sorted set.
|
||||
/// - Returns: The number of elements in the new sorted set.
|
||||
@inlinable
|
||||
public func zinterstore(
|
||||
_ sources: [String],
|
||||
to destination: String,
|
||||
as destination: String,
|
||||
sources: [String],
|
||||
weights: [Int]? = nil,
|
||||
aggregateMethod aggregate: String? = nil) -> EventLoopFuture<Int>
|
||||
{
|
||||
aggregateMethod aggregate: String? = nil
|
||||
) -> EventLoopFuture<Int> {
|
||||
return _zopstore(command: "ZINTERSTORE", sources, destination, weights, aggregate)
|
||||
}
|
||||
|
||||
@@ -375,46 +378,54 @@ extension RedisCommandExecutor {
|
||||
// MARK: Range
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns the specified range of elements in a sorted set.
|
||||
/// Gets the specified range of elements in a sorted set.
|
||||
/// - Note: This treats the ordered set as ordered from low to high.
|
||||
/// For the inverse, see `zrevrange(of:startIndex:endIndex:withScores:)`.
|
||||
///
|
||||
/// For the inverse, see `zrevrange(within:from:withScores:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrange](https://redis.io/commands/zrange)
|
||||
/// - Parameters:
|
||||
/// - withinIndices: The start and stop 0-based indices of the range of elements to include.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the items AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - range: The start and stop 0-based indices of the range of elements to include.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the elements AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided, and optionally their scores.
|
||||
@inlinable
|
||||
public func zrange(
|
||||
withinIndices range: (start: Int, stop: Int),
|
||||
within range: (start: Int, stop: Int),
|
||||
from key: String,
|
||||
withScores: Bool = false) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
withScores: Bool = false
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrange(command: "ZRANGE", key, range.start, range.stop, withScores)
|
||||
}
|
||||
|
||||
/// Returns the specified range of elements in a sorted set.
|
||||
/// Gets the specified range of elements in a sorted set.
|
||||
/// - Note: This treats the ordered set as ordered from high to low.
|
||||
/// For the inverse, see `zrange(of:startIndex:endIndex:withScores:)`.
|
||||
///
|
||||
/// For the inverse, see `zrange(within:from:withScores:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrevrange](https://redis.io/commands/zrevrange)
|
||||
/// - Parameters:
|
||||
/// - withinIndices: The start and stop 0-based indices of the range of elements to include.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the items AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - range: The start and stop 0-based indices of the range of elements to include.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the elements AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided, and optionally their scores.
|
||||
@inlinable
|
||||
public func zrevrange(
|
||||
withinIndices range: (start: Int, stop: Int),
|
||||
within range: (start: Int, stop: Int),
|
||||
from key: String,
|
||||
withScores: Bool = false) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
withScores: Bool = false
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrange(command: "ZREVRANGE", key, range.start, range.stop, withScores)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func _zrange(command: String, _ key: String, _ start: Int, _ stop: Int, _ withScores: Bool) -> EventLoopFuture<[RESPValue]> {
|
||||
func _zrange(
|
||||
command: String,
|
||||
_ key: String,
|
||||
_ start: Int,
|
||||
_ stop: Int,
|
||||
_ withScores: Bool
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
var args: [RESPValueConvertible] = [key, start, stop]
|
||||
|
||||
if withScores { args.append("WITHSCORES") }
|
||||
@@ -427,50 +438,58 @@ extension RedisCommandExecutor {
|
||||
// MARK: Range by Score
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns elements from a sorted set whose score fits within the range specified.
|
||||
/// Gets elements from a sorted set whose score fits within the range specified.
|
||||
/// - Note: This treats the ordered set as ordered from low to high.
|
||||
/// For the inverse, see `zrevrangebyscore(of:within:withScores:limitBy:)`.
|
||||
///
|
||||
/// For the inverse, see `zrevrangebyscore(within:from:withScores:limitBy:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrangebyscore](https://redis.io/commands/zrangebyscore)
|
||||
/// - Parameters:
|
||||
/// - within: The range of min and max scores to filter elements by.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the items AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - limitBy: The optional offset and count of items to query.
|
||||
/// - range: The range of min and max scores to filter elements by.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the elements AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - limit: The optional offset and count of elements to query.
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided, and optionally their scores.
|
||||
@inlinable
|
||||
public func zrangebyscore(
|
||||
within range: (min: String, max: String),
|
||||
from key: String,
|
||||
withScores: Bool = false,
|
||||
limitBy limit: (offset: Int, count: Int)? = nil) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
limitBy limit: (offset: Int, count: Int)? = nil
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrangebyscore(command: "ZRANGEBYSCORE", key, range, withScores, limit)
|
||||
}
|
||||
|
||||
/// Returns elements from a sorted set whose score fits within the range specified.
|
||||
/// Gets elements from a sorted set whose score fits within the range specified.
|
||||
/// - Note: This treats the ordered set as ordered from high to low.
|
||||
/// For the inverse, see `zrangebyscore(of:within:withScores:limitBy:)`.
|
||||
///
|
||||
/// For the inverse, see `zrangebyscore(within:from:withScores:limitBy:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrevrangebyscore](https://redis.io/commands/zrevrangebyscore)
|
||||
/// - Parameters:
|
||||
/// - within: The range of min and max scores to filter elements by.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the items AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - limitBy: The optional offset and count of items to query.
|
||||
/// - range: The range of min and max scores to filter elements by.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - withScores: Should the list contain the elements AND their scores? [Item_1, Score_1, Item_2, ...]
|
||||
/// - limit: The optional offset and count of elements to query.
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided, and optionally their scores.
|
||||
@inlinable
|
||||
public func zrevrangebyscore(
|
||||
within range: (min: String, max: String),
|
||||
from key: String,
|
||||
withScores: Bool = false,
|
||||
limitBy limit: (offset: Int, count: Int)? = nil) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
limitBy limit: (offset: Int, count: Int)? = nil
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrangebyscore(command: "ZREVRANGEBYSCORE", key, (range.max, range.min), withScores, limit)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func _zrangebyscore(command: String, _ key: String, _ range: (min: String, max: String), _ withScores: Bool, _ limit: (offset: Int, count: Int)?) -> EventLoopFuture<[RESPValue]> {
|
||||
func _zrangebyscore(
|
||||
command: String,
|
||||
_ key: String,
|
||||
_ range: (min: String, max: String),
|
||||
_ withScores: Bool,
|
||||
_ limit: (offset: Int, count: Int)?
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
var args: [RESPValueConvertible] = [key, range.min, range.max]
|
||||
|
||||
if withScores { args.append("WITHSCORES") }
|
||||
@@ -488,48 +507,55 @@ extension RedisCommandExecutor {
|
||||
// MARK: Range by Lexiographical
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Returns elements from a sorted set whose lexiographical values are between the range specified.
|
||||
/// Gets elements from a sorted set whose lexiographical values are between the range specified.
|
||||
/// - Important: This assumes all elements in the sorted set have the same score. If not, the returned elements are unspecified.
|
||||
/// - Note: This treats the ordered set as ordered from low to high.
|
||||
/// For the inverse, see `zrevrangebylex(of:within:limitBy:)`.
|
||||
///
|
||||
/// For the inverse, see `zrevrangebylex(within:from:limitBy:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrangebylex](https://redis.io/commands/zrangebylex)
|
||||
/// - Parameters:
|
||||
/// - within: The value range to filter elements by.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - limitBy: The optional offset and count of items to query.
|
||||
/// - range: The value range to filter elements by.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - limit: The optional offset and count of elements to query.
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided.
|
||||
@inlinable
|
||||
public func zrangebylex(
|
||||
within range: (min: String, max: String),
|
||||
from key: String,
|
||||
limitBy limit: (offset: Int, count: Int)? = nil) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
limitBy limit: (offset: Int, count: Int)? = nil
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrangebylex(command: "ZRANGEBYLEX", key, range, limit)
|
||||
}
|
||||
|
||||
/// Returns elements from a sorted set whose lexiographical values are between the range specified.
|
||||
/// Gets elements from a sorted set whose lexiographical values are between the range specified.
|
||||
/// - Important: This assumes all elements in the sorted set have the same score. If not, the returned elements are unspecified.
|
||||
/// - Note: This treats the ordered set as ordered from high to low.
|
||||
/// For the inverse, see `zrangebylex(of:within:limitBy:)`.
|
||||
///
|
||||
/// For the inverse, see `zrangebylex(within:from:limitBy:)`.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrevrangebylex](https://redis.io/commands/zrevrangebylex)
|
||||
/// - Parameters:
|
||||
/// - within: The value range to filter elements by.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - limitBy: The optional offset and count of items to query.
|
||||
/// - range: The value range to filter elements by.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - limit: The optional offset and count of elements to query.
|
||||
/// - Returns: A list of elements from the sorted set that were within the range provided.
|
||||
@inlinable
|
||||
public func zrevrangebylex(
|
||||
within range: (min: String, max: String),
|
||||
from key: String,
|
||||
limitBy limit: (offset: Int, count: Int)? = nil) -> EventLoopFuture<[RESPValue]>
|
||||
{
|
||||
limitBy limit: (offset: Int, count: Int)? = nil
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
return _zrangebylex(command: "ZREVRANGEBYLEX", key, (range.max, range.min), limit)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func _zrangebylex(command: String, _ key: String, _ range: (min: String, max: String), _ limit: (offset: Int, count: Int)?) -> EventLoopFuture<[RESPValue]> {
|
||||
func _zrangebylex(
|
||||
command: String,
|
||||
_ key: String,
|
||||
_ range: (min: String, max: String),
|
||||
_ limit: (offset: Int, count: Int)?
|
||||
) -> EventLoopFuture<[RESPValue]> {
|
||||
var args: [RESPValueConvertible] = [key, range.min, range.max]
|
||||
|
||||
if let l = limit {
|
||||
@@ -545,18 +571,18 @@ extension RedisCommandExecutor {
|
||||
// MARK: Remove
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Removes the specified items from a sorted set.
|
||||
/// Removes the specified elements from a sorted set.
|
||||
///
|
||||
/// See [https://redis.io/commands/zrem](https://redis.io/commands/zrem)
|
||||
/// - Parameters:
|
||||
/// - items: The values to remove from the sorted set.
|
||||
/// - from: The key of the sorted set.
|
||||
/// - Returns: The number of items removed from the set.
|
||||
/// - elements: The values to remove from the sorted set.
|
||||
/// - key: The key of the sorted set.
|
||||
/// - Returns: The number of elements removed from the set.
|
||||
@inlinable
|
||||
public func zrem(_ items: [RESPValueConvertible], from key: String) -> EventLoopFuture<Int> {
|
||||
assert(items.count > 0, "At least 1 item should be provided.")
|
||||
public func zrem(_ elements: [RESPValueConvertible], from key: String) -> EventLoopFuture<Int> {
|
||||
guard elements.count > 0 else { return self.eventLoop.makeSucceededFuture(0) }
|
||||
|
||||
return send(command: "ZREM", with: [key] + items)
|
||||
return send(command: "ZREM", with: [key] + elements)
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
@@ -565,11 +591,14 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zremrangebylex](https://redis.io/commands/zremrangebylex)
|
||||
/// - Parameters:
|
||||
/// - within: The value range to filter for elements to remove.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - range: The value range to filter for elements to remove.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - Returns: The number of elements removed from the sorted set.
|
||||
@inlinable
|
||||
public func zremrangebylex(within range: (min: String, max: String), from key: String) -> EventLoopFuture<Int> {
|
||||
public func zremrangebylex(
|
||||
within range: (min: String, max: String),
|
||||
from key: String
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZREMRANGEBYLEX", with: [key, range.min, range.max])
|
||||
.mapFromRESP()
|
||||
}
|
||||
@@ -578,13 +607,15 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zremrangebyrank](https://redis.io/commands/zremrangebyrank)
|
||||
/// - Parameters:
|
||||
/// - startingFrom: The starting index of the range.
|
||||
/// - endingAt: The ending index of the range.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - range: The index range of elements to remove.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - Returns: The number of elements removed from the sorted set.
|
||||
@inlinable
|
||||
public func zremrangebyrank(startingFrom start: Int, endingAt stop: Int, from key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZREMRANGEBYRANK", with: [key, start, stop])
|
||||
public func zremrangebyrank(
|
||||
within range: (start: Int, stop: Int),
|
||||
from key: String
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZREMRANGEBYRANK", with: [key, range.start, range.stop])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
@@ -592,11 +623,14 @@ extension RedisCommandExecutor {
|
||||
///
|
||||
/// See [https://redis.io/commands/zremrangebyscore](https://redis.io/commands/zremrangebyscore)
|
||||
/// - Parameters:
|
||||
/// - within: The score range to filter for elements to remove.
|
||||
/// - from: The key of the sorted set to search.
|
||||
/// - range: The score range to filter for elements to remove.
|
||||
/// - key: The key of the sorted set to search.
|
||||
/// - Returns: The number of elements removed from the sorted set.
|
||||
@inlinable
|
||||
public func zremrangebyscore(within range: (min: String, max: String), from key: String) -> EventLoopFuture<Int> {
|
||||
public func zremrangebyscore(
|
||||
within range: (min: String, max: String),
|
||||
from key: String
|
||||
) -> EventLoopFuture<Int> {
|
||||
return send(command: "ZREMRANGEBYSCORE", with: [key, range.min, range.max])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import NIO
|
||||
|
||||
// MARK: Get
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Get the value of a key.
|
||||
/// - Note: This operation only works with string values.
|
||||
/// The `EventLoopFuture` will fail with a `RedisError` if the value is not a string, such as a Set.
|
||||
///
|
||||
/// [https://redis.io/commands/get](https://redis.io/commands/get)
|
||||
/// - Parameter key: The key to fetch the value from.
|
||||
/// - Returns: The string value stored at the key provided, otherwise `nil` if the key does not exist.
|
||||
@inlinable
|
||||
public func get(_ key: String) -> EventLoopFuture<String?> {
|
||||
return send(command: "GET", with: [key])
|
||||
.map { return $0.string }
|
||||
}
|
||||
|
||||
/// Gets the values of all specified keys, using `.null` to represent non-existant values.
|
||||
///
|
||||
/// See [https://redis.io/commands/mget](https://redis.io/commands/mget)
|
||||
/// - Parameter keys: The list of keys to fetch the values from.
|
||||
/// - Returns: The values stored at the keys provided, matching the same order.
|
||||
@inlinable
|
||||
public func mget(_ keys: [String]) -> EventLoopFuture<[RESPValue]> {
|
||||
guard keys.count > 0 else { return self.eventLoop.makeSucceededFuture([]) }
|
||||
|
||||
return send(command: "MGET", with: keys)
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Set
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Sets the value stored in the key provided, overwriting the previous value.
|
||||
///
|
||||
/// Any previous expiration set on the key is discarded if the SET operation was successful.
|
||||
///
|
||||
/// - Important: Regardless of the type of value stored at the key, it will be overwritten to a string value.
|
||||
///
|
||||
/// [https://redis.io/commands/set](https://redis.io/commands/set)
|
||||
/// - Parameters:
|
||||
/// - key: The key to use to uniquely identify this value.
|
||||
/// - value: The value to set the key to.
|
||||
/// - Returns: An `EventLoopFuture` that resolves if the operation was successful.
|
||||
@inlinable
|
||||
public func set(_ key: String, to value: RESPValueConvertible) -> EventLoopFuture<Void> {
|
||||
return send(command: "SET", with: [key, value])
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Sets each key to their 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)
|
||||
/// - Parameter operations: The key-value list of SET operations to execute.
|
||||
/// - Returns: An `EventLoopFuture` that resolves if the operation was successful.
|
||||
@inlinable
|
||||
public func mset(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Void> {
|
||||
return _mset(command: "MSET", operations)
|
||||
.map { _ in () }
|
||||
}
|
||||
|
||||
/// Sets each key to their respective new value, only if all keys do not currently exist.
|
||||
/// - Note: Use `mset(_:)` if you don't care about overwriting values.
|
||||
///
|
||||
/// See [https://redis.io/commands/msetnx](https://redis.io/commands/msetnx)
|
||||
/// - Parameter operations: The key-value list of SET operations to execute.
|
||||
/// - Returns: `true` if the operation successfully completed.
|
||||
@inlinable
|
||||
public func msetnx(_ operations: [String: RESPValueConvertible]) -> EventLoopFuture<Bool> {
|
||||
return _mset(command: "MSETNX", operations)
|
||||
.mapFromRESP(to: Int.self)
|
||||
.map { return $0 == 1 }
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func _mset(
|
||||
command: String,
|
||||
_ operations: [String: RESPValueConvertible]
|
||||
) -> EventLoopFuture<RESPValue> {
|
||||
assert(operations.count > 0, "At least 1 key-value pair should be provided.")
|
||||
|
||||
let args: [RESPValueConvertible] = operations.reduce(into: [], { (result, element) in
|
||||
result.append(element.key)
|
||||
result.append(element.value)
|
||||
})
|
||||
|
||||
return send(command: command, with: args)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Increment
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Increments the stored value by 1.
|
||||
///
|
||||
/// See [https://redis.io/commands/incr](https://redis.io/commands/incr)
|
||||
/// - Parameter key: The key whose value should be incremented.
|
||||
/// - Returns: The new value after the operation.
|
||||
@inlinable
|
||||
public func increment(_ key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "INCR", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Increments the stored value by the amount desired .
|
||||
///
|
||||
/// See [https://redis.io/commands/incrby](https://redis.io/commands/incrby)
|
||||
/// - Parameters:
|
||||
/// - key: The key whose value should be incremented.
|
||||
/// - count: The amount that this value should be incremented, supporting both positive and negative values.
|
||||
/// - Returns: The new value after the operation.
|
||||
@inlinable
|
||||
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.
|
||||
///
|
||||
/// See [https://redis.io/commands/incrbyfloat](https://redis.io/commands/incrbyfloat)
|
||||
/// - Parameters:
|
||||
/// - key: The key whose value should be incremented.
|
||||
/// - count: The amount that this value should be incremented, supporting both positive and negative values.
|
||||
/// - Returns: The new value after the operation.
|
||||
@inlinable
|
||||
public func increment<T: BinaryFloatingPoint>(_ key: String, by count: T) -> EventLoopFuture<T>
|
||||
where T: RESPValueConvertible
|
||||
{
|
||||
return send(command: "INCRBYFLOAT", with: [key, count])
|
||||
.mapFromRESP()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Decrement
|
||||
|
||||
extension RedisCommandExecutor {
|
||||
/// Decrements the stored value by 1.
|
||||
///
|
||||
/// See [https://redis.io/commands/decr](https://redis.io/commands/decr)
|
||||
/// - Parameter key: The key whose value should be decremented.
|
||||
/// - Returns: The new value after the operation.
|
||||
@inlinable
|
||||
public func decrement(_ key: String) -> EventLoopFuture<Int> {
|
||||
return send(command: "DECR", with: [key])
|
||||
.mapFromRESP()
|
||||
}
|
||||
|
||||
/// Decrements the stored valye by the amount desired.
|
||||
///
|
||||
/// See [https://redis.io/commands/decrby](https://redis.io/commands/decrby)
|
||||
/// - Parameters:
|
||||
/// - key: The key whose value should be decremented.
|
||||
/// - count: The amount that this value should be decremented, supporting both positive and negative values.
|
||||
/// - 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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user