@@ -46,6 +46,22 @@ extension RedisClient {
46
46
47
47
// MARK: General
48
48
49
+ /// The supported options for the `zadd` command with Redis SortedSet types.
50
+ /// - Important: Per Redis documentation, `.onlyUpdateExistingElements` and `.onlyAddNewElements` are mutually exclusive!
51
+ /// - Note: `INCR` is not supported by this library in `zadd`. Use the `zincrby(:element:in:)` method instead.
52
+ /// See [https://redis.io/commands/zadd#zadd-options-redis-302-or-greater](https://redis.io/commands/zadd#zadd-options-redis-302-or-greater)
53
+ public enum RedisSortedSetAddOption : String {
54
+ /// When adding elements, any that do not already exist in the SortedSet will be ignored and the score of the existing element will be updated.
55
+ case onlyUpdateExistingElements = " XX "
56
+ /// When adding elements, any that already exist in the SortedSet will be ignored and the score of the existing element will not be updated.
57
+ case onlyAddNewElements = " NX "
58
+ /// `zadd` normally returns the number of new elements added to the set,
59
+ /// but this option will instead have the command return the number of elements changed.
60
+ ///
61
+ /// "Changed" in this context are new elements added, and elements that had their score updated.
62
+ case returnChangedCount = " CH "
63
+ }
64
+
49
65
extension RedisClient {
50
66
/// Adds elements to a sorted set, assigning their score to the values provided.
51
67
///
@@ -59,28 +75,21 @@ extension RedisClient {
59
75
public func zadd< Value: RESPValueConvertible > (
60
76
_ elements: [ ( element: Value , score: Double ) ] ,
61
77
to key: String ,
62
- options: Set < String > = [ ]
78
+ options: Set < RedisSortedSetAddOption > = [ ]
63
79
) -> EventLoopFuture < Int > {
64
- guard !options. contains ( " INCR " ) else {
65
- return self . eventLoop. makeFailedFuture ( RedisNIOError . unsupportedOperation (
66
- method: #function,
67
- message: " INCR option is unsupported. Use zincrby(_:element:in:) instead. "
68
- ) )
69
- }
70
-
71
80
assert ( options. count <= 2 , " Invalid number of options provided. " )
72
- assert ( options. allSatisfy ( [ " XX " , " NX " , " CH " ] . contains) , " Unsupported option provided! " )
73
81
assert (
74
- !( options. contains ( " XX " ) && options. contains ( " NX " ) ) ,
75
- " XX and NX options are mutually exclusive."
82
+ !( options. contains ( . onlyAddNewElements ) && options. contains ( . onlyUpdateExistingElements ) ) ,
83
+ " .onlyAddNewElements and .onlyUpdateExistingElements options are mutually exclusive."
76
84
)
77
85
78
86
var args : [ RESPValue ] = [ . init( bulk: key) ]
79
- args. append ( convertingContentsOf: options)
80
-
81
- for (element, score) in elements {
82
- args. append ( . init( bulk: score. description) )
83
- args. append ( element. convertedToRESPValue ( ) )
87
+ args. add ( contentsOf: options) { ( array, option) in
88
+ array. append ( . init( bulk: option. rawValue) )
89
+ }
90
+ args. add ( contentsOf: elements, overestimatedCountBeingAdded: elements. count * 2 ) { ( array, next) in
91
+ array. append ( . init( bulk: next. score. description) )
92
+ array. append ( next. element. convertedToRESPValue ( ) )
84
93
}
85
94
86
95
return send ( command: " ZADD " , with: args)
@@ -99,7 +108,7 @@ extension RedisClient {
99
108
public func zadd< Value: RESPValueConvertible > (
100
109
_ element: ( element: Value , score: Double ) ,
101
110
to key: String ,
102
- options: Set < String > = [ ]
111
+ options: Set < RedisSortedSetAddOption > = [ ]
103
112
) -> EventLoopFuture < Bool > {
104
113
return zadd ( [ element] , to: key, options: options)
105
114
. map { return $0 == 1 }
@@ -490,6 +499,20 @@ extension RedisClient {
490
499
491
500
// MARK: Intersect and Union
492
501
502
+ /// The supported methods for aggregating results from the `zunionstore` or `zinterstore` commands in Redis.
503
+ ///
504
+ /// For more information on these values, see
505
+ /// [https://redis.io/commands/zunionstore](https://redis.io/commands/zunionstore)
506
+ /// [https://redis.io/commands/zinterstore](https://redis.io/commands/zinterstore)
507
+ public enum RedisSortedSetAggregateMethod : String {
508
+ /// Add the score of all matching elements in the source SortedSets.
509
+ case sum = " SUM "
510
+ /// Use the minimum score of the matching elements in the source SortedSets.
511
+ case min = " MIN "
512
+ /// Use the maximum score of the matching elements in the source SortedSets.
513
+ case max = " MAX "
514
+ }
515
+
493
516
extension RedisClient {
494
517
/// Calculates the union of two or more sorted sets and stores the result.
495
518
/// - Note: This operation overwrites any value stored at the destination key.
@@ -499,14 +522,14 @@ extension RedisClient {
499
522
/// - destination: The key of the new sorted set from the result.
500
523
/// - sources: The list of sorted set keys to treat as the source of the union.
501
524
/// - weights: The multiplying factor to apply to the corresponding `sources` key based on index of the two parameters.
502
- /// - aggregateMethod: The method of aggregating the values of the union. Supported values are "SUM", "MIN", and "MAX" .
525
+ /// - aggregateMethod: The method of aggregating the values of the union. If one isn't specified, Redis will default to `.sum` .
503
526
/// - Returns: The number of elements in the new sorted set.
504
527
@inlinable
505
528
public func zunionstore(
506
529
as destination: String ,
507
530
sources: [ String ] ,
508
531
weights: [ Int ] ? = nil ,
509
- aggregateMethod aggregate: String ? = nil
532
+ aggregateMethod aggregate: RedisSortedSetAggregateMethod ? = nil
510
533
) -> EventLoopFuture < Int > {
511
534
return _zopstore ( command: " ZUNIONSTORE " , sources, destination, weights, aggregate)
512
535
}
@@ -519,14 +542,14 @@ extension RedisClient {
519
542
/// - destination: The key of the new sorted set from the result.
520
543
/// - sources: The list of sorted set keys to treat as the source of the intersection.
521
544
/// - weights: The multiplying factor to apply to the corresponding `sources` key based on index of the two parameters.
522
- /// - aggregateMethod: The method of aggregating the values of the intersection. Supported values are "SUM", "MIN", and "MAX" .
545
+ /// - aggregateMethod: The method of aggregating the values of the intersection. If one isn't specified, Redis will default to `.sum` .
523
546
/// - Returns: The number of elements in the new sorted set.
524
547
@inlinable
525
548
public func zinterstore(
526
549
as destination: String ,
527
550
sources: [ String ] ,
528
551
weights: [ Int ] ? = nil ,
529
- aggregateMethod aggregate: String ? = nil
552
+ aggregateMethod aggregate: RedisSortedSetAggregateMethod ? = nil
530
553
) -> EventLoopFuture < Int > {
531
554
return _zopstore ( command: " ZINTERSTORE " , sources, destination, weights, aggregate)
532
555
}
@@ -537,8 +560,8 @@ extension RedisClient {
537
560
_ sources: [ String ] ,
538
561
_ destination: String ,
539
562
_ weights: [ Int ] ? ,
540
- _ aggregate: String ? ) -> EventLoopFuture < Int >
541
- {
563
+ _ aggregate: RedisSortedSetAggregateMethod ?
564
+ ) -> EventLoopFuture < Int > {
542
565
assert ( sources. count > 0 , " At least 1 source key should be provided. " )
543
566
544
567
var args : [ RESPValue ] = [
@@ -556,10 +579,8 @@ extension RedisClient {
556
579
}
557
580
558
581
if let a = aggregate {
559
- assert ( a == " SUM " || a == " MIN " || a == " MAX " , " Aggregate method provided is unsupported. " )
560
-
561
582
args. append ( . init( bulk: " AGGREGATE " ) )
562
- args. append ( . init( bulk: a) )
583
+ args. append ( . init( bulk: a. rawValue ) )
563
584
}
564
585
565
586
return send ( command: command, with: args)
0 commit comments