diff --git a/src/commands.def b/src/commands.def index 07e1dccc6..9f1f88b0d 100644 --- a/src/commands.def +++ b/src/commands.def @@ -9165,7 +9165,9 @@ struct COMMAND_ARG ZINCRBY_Args[] = { #ifndef SKIP_CMD_HISTORY_TABLE /* ZINTER history */ -#define ZINTER_History NULL +commandHistory ZINTER_History[] = { +{"8.8.0","Added `COUNT` aggregate option."}, +}; #endif #ifndef SKIP_CMD_TIPS_TABLE @@ -9185,6 +9187,7 @@ struct COMMAND_ARG ZINTER_aggregate_Subargs[] = { {MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)}, +{MAKE_ARG("count",ARG_TYPE_PURE_TOKEN,-1,"COUNT",NULL,"8.8.0",CMD_ARG_NONE,0,NULL)}, }; /* ZINTER argument table */ @@ -9192,7 +9195,7 @@ struct COMMAND_ARG ZINTER_Args[] = { {MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)}, {MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)}, -{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZINTER_aggregate_Subargs}, +{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=ZINTER_aggregate_Subargs}, {MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)}, }; @@ -9226,7 +9229,9 @@ struct COMMAND_ARG ZINTERCARD_Args[] = { #ifndef SKIP_CMD_HISTORY_TABLE /* ZINTERSTORE history */ -#define ZINTERSTORE_History NULL +commandHistory ZINTERSTORE_History[] = { +{"8.8.0","Added `COUNT` aggregate option."}, +}; #endif #ifndef SKIP_CMD_TIPS_TABLE @@ -9246,6 +9251,7 @@ struct COMMAND_ARG ZINTERSTORE_aggregate_Subargs[] = { {MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)}, +{MAKE_ARG("count",ARG_TYPE_PURE_TOKEN,-1,"COUNT",NULL,"8.8.0",CMD_ARG_NONE,0,NULL)}, }; /* ZINTERSTORE argument table */ @@ -9254,7 +9260,7 @@ struct COMMAND_ARG ZINTERSTORE_Args[] = { {MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)}, {MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)}, -{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZINTERSTORE_aggregate_Subargs}, +{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=ZINTERSTORE_aggregate_Subargs}, }; /********** ZLEXCOUNT ********************/ @@ -9894,7 +9900,9 @@ struct COMMAND_ARG ZSCORE_Args[] = { #ifndef SKIP_CMD_HISTORY_TABLE /* ZUNION history */ -#define ZUNION_History NULL +commandHistory ZUNION_History[] = { +{"8.8.0","Added `COUNT` aggregate option."}, +}; #endif #ifndef SKIP_CMD_TIPS_TABLE @@ -9914,6 +9922,7 @@ struct COMMAND_ARG ZUNION_aggregate_Subargs[] = { {MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)}, +{MAKE_ARG("count",ARG_TYPE_PURE_TOKEN,-1,"COUNT",NULL,"8.8.0",CMD_ARG_NONE,0,NULL)}, }; /* ZUNION argument table */ @@ -9921,7 +9930,7 @@ struct COMMAND_ARG ZUNION_Args[] = { {MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)}, {MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)}, -{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZUNION_aggregate_Subargs}, +{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=ZUNION_aggregate_Subargs}, {MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)}, }; @@ -9929,7 +9938,9 @@ struct COMMAND_ARG ZUNION_Args[] = { #ifndef SKIP_CMD_HISTORY_TABLE /* ZUNIONSTORE history */ -#define ZUNIONSTORE_History NULL +commandHistory ZUNIONSTORE_History[] = { +{"8.8.0","Added `COUNT` aggregate option."}, +}; #endif #ifndef SKIP_CMD_TIPS_TABLE @@ -9949,6 +9960,7 @@ struct COMMAND_ARG ZUNIONSTORE_aggregate_Subargs[] = { {MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)}, +{MAKE_ARG("count",ARG_TYPE_PURE_TOKEN,-1,"COUNT",NULL,"8.8.0",CMD_ARG_NONE,0,NULL)}, }; /* ZUNIONSTORE argument table */ @@ -9957,7 +9969,7 @@ struct COMMAND_ARG ZUNIONSTORE_Args[] = { {MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)}, {MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)}, -{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZUNIONSTORE_aggregate_Subargs}, +{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=ZUNIONSTORE_aggregate_Subargs}, }; /********** XACK ********************/ @@ -11988,9 +12000,9 @@ struct COMMAND_STRUCT redisCommandTable[] = { {MAKE_CMD("zdiff","Returns the difference between multiple sorted sets.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFF_History,0,ZDIFF_Tips,0,zdiffCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZDIFF_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZDIFF_Args}, {MAKE_CMD("zdiffstore","Stores the difference of multiple sorted sets in a key.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFFSTORE_History,0,ZDIFFSTORE_Tips,0,zdiffstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZDIFFSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,3),.args=ZDIFFSTORE_Args}, {MAKE_CMD("zincrby","Increments the score of a member in a sorted set.","O(log(N)) where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINCRBY_History,0,ZINCRBY_Tips,0,zincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZINCRBY_Keyspecs,1,NULL,3),.args=ZINCRBY_Args}, -{MAKE_CMD("zinter","Returns the intersect of multiple sorted sets.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTER_History,0,ZINTER_Tips,0,zinterCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZINTER_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZINTER_Args}, +{MAKE_CMD("zinter","Returns the intersect of multiple sorted sets.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTER_History,1,ZINTER_Tips,0,zinterCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZINTER_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZINTER_Args}, {MAKE_CMD("zintercard","Returns the number of members of the intersect of multiple sorted sets.","O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERCARD_History,0,ZINTERCARD_Tips,0,zinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZINTERCARD_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZINTERCARD_Args}, -{MAKE_CMD("zinterstore","Stores the intersect of multiple sorted sets in a key.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERSTORE_History,0,ZINTERSTORE_Tips,0,zinterstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZINTERSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZINTERSTORE_Args}, +{MAKE_CMD("zinterstore","Stores the intersect of multiple sorted sets in a key.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERSTORE_History,1,ZINTERSTORE_Tips,0,zinterstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZINTERSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZINTERSTORE_Args}, {MAKE_CMD("zlexcount","Returns the number of members in a sorted set within a lexicographical range.","O(log(N)) with N being the number of elements in the sorted set.","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZLEXCOUNT_History,0,ZLEXCOUNT_Tips,0,zlexcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZLEXCOUNT_Keyspecs,1,NULL,3),.args=ZLEXCOUNT_Args}, {MAKE_CMD("zmpop","Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMPOP_History,0,ZMPOP_Tips,0,zmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,ZMPOP_Keyspecs,1,zmpopGetKeys,4),.args=ZMPOP_Args}, {MAKE_CMD("zmscore","Returns the score of one or more members in a sorted set.","O(N) where N is the number of members being requested.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMSCORE_History,0,ZMSCORE_Tips,0,zmscoreCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZMSCORE_Keyspecs,1,NULL,2),.args=ZMSCORE_Args}, @@ -12012,8 +12024,8 @@ struct COMMAND_STRUCT redisCommandTable[] = { {MAKE_CMD("zrevrank","Returns the index of a member in a sorted set ordered by descending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANK_History,1,ZREVRANK_Tips,0,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZREVRANK_Keyspecs,1,NULL,3),.args=ZREVRANK_Args}, {MAKE_CMD("zscan","Iterates over members and scores of a sorted set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCAN_History,0,ZSCAN_Tips,1,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZSCAN_Keyspecs,1,NULL,4),.args=ZSCAN_Args}, {MAKE_CMD("zscore","Returns the score of a member in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCORE_History,0,ZSCORE_Tips,0,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZSCORE_Keyspecs,1,NULL,2),.args=ZSCORE_Args}, -{MAKE_CMD("zunion","Returns the union of multiple sorted sets.","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNION_History,0,ZUNION_Tips,0,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZUNION_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZUNION_Args}, -{MAKE_CMD("zunionstore","Stores the union of multiple sorted sets in a key.","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,0,ZUNIONSTORE_Tips,0,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZUNIONSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZUNIONSTORE_Args}, +{MAKE_CMD("zunion","Returns the union of multiple sorted sets.","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNION_History,1,ZUNION_Tips,0,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZUNION_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZUNION_Args}, +{MAKE_CMD("zunionstore","Stores the union of multiple sorted sets in a key.","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,1,ZUNIONSTORE_Tips,0,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZUNIONSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZUNIONSTORE_Args}, /* stream */ {MAKE_CMD("xack","Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.","O(1) for each message ID processed.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XACK_History,0,XACK_Tips,0,xackCommand,-4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XACK_Keyspecs,1,NULL,3),.args=XACK_Args}, {MAKE_CMD("xackdel","Acknowledges and deletes one or multiple messages for a stream consumer group.","O(1) for each message ID processed.","8.2.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XACKDEL_History,0,XACKDEL_Tips,0,xackdelCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XACKDEL_Keyspecs,1,NULL,4),.args=XACKDEL_Args}, diff --git a/src/commands/zinter.json b/src/commands/zinter.json index 4828e21d6..1b192cdb2 100644 --- a/src/commands/zinter.json +++ b/src/commands/zinter.json @@ -7,6 +7,12 @@ "arity": -3, "function": "zinterCommand", "get_keys_function": "zunionInterDiffGetKeys", + "history": [ + [ + "8.8.0", + "Added `COUNT` aggregate option." + ] + ], "command_flags": [ "READONLY" ], @@ -101,6 +107,12 @@ "name": "max", "type": "pure-token", "token": "MAX" + }, + { + "name": "count", + "type": "pure-token", + "token": "COUNT", + "since": "8.8.0" } ] }, diff --git a/src/commands/zinterstore.json b/src/commands/zinterstore.json index 5bd940c65..0404bf749 100644 --- a/src/commands/zinterstore.json +++ b/src/commands/zinterstore.json @@ -7,6 +7,12 @@ "arity": -4, "function": "zinterstoreCommand", "get_keys_function": "zunionInterDiffStoreGetKeys", + "history": [ + [ + "8.8.0", + "Added `COUNT` aggregate option." + ] + ], "command_flags": [ "WRITE", "DENYOOM" @@ -100,6 +106,12 @@ "name": "max", "type": "pure-token", "token": "MAX" + }, + { + "name": "count", + "type": "pure-token", + "token": "COUNT", + "since": "8.8.0" } ] } diff --git a/src/commands/zunion.json b/src/commands/zunion.json index 1ce3dc5ee..366e0e8f9 100644 --- a/src/commands/zunion.json +++ b/src/commands/zunion.json @@ -7,6 +7,12 @@ "arity": -3, "function": "zunionCommand", "get_keys_function": "zunionInterDiffGetKeys", + "history": [ + [ + "8.8.0", + "Added `COUNT` aggregate option." + ] + ], "command_flags": [ "READONLY" ], @@ -101,6 +107,12 @@ "name": "max", "type": "pure-token", "token": "MAX" + }, + { + "name": "count", + "type": "pure-token", + "token": "COUNT", + "since": "8.8.0" } ] }, diff --git a/src/commands/zunionstore.json b/src/commands/zunionstore.json index 65e7b5469..fd208a6c0 100644 --- a/src/commands/zunionstore.json +++ b/src/commands/zunionstore.json @@ -7,6 +7,12 @@ "arity": -4, "function": "zunionstoreCommand", "get_keys_function": "zunionInterDiffStoreGetKeys", + "history": [ + [ + "8.8.0", + "Added `COUNT` aggregate option." + ] + ], "command_flags": [ "WRITE", "DENYOOM" @@ -99,6 +105,12 @@ "name": "max", "type": "pure-token", "token": "MAX" + }, + { + "name": "count", + "type": "pure-token", + "token": "COUNT", + "since": "8.8.0" } ] } diff --git a/src/t_zset.c b/src/t_zset.c index 346bcd38c..b4cd47c23 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -2653,6 +2653,15 @@ static int zuiCompareByRevCardinality(const void *s1, const void *s2) { #define REDIS_AGGR_SUM 1 #define REDIS_AGGR_MIN 2 #define REDIS_AGGR_MAX 3 +#define REDIS_AGGR_COUNT 4 + +/* Return the weighted contribution of a single sorted set member. + * For COUNT aggregation the actual score is irrelevant — each member + * contributes its set's weight (i.e. "one occurrence worth "). + * For all other aggregation modes the contribution is weight * score. */ +inline static double zuiWeightedScore(double score, double weight, int aggregate) { + return (aggregate == REDIS_AGGR_COUNT) ? weight : weight * score; +} inline static void zunionInterAggregate(double *target, double val, int aggregate) { if (aggregate == REDIS_AGGR_SUM) { @@ -2661,6 +2670,11 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat * is +inf and the other is -inf. When these numbers are added, * we maintain the convention of the result being 0.0. */ if (isnan(*target)) *target = 0.0; + } else if (aggregate == REDIS_AGGR_COUNT) { + *target += val; + /* The val is zuiWeightedScore(…) == weight, which can be +inf/-inf, + * so the NaN guard applies here. */ + if (isnan(*target)) *target = 0.0; } else if (aggregate == REDIS_AGGR_MIN) { *target = val < *target ? val : *target; } else if (aggregate == REDIS_AGGR_MAX) { @@ -2962,6 +2976,8 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in aggregate = REDIS_AGGR_MIN; } else if (!strcasecmp(c->argv[j]->ptr,"max")) { aggregate = REDIS_AGGR_MAX; + } else if (!strcasecmp(c->argv[j]->ptr,"count")) { + aggregate = REDIS_AGGR_COUNT; } else { zfree(src); addReplyErrorObject(c,shared.syntaxerr); @@ -3018,17 +3034,17 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in while (zuiNext(&src[0],&zval)) { double score, value; - score = src[0].weight * zval.score; + score = zuiWeightedScore(zval.score, src[0].weight, aggregate); if (isnan(score)) score = 0; for (j = 1; j < setnum; j++) { /* It is not safe to access the zset we are * iterating, so explicitly check for equal object. */ if (src[j].subject == src[0].subject) { - value = zval.score*src[j].weight; + value = zuiWeightedScore(zval.score, src[j].weight, aggregate); zunionInterAggregate(&score,value,aggregate); } else if (zuiFind(&src[j],&zval,&value)) { - value *= src[j].weight; + value = zuiWeightedScore(value, src[j].weight, aggregate); zunionInterAggregate(&score,value,aggregate); } else { break; @@ -3075,7 +3091,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in zuiInitIterator(&src[i]); while (zuiNext(&src[i],&zval)) { /* Initialize value */ - score = src[i].weight * zval.score; + score = zuiWeightedScore(zval.score, src[i].weight, aggregate); if (isnan(score)) score = 0; /* Search for this element in the dict (which stores node pointers). */ diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl index ad9483b2d..f08ddf70c 100644 --- a/tests/unit/type/zset.tcl +++ b/tests/unit/type/zset.tcl @@ -971,6 +971,26 @@ start_server {tags {"zset"}} { assert_equal {b 2 c 3} [r zinter 2 zseta{t} zsetb{t} aggregate max withscores] } + test "ZUNIONSTORE with AGGREGATE COUNT - $encoding" { + assert_equal 4 [r zunionstore zsetc{t} 2 zseta{t} zsetb{t} aggregate count] + assert_equal {a 1 d 1 b 2 c 2} [r zrange zsetc{t} 0 -1 withscores] + } + + test "ZUNION/ZINTER with AGGREGATE COUNT - $encoding" { + assert_equal {a 1 d 1 b 2 c 2} [r zunion 2 zseta{t} zsetb{t} aggregate count withscores] + assert_equal {b 2 c 2} [r zinter 2 zseta{t} zsetb{t} aggregate count withscores] + } + + test "ZUNIONSTORE with AGGREGATE COUNT and WEIGHTS - $encoding" { + assert_equal 4 [r zunionstore zsetc{t} 2 zseta{t} zsetb{t} weights 2 3 aggregate count] + assert_equal {a 2 d 3 b 5 c 5} [r zrange zsetc{t} 0 -1 withscores] + } + + test "ZUNION/ZINTER with AGGREGATE COUNT and WEIGHTS - $encoding" { + assert_equal {a 2 d 3 b 5 c 5} [r zunion 2 zseta{t} zsetb{t} weights 2 3 aggregate count withscores] + assert_equal {b 5 c 5} [r zinter 2 zseta{t} zsetb{t} weights 2 3 aggregate count withscores] + } + test "ZINTERSTORE basics - $encoding" { assert_equal 2 [r zinterstore zsetc{t} 2 zseta{t} zsetb{t}] assert_equal {b 3 c 5} [r zrange zsetc{t} 0 -1 withscores] @@ -1030,6 +1050,39 @@ start_server {tags {"zset"}} { assert_equal {b 2 c 3} [r zrange zsetc{t} 0 -1 withscores] } + test "ZINTERSTORE with AGGREGATE COUNT - $encoding" { + assert_equal 2 [r zinterstore zsetc{t} 2 zseta{t} zsetb{t} aggregate count] + assert_equal {b 2 c 2} [r zrange zsetc{t} 0 -1 withscores] + } + + test "ZINTERSTORE with AGGREGATE COUNT and WEIGHTS - $encoding" { + assert_equal 2 [r zinterstore zsetc{t} 2 zseta{t} zsetb{t} weights 2 3 aggregate count] + assert_equal {b 5 c 5} [r zrange zsetc{t} 0 -1 withscores] + } + + test "ZUNIONSTORE/ZINTERSTORE with AGGREGATE COUNT - 3 sets - $encoding" { + r del s1{t} s2{t} s3{t} t1{t} + r zadd s1{t} 1 foo 1 bar + r zadd s2{t} 2 foo 2 bar + r zadd s3{t} 3 foo + + assert_equal 1 [r zinterstore t1{t} 3 s1{t} s2{t} s3{t} aggregate count] + assert_equal {foo 3} [r zrange t1{t} 0 -1 withscores] + + assert_equal 2 [r zunionstore t1{t} 3 s1{t} s2{t} s3{t} aggregate count] + assert_equal {bar 2 foo 3} [r zrange t1{t} 0 -1 withscores] + } + + test "ZUNIONSTORE/ZINTERSTORE with AGGREGATE COUNT and WEIGHTS - 3 sets - $encoding" { + assert_equal 1 [r zinterstore t1{t} 3 s1{t} s2{t} s3{t} weights 10 5 3 aggregate count] + assert_equal {foo 18} [r zrange t1{t} 0 -1 withscores] + + assert_equal 2 [r zunionstore t1{t} 3 s1{t} s2{t} s3{t} weights 10 5 3 aggregate count] + assert_equal {bar 15 foo 18} [r zrange t1{t} 0 -1 withscores] + + r del s1{t} s2{t} s3{t} t1{t} + } + foreach cmd {ZUNIONSTORE ZINTERSTORE} { test "$cmd with +inf/-inf scores - $encoding" { r del zsetinf1{t} zsetinf2{t}