From 1661903cbaa40d55ba03a783f63cdf4c03243e59 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:08:26 +0200 Subject: [PATCH] Extended Count-Min Sketch support by implementing CMS.MERGE command (#1092) * Added support for CMS.MERGE command * Added command description --------- Co-authored-by: Vladyslav Vildanov --- src/ClientContextInterface.php | 1 + src/ClientInterface.php | 1 + src/Command/Redis/CountMinSketch/CMSMERGE.php | 42 +++++ .../Redis/CountMinSketch/CMSINCRBY_Test.php | 8 + .../Redis/CountMinSketch/CMSMERGE_Test.php | 176 ++++++++++++++++++ .../Redis/CountMinSketch/CMSQUERY_Test.php | 8 + 6 files changed, 236 insertions(+) create mode 100644 src/Command/Redis/CountMinSketch/CMSMERGE.php create mode 100644 tests/Predis/Command/Redis/CountMinSketch/CMSMERGE_Test.php diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 13d02328..bc77d2cb 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -75,6 +75,7 @@ use Predis\Command\CommandInterface; * @method $this cmsinfo(string $key) * @method $this cmsinitbydim(string $key, int $width, int $depth) * @method $this cmsinitbyprob(string $key, float $errorRate, float $probability) + * @method $this cmsmerge(string $destination, array $sources, array $weights = []) * @method $this cmsquery(string $key, string ...$item) * @method $this decr($key) * @method $this decrby($key, $decrement) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 5cc16e26..1748c345 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -84,6 +84,7 @@ use Predis\Response\Status; * @method array cmsinfo(string $key) * @method Status cmsinitbydim(string $key, int $width, int $depth) * @method Status cmsinitbyprob(string $key, float $errorRate, float $probability) + * @method Status cmsmerge(string $destination, array $sources, array $weights = []) * @method array cmsquery(string $key, string ...$item) * @method int decr(string $key) * @method int decrby(string $key, int $decrement) diff --git a/src/Command/Redis/CountMinSketch/CMSMERGE.php b/src/Command/Redis/CountMinSketch/CMSMERGE.php new file mode 100644 index 00000000..0e85c3b0 --- /dev/null +++ b/src/Command/Redis/CountMinSketch/CMSMERGE.php @@ -0,0 +1,42 @@ +assertSameValues($expectedArguments, $command->getArguments()); } + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $this->assertSame(1, $this->getCommand()->parseResponse(1)); + } + /** * @group connected * @dataProvider sketchesProvider diff --git a/tests/Predis/Command/Redis/CountMinSketch/CMSMERGE_Test.php b/tests/Predis/Command/Redis/CountMinSketch/CMSMERGE_Test.php new file mode 100644 index 00000000..67c11b99 --- /dev/null +++ b/tests/Predis/Command/Redis/CountMinSketch/CMSMERGE_Test.php @@ -0,0 +1,176 @@ +getCommand(); + $command->setArguments($actualArguments); + + $this->assertSame($expectedArguments, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $this->assertSame(1, $this->getCommand()->parseResponse(1)); + } + + /** + * @group connected + * @dataProvider sketchesProvider + * @param array $mergeArguments + * @param string $destinationKey + * @param array $items + * @param array $expectedCounts + * @return void + * @requiresRedisBfVersion >= 2.0.0 + */ + public function testMergeSketchesAndSaveWithinDestinationCountMinSketch( + array $mergeArguments, + string $destinationKey, + array $items, + array $expectedCounts + ): void { + $redis = $this->getClient(); + + $redis->cmsinitbyprob('source1', 0.001, 0.01); + $redis->cmsinitbyprob('source2', 0.001, 0.01); + $redis->cmsinitbyprob('destination', 0.001, 0.01); + $redis->cmsincrby('source1', 'item1', 1, 'item2', 1); + $redis->cmsincrby('source2', 'item1', 1, 'item2', 1); + + $actualResponse = $redis->cmsmerge(...$mergeArguments); + + $this->assertEquals('OK', $actualResponse); + $this->assertSame($expectedCounts, $redis->cmsquery('destination', ...$items)); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.0.0 + */ + public function testThrowsExceptionOnNonExistingDestinationCountMinSketch(): void + { + $redis = $this->getClient(); + + $redis->cmsinitbyprob('source1', 0.001, 0.01); + $redis->cmsinitbyprob('source2', 0.001, 0.01); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage('CMS: key does not exist'); + + $redis->cmsmerge('destination', ['source1', 'source2']); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.0.0 + */ + public function testThrowsExceptionOnNonExistingSourceCountMinSketch(): void + { + $redis = $this->getClient(); + + $redis->cmsinitbyprob('source1', 0.001, 0.01); + $redis->cmsinitbyprob('destination', 0.001, 0.01); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage('CMS: key does not exist'); + + $redis->cmsmerge('destination', ['source1', 'source2']); + } + + /** + * @group connected + * @return void + * @requiresRedisBfVersion >= 2.0.0 + */ + public function testThrowsExceptionOnDifferentCountMinSketchesConfigurations(): void + { + $redis = $this->getClient(); + + $redis->cmsinitbyprob('source1', 0.001, 0.01); + $redis->cmsinitbyprob('source2', 0.01, 0.01); + $redis->cmsinitbyprob('destination', 0.001, 0.01); + + $this->expectException(ServerException::class); + $this->expectExceptionMessage('CMS: width/depth is not equal'); + + $redis->cmsmerge('destination', ['source1', 'source2']); + } + + public function argumentsProvider(): array + { + return [ + 'with default arguments' => [ + ['destination', ['source']], + ['destination', 1, 'source'], + ], + 'with multiple sources' => [ + ['destination', ['source1', 'source2']], + ['destination', 2, 'source1', 'source2'], + ], + 'with WEIGHTS' => [ + ['destination', ['source1', 'source2'], [1, 3]], + ['destination', 2, 'source1', 'source2', 'WEIGHTS', 1, 3], + ], + ]; + } + + public function sketchesProvider(): array + { + return [ + 'with default arguments' => [ + ['destination', ['source1', 'source2']], + 'destination', + ['item1', 'item2'], + [2, 2], + ], + 'with modified WEIGHTS' => [ + ['destination', ['source1', 'source2'], [2, 4]], + 'destination', + ['item1', 'item2'], + [6, 6], + ], + ]; + } +} diff --git a/tests/Predis/Command/Redis/CountMinSketch/CMSQUERY_Test.php b/tests/Predis/Command/Redis/CountMinSketch/CMSQUERY_Test.php index 05004e0f..84a11c83 100644 --- a/tests/Predis/Command/Redis/CountMinSketch/CMSQUERY_Test.php +++ b/tests/Predis/Command/Redis/CountMinSketch/CMSQUERY_Test.php @@ -47,6 +47,14 @@ class CMSQUERY_Test extends PredisCommandTestCase $this->assertSameValues($expectedArguments, $command->getArguments()); } + /** + * @group disconnected + */ + public function testParseResponse(): void + { + $this->assertSame(1, $this->getCommand()->parseResponse(1)); + } + /** * @group connected * @dataProvider sketchesProvider