Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend Sorted Set support by implementing ZDIFFSTORE command #828

Merged
merged 10 commits into from
Nov 30, 2022
1 change: 1 addition & 0 deletions src/ClientContextInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
* @method $this zdiff(array $keys, bool $withScores = false)
* @method $this zdiffstore(string $destination, array $keys)
* @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array|string $keys, array $options = null)
* @method $this zmscore(string $key, string ...$member)
Expand Down
1 change: 1 addition & 0 deletions src/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method array zdiff(array $keys, bool $withScores = false)
* @method int zdiffstore(string $destination, array $keys)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zmscore(string $key, string ...$member)
Expand Down
40 changes: 40 additions & 0 deletions src/Command/Redis/ZDIFFSTORE.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Predis\Command\Redis;

use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Numkeys;
use Predis\Command\Traits\Keys;

/**
* @link https://redis.io/commands/zdiffstore/
*
* Computes the difference between the first and all successive input sorted sets
* and stores the result in destination. The total number of input keys is specified by numkeys.
*
* Keys that do not exist are considered to be empty sets.
*
* If destination already exists, it is overwritten.
*/
class ZDIFFSTORE extends RedisCommand
{
use Keys;
use Numkeys {
setArguments as setNumkeys;
}

public static $keysArgumentPositionOffset = 1;

public function getId()
{
return 'ZDIFFSTORE';
}

public function setArguments(array $arguments)
{
$this->setNumkeys($arguments);
$arguments = $this->getArguments();
$this->unpackKeysArray(self::$keysArgumentPositionOffset + 1, $arguments);
parent::setArguments($arguments);
}
}
1 change: 0 additions & 1 deletion src/Command/Redis/ZMSCORE.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*
* For every member that does not exist in the sorted set, a null value is returned.
*
* @version >= 6.2.0
*/
class ZMSCORE extends RedisCommand
{
Expand Down
1 change: 0 additions & 1 deletion src/Command/Redis/ZRANDMEMBER.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* If called with a negative count, the behavior changes and the command
* is allowed to return the same element multiple times.
*
* @version >= 6.2.0
*/
class ZRANDMEMBER extends RedisCommand
{
Expand Down
95 changes: 95 additions & 0 deletions tests/Predis/Command/Redis/ZDIFFSTORE_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Predis\Command\Redis;

class ZDIFFSTORE_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return ZDIFFSTORE::class;
}

/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'ZDIFFSTORE';
}

/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$actualArguments = ['zset_diff', ['key1', 'key2']];
$expectedArguments = ['zset_diff', 2, 'key1', 'key2'];

$command = $this->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 sortedSetsProvider
* @param array $firstSetDictionary
* @param array $secondSetDictionary
* @param array $expectedResponse
* @param int $expectedResultingElements
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testStoresDifferenceBetweenSortedSets(
array $firstSetDictionary,
array $secondSetDictionary,
array $expectedResponse,
int $expectedResultingElements
): void {
$redis = $this->getClient();

$redis->zadd('test-zset-1', ...$firstSetDictionary);
$redis->zadd('test-zset-2', ...$secondSetDictionary);
$actualResponse = $redis->zdiffstore('zdiffstore', ['test-zset-1', 'test-zset-2']);

$this->assertSame($expectedResultingElements, $actualResponse);
$this->assertSame($expectedResponse, $redis->zrange('zdiffstore', 0, -1));
}


public function sortedSetsProvider(): array
{
return [
'no intersection' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member4', 2, 'member5', 3, 'member6'],
['member1', 'member2', 'member3'],
3
],
'partial intersection' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2', 3, 'member4'],
['member3'],
1
],
'full intersection' => [
[1, 'member1', 2, 'member2', 3, 'member3'],
[1, 'member1', 2, 'member2', 3, 'member3'],
[],
0
],
];
}
}