diff options
-rw-r--r-- | php_redis.h | 2 | ||||
-rw-r--r-- | redis.c | 24 | ||||
-rw-r--r-- | redis_cluster.c | 4 | ||||
-rw-r--r-- | redis_commands.c | 121 | ||||
-rw-r--r-- | redis_commands.h | 5 | ||||
-rw-r--r-- | tests/RedisClusterTest.php | 2 | ||||
-rw-r--r-- | tests/RedisTest.php | 34 |
7 files changed, 186 insertions, 6 deletions
diff --git a/php_redis.h b/php_redis.h index 975b8ab5..6b7b3be4 100644 --- a/php_redis.h +++ b/php_redis.h @@ -146,7 +146,9 @@ PHP_METHOD(Redis, zRevRank); PHP_METHOD(Redis, zScore); PHP_METHOD(Redis, zdiff); PHP_METHOD(Redis, zdiffstore); +PHP_METHOD(Redis, zinter); PHP_METHOD(Redis, zinterstore); +PHP_METHOD(Redis, zunion); PHP_METHOD(Redis, zunionstore); PHP_METHOD(Redis, eval); @@ -120,6 +120,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiff, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, options, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_zinterunion, 0, 0, 1) + ZEND_ARG_ARRAY_INFO(0, keys, 0) + ZEND_ARG_ARRAY_INFO(0, weights, 1) + ZEND_ARG_ARRAY_INFO(0, options, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_zdiffstore, 0, 0, 2) ZEND_ARG_INFO(0, destination) ZEND_ARG_ARRAY_INFO(0, keys, 0) @@ -484,8 +490,10 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, zScore, arginfo_key_member, ZEND_ACC_PUBLIC) PHP_ME(Redis, zdiff, arginfo_zdiff, ZEND_ACC_PUBLIC) PHP_ME(Redis, zdiffstore, arginfo_zdiffstore, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zinter, arginfo_zinterunion, ZEND_ACC_PUBLIC) PHP_ME(Redis, zinterstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC) + PHP_ME(Redis, zunion, arginfo_zinterunion, ZEND_ACC_PUBLIC) PHP_ME(Redis, zunionstore, arginfo_zstore, ZEND_ACC_PUBLIC) PHP_FE_END }; @@ -2317,6 +2325,18 @@ PHP_METHOD(Redis, zdiff) { } /* }}} */ +/* {{{ proto array Redis::zinter(array keys, array|null weights, array options) */ +PHP_METHOD(Redis, zinter) { + REDIS_PROCESS_KW_CMD("ZINTER", redis_zinterunion_cmd, redis_zdiff_response); +} +/* }}} */ + +/* {{{ proto array Redis::zunion(array keys, array|null weights, array options) */ +PHP_METHOD(Redis, zunion) { + REDIS_PROCESS_KW_CMD("ZUNION", redis_zinterunion_cmd, redis_zdiff_response); +} +/* }}} */ + /* {{{ proto array Redis::zdiffstore(string destination, array keys) */ PHP_METHOD(Redis, zdiffstore) { REDIS_PROCESS_CMD(zdiffstore, redis_long_response); @@ -2325,12 +2345,12 @@ PHP_METHOD(Redis, zdiffstore) { /* zinterstore */ PHP_METHOD(Redis, zinterstore) { - REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, redis_long_response); } /* zunionstore */ PHP_METHOD(Redis, zunionstore) { - REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, redis_long_response); + REDIS_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, redis_long_response); } /* {{{ proto array Redis::zPopMax(string key) */ diff --git a/redis_cluster.c b/redis_cluster.c index ab9d55b7..23bf781a 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1683,14 +1683,14 @@ PHP_METHOD(RedisCluster, zrangebyscore) { /* {{{ proto RedisCluster::zunionstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zunionstore) { - CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinter_cmd, cluster_long_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZUNIONSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0); } /* }}} */ /* {{{ proto RedisCluster::zinterstore(string dst, array keys, [array weights, * string agg]) */ PHP_METHOD(RedisCluster, zinterstore) { - CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinter_cmd, cluster_long_resp, 0); + CLUSTER_PROCESS_KW_CMD("ZINTERSTORE", redis_zinterunionstore_cmd, cluster_long_resp, 0); } /* }}} */ diff --git a/redis_commands.c b/redis_commands.c index e18690a4..aa1cf62d 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -676,6 +676,124 @@ redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } int +redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + int numkeys; + smart_string cmdstr = {0}; + zval *z_keys, *z_weights = NULL, *z_opts = NULL, *z_ele; + zend_string *aggregate = NULL; + zend_bool withscores = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|a!a", + &z_keys, &z_weights, &z_opts) == FAILURE) + { + return FAILURE; + } + + if ((numkeys = zend_hash_num_elements(Z_ARRVAL_P(z_keys))) == 0) { + return FAILURE; + } + + if (z_weights) { + if (zend_hash_num_elements(Z_ARRVAL_P(z_weights)) != numkeys) { + php_error_docref(NULL, E_WARNING, + "WEIGHTS and keys array should be the same size!"); + return FAILURE; + } + } + + if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { + zend_ulong idx; + zend_string *zkey; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(z_opts), idx, zkey, z_ele) { + if (zkey != NULL) { + ZVAL_DEREF(z_ele); + if (zend_string_equals_literal_ci(zkey, "aggregate")) { + aggregate = zval_get_string(z_ele); + if (!zend_string_equals_literal_ci(aggregate, "sum") && + !zend_string_equals_literal_ci(aggregate, "min") && + !zend_string_equals_literal_ci(aggregate, "max") + ) { + php_error_docref(NULL, E_WARNING, + "Invalid AGGREGATE option provided!"); + zend_string_release(aggregate); + return FAILURE; + } + } else if (zend_string_equals_literal_ci(zkey, "withscores")) { + withscores = zval_is_true(z_ele); + } + } + } ZEND_HASH_FOREACH_END(); + } + + redis_cmd_init_sstr(&cmdstr, 1 + numkeys + (z_weights ? 1 + numkeys : 0) + (aggregate ? 2 : 0) + withscores, kw, strlen(kw)); + redis_cmd_append_sstr_long(&cmdstr, numkeys); + + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_keys), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + } ZEND_HASH_FOREACH_END(); + + if (z_weights) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WEIGHTS"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(z_weights), z_ele) { + ZVAL_DEREF(z_ele); + switch (Z_TYPE_P(z_ele)) { + case IS_LONG: + redis_cmd_append_sstr_long(&cmdstr, Z_LVAL_P(z_ele)); + break; + case IS_DOUBLE: + redis_cmd_append_sstr_dbl(&cmdstr, Z_DVAL_P(z_ele)); + break; + case IS_STRING: { + double dval; + zend_long lval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele), &lval, &dval, 0); + if (type == IS_LONG) { + redis_cmd_append_sstr_long(&cmdstr, lval); + break; + } else if (type == IS_DOUBLE) { + redis_cmd_append_sstr_dbl(&cmdstr, dval); + break; + } else if (strncasecmp(Z_STRVAL_P(z_ele), "-inf", sizeof("-inf") - 1) == 0 || + strncasecmp(Z_STRVAL_P(z_ele), "+inf", sizeof("+inf") - 1) == 0 || + strncasecmp(Z_STRVAL_P(z_ele), "inf", sizeof("inf") - 1) == 0 + ) { + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_ele), Z_STRLEN_P(z_ele)); + break; + } + // fall through + } + default: + php_error_docref(NULL, E_WARNING, + "Weights must be numeric or '-inf','inf','+inf'"); + if (aggregate) zend_string_release(aggregate); + efree(cmdstr.c); + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + } + + if (aggregate) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "AGGREGATE"); + redis_cmd_append_sstr_zstr(&cmdstr, aggregate); + zend_string_release(aggregate); + } + + if (withscores) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); + *ctx = redis_sock; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { @@ -710,7 +828,8 @@ redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* ZUNIONSTORE, ZINTERSTORE */ -int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int +redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { diff --git a/redis_commands.h b/redis_commands.h index c2f79095..be30a310 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -106,10 +106,13 @@ int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_zdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_zinterunion_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_zdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_zinter_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_zinterunionstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index b7746c5e..350f35f8 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -52,6 +52,8 @@ class Redis_Cluster_Test extends Redis_Test { public function testlMove() { return $this->markTestSkipped(); } public function testsMisMember() { return $this->markTestSkipped(); } public function testzDiff() { return $this->markTestSkipped(); } + public function testzInter() { return $this->markTestSkipped(); } + public function testzUnion() { return $this->markTestSkipped(); } public function testzDiffStore() { return $this->markTestSkipped(); } public function testzMscore() { return $this->marktestSkipped(); } public function testCopy() { return $this->marktestSkipped(); } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index b4410cd9..a2463f59 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2547,6 +2547,40 @@ class Redis_Test extends TestSuite $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true])); } + public function testzInter() + { + // Only available since 6.2.0 + if (version_compare($this->version, '6.2.0') < 0) { + $this->markTestSkipped(); + return; + } + + $this->redis->del('key'); + foreach (range('a', 'c') as $c) { + $this->redis->zAdd('key', 1, $c); + } + + $this->assertEquals(['a', 'b', 'c'], $this->redis->zInter(['key'])); + $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zInter(['key'], null, ['withscores' => true])); + } + + public function testzUnion() + { + // Only available since 6.2.0 + if (version_compare($this->version, '6.2.0') < 0) { + $this->markTestSkipped(); + return; + } + + $this->redis->del('key'); + foreach (range('a', 'c') as $c) { + $this->redis->zAdd('key', 1, $c); + } + + $this->assertEquals(['a', 'b', 'c'], $this->redis->zUnion(['key'])); + $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zUnion(['key'], null, ['withscores' => true])); + } + public function testzDiffStore() { // Only available since 6.2.0 |