From 2aa0b071fc6617754cd918c24eeffef6fae89afc Mon Sep 17 00:00:00 2001 From: Daniele Alessandri Date: Fri, 30 May 2014 11:32:39 +0200 Subject: [PATCH] New commands: PFADD, PFCOUNT, PFMERGE (Redis 2.8.9). Many thanks to @rubensayshi for his initial commits on this in #163, but I had to start from scratch for the master branch first. --- lib/Predis/Cluster/PredisStrategy.php | 5 + lib/Predis/Cluster/RedisStrategy.php | 5 + lib/Predis/Command/HyperLogLogAdd.php | 43 ++++++++ lib/Predis/Command/HyperLogLogCount.php | 35 ++++++ lib/Predis/Command/HyperLogLogMerge.php | 35 ++++++ .../Command/Processor/KeyPrefixProcessor.php | 3 + lib/Predis/Profile/RedisVersion280.php | 5 + .../Replication/ReplicationStrategy.php | 1 + tests/Predis/Cluster/PredisStrategyTest.php | 5 + tests/Predis/Cluster/RedisStrategyTest.php | 5 + tests/Predis/Command/HyperLogLogAddTest.php | 88 +++++++++++++++ tests/Predis/Command/HyperLogLogCountTest.php | 103 ++++++++++++++++++ tests/Predis/Command/HyperLogLogMergeTest.php | 86 +++++++++++++++ .../Processor/KeyPrefixProcessorTest.php | 12 ++ tests/Predis/Profile/RedisUnstableTest.php | 3 + tests/Predis/Profile/RedisVersion280Test.php | 3 + .../Replication/ReplicationStrategyTest.php | 5 + 17 files changed, 442 insertions(+) create mode 100644 lib/Predis/Command/HyperLogLogAdd.php create mode 100644 lib/Predis/Command/HyperLogLogCount.php create mode 100644 lib/Predis/Command/HyperLogLogMerge.php create mode 100644 tests/Predis/Command/HyperLogLogAddTest.php create mode 100644 tests/Predis/Command/HyperLogLogCountTest.php create mode 100644 tests/Predis/Command/HyperLogLogMergeTest.php diff --git a/lib/Predis/Cluster/PredisStrategy.php b/lib/Predis/Cluster/PredisStrategy.php index 017ac41e..f75b043a 100644 --- a/lib/Predis/Cluster/PredisStrategy.php +++ b/lib/Predis/Cluster/PredisStrategy.php @@ -156,6 +156,11 @@ class PredisStrategy implements StrategyInterface 'HVALS' => $keyIsFirstArgument, 'HSCAN' => $keyIsFirstArgument, + /* commands operating on HyperLogLog */ + 'PFADD' => $keyIsFirstArgument, + 'PFCOUNT' => $keysAreAllArguments, + 'PFMERGE' => $keysAreAllArguments, + /* scripting */ 'EVAL' => array($this, 'getKeyFromScriptingCommands'), 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), diff --git a/lib/Predis/Cluster/RedisStrategy.php b/lib/Predis/Cluster/RedisStrategy.php index e839def8..97e7ce0b 100644 --- a/lib/Predis/Cluster/RedisStrategy.php +++ b/lib/Predis/Cluster/RedisStrategy.php @@ -141,6 +141,11 @@ class RedisStrategy implements StrategyInterface 'HVALS' => $keyIsFirstArgument, 'HSCAN' => $keyIsFirstArgument, + /* commands operating on HyperLogLog */ + 'PFADD' => $keyIsFirstArgument, + 'PFCOUNT' => array($this, 'getKeyFromAllArguments'), + 'PFMERGE' => array($this, 'getKeyFromAllArguments'), + /* scripting */ 'EVAL' => array($this, 'getKeyFromScriptingCommands'), 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), diff --git a/lib/Predis/Command/HyperLogLogAdd.php b/lib/Predis/Command/HyperLogLogAdd.php new file mode 100644 index 00000000..5d2f1eb2 --- /dev/null +++ b/lib/Predis/Command/HyperLogLogAdd.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfadd + * @author Daniele Alessandri + */ +class HyperLogLogAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/lib/Predis/Command/HyperLogLogCount.php b/lib/Predis/Command/HyperLogLogCount.php new file mode 100644 index 00000000..231c6f41 --- /dev/null +++ b/lib/Predis/Command/HyperLogLogCount.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfcount + * @author Daniele Alessandri + */ +class HyperLogLogCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFCOUNT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/lib/Predis/Command/HyperLogLogMerge.php b/lib/Predis/Command/HyperLogLogMerge.php new file mode 100644 index 00000000..96746ac2 --- /dev/null +++ b/lib/Predis/Command/HyperLogLogMerge.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @link http://redis.io/commands/pfmerge + * @author Daniele Alessandri + */ +class HyperLogLogMerge extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFMERGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/lib/Predis/Command/Processor/KeyPrefixProcessor.php b/lib/Predis/Command/Processor/KeyPrefixProcessor.php index 68c2a30e..64bac06b 100644 --- a/lib/Predis/Command/Processor/KeyPrefixProcessor.php +++ b/lib/Predis/Command/Processor/KeyPrefixProcessor.php @@ -149,6 +149,9 @@ class KeyPrefixProcessor implements ProcessorInterface 'SSCAN' => 'self::first', 'ZSCAN' => 'self::first', 'HSCAN' => 'self::first', + 'PFADD' => 'self::first', + 'PFCOUNT' => 'self::all', + 'PFMERGE' => 'self::all', ); } diff --git a/lib/Predis/Profile/RedisVersion280.php b/lib/Predis/Profile/RedisVersion280.php index 17ff267d..8707388b 100644 --- a/lib/Predis/Profile/RedisVersion280.php +++ b/lib/Predis/Profile/RedisVersion280.php @@ -246,6 +246,11 @@ class RedisVersion280 extends RedisProfile /* publish - subscribe */ 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', ); } } diff --git a/lib/Predis/Replication/ReplicationStrategy.php b/lib/Predis/Replication/ReplicationStrategy.php index a0af0575..a8cbdd08 100644 --- a/lib/Predis/Replication/ReplicationStrategy.php +++ b/lib/Predis/Replication/ReplicationStrategy.php @@ -220,6 +220,7 @@ class ReplicationStrategy 'OBJECT' => true, 'BITCOUNT' => true, 'TIME' => true, + 'PFCOUNT' => true, 'SORT' => array($this, 'isSortReadOnly'), ); } diff --git a/tests/Predis/Cluster/PredisStrategyTest.php b/tests/Predis/Cluster/PredisStrategyTest.php index 8319f7c8..f9b4730f 100644 --- a/tests/Predis/Cluster/PredisStrategyTest.php +++ b/tests/Predis/Cluster/PredisStrategyTest.php @@ -362,6 +362,11 @@ class PredisStrategyTest extends PredisTestCase 'HVALS' => 'keys-first', 'HSCAN' => 'keys-first', + /* commands operating on HyperLogLog */ + 'PFADD' => 'keys-first', + 'PFCOUNT' => 'keys-all', + 'PFMERGE' => 'keys-all', + /* scripting */ 'EVAL' => 'keys-script', 'EVALSHA' => 'keys-script', diff --git a/tests/Predis/Cluster/RedisStrategyTest.php b/tests/Predis/Cluster/RedisStrategyTest.php index 7d98595d..ff3da933 100644 --- a/tests/Predis/Cluster/RedisStrategyTest.php +++ b/tests/Predis/Cluster/RedisStrategyTest.php @@ -359,6 +359,11 @@ class RedisStrategyTest extends PredisTestCase 'HVALS' => 'keys-first', 'HSCAN' => 'keys-first', + /* commands operating on HyperLogLog */ + 'PFADD' => 'keys-first', + 'PFCOUNT' => 'keys-all', + 'PFMERGE' => 'keys-all', + /* scripting */ 'EVAL' => 'keys-script', 'EVALSHA' => 'keys-script', diff --git a/tests/Predis/Command/HyperLogLogAddTest.php b/tests/Predis/Command/HyperLogLogAddTest.php new file mode 100644 index 00000000..52502840 --- /dev/null +++ b/tests/Predis/Command/HyperLogLogAddTest.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @group commands + * @group realm-hyperloglog + * @todo Add integration tests depending on the minor redis version + */ +class HyperLogLogAddTest extends PredisCommandTestCase +{ + /** + * {@inheritdoc} + */ + protected function getExpectedCommand() + { + return 'Predis\Command\HyperLogLogAdd'; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedId() + { + return 'PFADD'; + } + + /** + * @group disconnected + */ + public function testFilterArguments() + { + $arguments = array('key', 'a', 'b', 'c'); + $expected = array('key', 'a', 'b', 'c'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testFilterArgumentsFieldsAsSingleArray() + { + $arguments = array('key', array('a', 'b', 'c')); + $expected = array('key', 'a', 'b', 'c'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse() + { + $command = $this->getCommand(); + + $this->assertSame(false, $command->parseResponse(0)); + $this->assertSame(true, $command->parseResponse(1)); + } + + /** + * @group connected + * @expectedException Predis\Response\ServerException + * @expectedExceptionMessage Operation against a key holding the wrong kind of value + */ + public function testThrowsExceptionOnWrongType() + { + $redis = $this->getClient(); + + $redis->lpush('metavars', 'foo', 'hoge'); + $redis->pfadd('metavars', 'foofoo'); + } +} diff --git a/tests/Predis/Command/HyperLogLogCountTest.php b/tests/Predis/Command/HyperLogLogCountTest.php new file mode 100644 index 00000000..e753b7b9 --- /dev/null +++ b/tests/Predis/Command/HyperLogLogCountTest.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @group commands + * @group realm-hyperloglog + * @todo Add integration tests depending on the minor redis version + */ +class HyperLogLogCountTest extends PredisCommandTestCase +{ + /** + * {@inheritdoc} + */ + protected function getExpectedCommand() + { + return 'Predis\Command\HyperLogLogCount'; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedId() + { + return 'PFCOUNT'; + } + + /** + * @group disconnected + */ + public function testFilterArguments() + { + $arguments = array('key:1', 'key:2'); + $expected = array('key:1', 'key:2'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testFilterArgumentsFieldsAsSingleArray() + { + $arguments = array(array('key:1', 'key:2')); + $expected = array('key:1', 'key:2'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse() + { + $command = $this->getCommand(); + + $this->assertSame(0, $command->parseResponse(0)); + $this->assertSame(1, $command->parseResponse(1)); + $this->assertSame(10, $command->parseResponse(10)); + } + + /** + * @group connected + * @expectedException Predis\Response\ServerException + * @expectedExceptionMessage Operation against a key holding the wrong kind of value + */ + public function testThrowsExceptionOnWrongType() + { + $redis = $this->getClient(); + + $redis->lpush('metavars', 'foo', 'hoge'); + $redis->pfcount('metavars'); + } + + /** + * @group connected + * @expectedException Predis\Response\ServerException + * @expectedExceptionMessage Operation against a key holding the wrong kind of value + */ + public function testThrowsExceptionOnWrongTypeOfAtLeastOneKey() + { + $redis = $this->getClient(); + + $redis->pfadd('metavars:1', 'foo', 'hoge'); + $redis->lpush('metavars:2', 'foofoo'); + $redis->pfcount('metavars:1', 'metavars:2'); + } +} diff --git a/tests/Predis/Command/HyperLogLogMergeTest.php b/tests/Predis/Command/HyperLogLogMergeTest.php new file mode 100644 index 00000000..2b0bce8f --- /dev/null +++ b/tests/Predis/Command/HyperLogLogMergeTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +/** + * @group commands + * @group realm-hyperloglog + * @todo Add integration tests depending on the minor redis version + */ +class HyperLogLogMergeTest extends PredisCommandTestCase +{ + /** + * {@inheritdoc} + */ + protected function getExpectedCommand() + { + return 'Predis\Command\HyperLogLogMerge'; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedId() + { + return 'PFMERGE'; + } + + /** + * @group disconnected + */ + public function testFilterArguments() + { + $arguments = array('key:1', 'key:2', 'key:3'); + $expected = array('key:1', 'key:2', 'key:3'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testFilterArgumentsFieldsAsSingleArray() + { + $arguments = array(array('key:1', 'key:2', 'key:3')); + $expected = array('key:1', 'key:2', 'key:3'); + + $command = $this->getCommand(); + $command->setArguments($arguments); + + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse() + { + $this->assertSame('OK', $this->getCommand()->parseResponse('OK')); + } + + /** + * @group connected + * @expectedException Predis\Response\ServerException + * @expectedExceptionMessage Operation against a key holding the wrong kind of value + */ + public function testThrowsExceptionOnWrongType() + { + $redis = $this->getClient(); + + $redis->pfadd('metavars:1', 'foo', 'hoge'); + $redis->lpush('metavars:2', 'foofoo'); + $redis->pfmerge('metavars:1', 'metavars:2'); + } +} diff --git a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php index 9238cdf5..b9c70909 100644 --- a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php +++ b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php @@ -799,6 +799,18 @@ class KeyPrefixProcessorTest extends PredisTestCase array('key', '0', 'MATCH', 'field:*', 'COUNT', 10), array('prefix:key', '0', 'MATCH', 'field:*', 'COUNT', 10), ), + array('PFADD', + array('key', 'a', 'b', 'c'), + array('prefix:key', 'a', 'b', 'c'), + ), + array('PFCOUNT', + array('key:1', 'key:2', 'key:3'), + array('prefix:key:1', 'prefix:key:2', 'prefix:key:3'), + ), + array('PFMERGE', + array('key:1', 'key:2', 'key:3'), + array('prefix:key:1', 'prefix:key:2', 'prefix:key:3'), + ), ); } } diff --git a/tests/Predis/Profile/RedisUnstableTest.php b/tests/Predis/Profile/RedisUnstableTest.php index 21dbf45b..2d4ac7e0 100644 --- a/tests/Predis/Profile/RedisUnstableTest.php +++ b/tests/Predis/Profile/RedisUnstableTest.php @@ -179,6 +179,9 @@ class RedisUnstableTest extends PredisProfileTestCase 138 => 'ZSCAN', 139 => 'HSCAN', 140 => 'PUBSUB', + 141 => 'PFADD', + 142 => 'PFCOUNT', + 143 => 'PFMERGE', ); } } diff --git a/tests/Predis/Profile/RedisVersion280Test.php b/tests/Predis/Profile/RedisVersion280Test.php index 714fb17d..2063182d 100644 --- a/tests/Predis/Profile/RedisVersion280Test.php +++ b/tests/Predis/Profile/RedisVersion280Test.php @@ -179,6 +179,9 @@ class RedisVersion280Test extends PredisProfileTestCase 138 => 'ZSCAN', 139 => 'HSCAN', 140 => 'PUBSUB', + 141 => 'PFADD', + 142 => 'PFCOUNT', + 143 => 'PFMERGE', ); } } diff --git a/tests/Predis/Replication/ReplicationStrategyTest.php b/tests/Predis/Replication/ReplicationStrategyTest.php index c1575a63..79f4aa36 100644 --- a/tests/Predis/Replication/ReplicationStrategyTest.php +++ b/tests/Predis/Replication/ReplicationStrategyTest.php @@ -358,6 +358,11 @@ class ReplicationStrategyTest extends PredisTestCase 'HVALS' => 'read', 'HSCAN' => 'read', + /* commands operating on HyperLogLog */ + 'PFADD' => 'write', + 'PFMERGE' => 'write', + 'PFCOUNT' => 'read', + /* scripting */ 'EVAL' => 'write', 'EVALSHA' => 'write',