mirror of
https://github.com/predis/predis.git
synced 2026-09-15 04:47:01 +00:00
Extended Sorted Set support by adding ZINTERCARD command (#861)
* Refactored zinterstore, zunionstore commands and command traits * Merge conflicts resolve, update cluster strategy test with new arguments * Updated assertion in case if command executed faster then duration minimal threshold * Updated Limit trait to handle integer values * Updated Limit trait to return previous arguments if limit argument isn't set * Added support for ZINTERCARD command Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
This commit is contained in:
committed by
GitHub
parent
87ecd7bde2
commit
7c0cc7bdbe
@@ -114,6 +114,7 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zdiff(array $keys, bool $withScores = false)
|
||||
* @method $this zdiffstore(string $destination, array $keys)
|
||||
* @method $this zincrby($key, $increment, $member)
|
||||
* @method $this zintercard(array $keys, int $limit = 0)
|
||||
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method $this zmscore(string $key, string ...$member)
|
||||
|
||||
@@ -130,6 +130,7 @@ use Predis\Response\Status;
|
||||
* @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 zintercard(array $keys, int $limit = 0)
|
||||
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
|
||||
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method array zmscore(string $key, string ...$member)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use Predis\Command\Traits\Keys;
|
||||
use Predis\Command\Traits\Limit;
|
||||
|
||||
/**
|
||||
* @link https://redis.io/commands/zintercard/
|
||||
*
|
||||
* This command is similar to ZINTER, but instead of returning the result set,
|
||||
* it returns just the cardinality of the result.
|
||||
*/
|
||||
class ZINTERCARD extends RedisCommand
|
||||
{
|
||||
use Keys {
|
||||
Keys::setArguments as setKeys;
|
||||
}
|
||||
use Limit {
|
||||
Limit::setArguments as setLimit;
|
||||
}
|
||||
|
||||
protected static $keysArgumentPositionOffset = 0;
|
||||
protected static $limitArgumentPositionOffset = 1;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return 'ZINTERCARD';
|
||||
}
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->setLimit($arguments);
|
||||
$arguments = $this->getArguments();
|
||||
|
||||
$this->setKeys($arguments);
|
||||
}
|
||||
}
|
||||
@@ -10,24 +10,33 @@ use UnexpectedValueException;
|
||||
*/
|
||||
trait Limit
|
||||
{
|
||||
private static $limitModifier = 'LIMIT';
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$argument = $arguments[static::$limitArgumentPositionOffset];
|
||||
$argumentsLength = count($arguments);
|
||||
$argumentsBefore = array_slice($arguments, 0, static::$limitArgumentPositionOffset);
|
||||
|
||||
if (false === $argument) {
|
||||
if (
|
||||
static::$limitArgumentPositionOffset >= $argumentsLength
|
||||
|| false === $arguments[static::$limitArgumentPositionOffset]
|
||||
) {
|
||||
parent::setArguments($argumentsBefore);
|
||||
return;
|
||||
}
|
||||
|
||||
$argument = $arguments[static::$limitArgumentPositionOffset];
|
||||
$argumentsAfter = array_slice($arguments, static::$limitArgumentPositionOffset + 1);
|
||||
|
||||
if (true === $argument) {
|
||||
$argument = 'LIMIT';
|
||||
} else {
|
||||
parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], $argumentsAfter));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_int($argument)) {
|
||||
throw new UnexpectedValueException('Wrong limit argument type');
|
||||
}
|
||||
|
||||
$argumentsAfter = array_slice($arguments, static::$limitArgumentPositionOffset + 1);
|
||||
|
||||
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
|
||||
parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], [$argument], $argumentsAfter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Response\ServerException;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class ZINTERCARD_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return ZINTERCARD::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'ZINTERCARD';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider argumentsProvider
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
|
||||
{
|
||||
$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 $firstSortedSet
|
||||
* @param array $secondSortedSet
|
||||
* @param int $limit
|
||||
* @param int $expectedResponse
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.0.0
|
||||
*/
|
||||
public function testReturnsIntersectionCardinalityOnSortedSets(
|
||||
array $firstSortedSet,
|
||||
array $secondSortedSet,
|
||||
int $limit,
|
||||
int $expectedResponse
|
||||
): void {
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('test-zintercard1', ...$firstSortedSet);
|
||||
$redis->zadd('test-zintercard2', ...$secondSortedSet);
|
||||
|
||||
$this->assertSame($expectedResponse, $redis->zintercard(['test-zintercard1', 'test-zintercard2'], $limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.0.0
|
||||
*/
|
||||
public function testReturnsIntersectionCardinalityZeroOnEmptySortedSetGiven(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$sortedSet = [1, 'member1', 2, 'member2', 3, 'member3'];
|
||||
|
||||
$redis->zadd('test-zintercard', ...$sortedSet);
|
||||
|
||||
$this->assertSame(0, $redis->zintercard(['test-zintercard', 'non-existing-key']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 7.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType(): void
|
||||
{
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->zintercard(['foo']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @dataProvider unexpectedValuesProvider
|
||||
* @param $keys
|
||||
* @param $limit
|
||||
* @param string $expectedExceptionMessage
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.0.0
|
||||
*/
|
||||
public function testThrowsExceptionOnUnexpectedValueGiven(
|
||||
$keys,
|
||||
$limit,
|
||||
string $expectedExceptionMessage
|
||||
): void {
|
||||
$redis = $this->getClient();
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage($expectedExceptionMessage);
|
||||
|
||||
$redis->zintercard($keys, $limit);
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with required arguments only' => [
|
||||
[['key1', 'key2']],
|
||||
[2, 'key1', 'key2'],
|
||||
],
|
||||
'with all arguments' => [
|
||||
[['key1', 'key2'], 2],
|
||||
[2, 'key1', 'key2', 'LIMIT', 2],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function sortedSetsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with full intersection' => [
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
0,
|
||||
3
|
||||
],
|
||||
'with partial intersection' => [
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
[1, 'member1', 2, 'member2', 4, 'member4'],
|
||||
0,
|
||||
2
|
||||
],
|
||||
'with no intersection' => [
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
[4, 'member4', 5, 'member5', 6, 'member6'],
|
||||
0,
|
||||
0
|
||||
],
|
||||
'with full intersection and limit' => [
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
[1, 'member1', 2, 'member2', 3, 'member3'],
|
||||
1,
|
||||
1
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function unexpectedValuesProvider(): array
|
||||
{
|
||||
return [
|
||||
'with wrong type keys argument' => [
|
||||
'wrong',
|
||||
0,
|
||||
'Wrong keys argument type or position offset'
|
||||
],
|
||||
'with wrong type limit argument' => [
|
||||
['key1', 'key'],
|
||||
[1],
|
||||
'Wrong limit argument type'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,16 @@ class LimitTest extends PredisTestCase
|
||||
['first argument', true, 'third argument'],
|
||||
['first argument', 'LIMIT', 'third argument']
|
||||
],
|
||||
'limit argument is integer' => [
|
||||
0,
|
||||
[1],
|
||||
['LIMIT', 1]
|
||||
],
|
||||
'limit argument with wrong offset' => [
|
||||
2,
|
||||
[1],
|
||||
[1],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user