@@ -46,6 +46,22 @@ extension RedisClient {
4646
4747// MARK: General
4848
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+
4965extension RedisClient {
5066 /// Adds elements to a sorted set, assigning their score to the values provided.
5167 ///
@@ -59,28 +75,21 @@ extension RedisClient {
5975 public func zadd< Value: RESPValueConvertible > (
6076 _ elements: [ ( element: Value , score: Double ) ] ,
6177 to key: String ,
62- options: Set < String > = [ ]
78+ options: Set < RedisSortedSetAddOption > = [ ]
6379 ) -> 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-
7180 assert ( options. count <= 2 , " Invalid number of options provided. " )
72- assert ( options. allSatisfy ( [ " XX " , " NX " , " CH " ] . contains) , " Unsupported option provided! " )
7381 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."
7684 )
7785
7886 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 ( ) )
8493 }
8594
8695 return send ( command: " ZADD " , with: args)
@@ -99,7 +108,7 @@ extension RedisClient {
99108 public func zadd< Value: RESPValueConvertible > (
100109 _ element: ( element: Value , score: Double ) ,
101110 to key: String ,
102- options: Set < String > = [ ]
111+ options: Set < RedisSortedSetAddOption > = [ ]
103112 ) -> EventLoopFuture < Bool > {
104113 return zadd ( [ element] , to: key, options: options)
105114 . map { return $0 == 1 }
@@ -490,6 +499,20 @@ extension RedisClient {
490499
491500// MARK: Intersect and Union
492501
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+
493516extension RedisClient {
494517 /// Calculates the union of two or more sorted sets and stores the result.
495518 /// - Note: This operation overwrites any value stored at the destination key.
@@ -499,14 +522,14 @@ extension RedisClient {
499522 /// - destination: The key of the new sorted set from the result.
500523 /// - sources: The list of sorted set keys to treat as the source of the union.
501524 /// - 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` .
503526 /// - Returns: The number of elements in the new sorted set.
504527 @inlinable
505528 public func zunionstore(
506529 as destination: String ,
507530 sources: [ String ] ,
508531 weights: [ Int ] ? = nil ,
509- aggregateMethod aggregate: String ? = nil
532+ aggregateMethod aggregate: RedisSortedSetAggregateMethod ? = nil
510533 ) -> EventLoopFuture < Int > {
511534 return _zopstore ( command: " ZUNIONSTORE " , sources, destination, weights, aggregate)
512535 }
@@ -519,14 +542,14 @@ extension RedisClient {
519542 /// - destination: The key of the new sorted set from the result.
520543 /// - sources: The list of sorted set keys to treat as the source of the intersection.
521544 /// - 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` .
523546 /// - Returns: The number of elements in the new sorted set.
524547 @inlinable
525548 public func zinterstore(
526549 as destination: String ,
527550 sources: [ String ] ,
528551 weights: [ Int ] ? = nil ,
529- aggregateMethod aggregate: String ? = nil
552+ aggregateMethod aggregate: RedisSortedSetAggregateMethod ? = nil
530553 ) -> EventLoopFuture < Int > {
531554 return _zopstore ( command: " ZINTERSTORE " , sources, destination, weights, aggregate)
532555 }
@@ -537,8 +560,8 @@ extension RedisClient {
537560 _ sources: [ String ] ,
538561 _ destination: String ,
539562 _ weights: [ Int ] ? ,
540- _ aggregate: String ? ) -> EventLoopFuture < Int >
541- {
563+ _ aggregate: RedisSortedSetAggregateMethod ?
564+ ) -> EventLoopFuture < Int > {
542565 assert ( sources. count > 0 , " At least 1 source key should be provided. " )
543566
544567 var args : [ RESPValue ] = [
@@ -556,10 +579,8 @@ extension RedisClient {
556579 }
557580
558581 if let a = aggregate {
559- assert ( a == " SUM " || a == " MIN " || a == " MAX " , " Aggregate method provided is unsupported. " )
560-
561582 args. append ( . init( bulk: " AGGREGATE " ) )
562- args. append ( . init( bulk: a) )
583+ args. append ( . init( bulk: a. rawValue ) )
563584 }
564585
565586 return send ( command: command, with: args)
0 commit comments