Эх сурвалжийг харах

Add new command: GEORADIUSBYMEMBER (Redis 3.2.0).

Daniele Alessandri 9 жил өмнө
parent
commit
8491b1d880

+ 1 - 0
src/ClientContextInterface.php

@@ -162,6 +162,7 @@ use Predis\Command\CommandInterface;
  * @method $this geopos($key, array $members)
  * @method $this geodist($key, $member1, $member2, $unit = null)
  * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
+ * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
  *
  * @author Daniele Alessandri <suppakilla@gmail.com>
  */

+ 1 - 0
src/ClientInterface.php

@@ -170,6 +170,7 @@ use Predis\Profile\ProfileInterface;
  * @method array  geopos($key, array $members)
  * @method string geodist($key, $member1, $member2, $unit = null)
  * @method array  georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
+ * @method array  georadiusbymember($key, $member, $radius, $unit, array $options = null)
  *
  * @author Daniele Alessandri <suppakilla@gmail.com>
  */

+ 5 - 3
src/Cluster/ClusterStrategy.php

@@ -172,6 +172,7 @@ abstract class ClusterStrategy implements StrategyInterface
             'GEOPOS' => $getKeyFromFirstArgument,
             'GEODIST' => $getKeyFromFirstArgument,
             'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'),
+            'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'),
         );
     }
 
@@ -331,7 +332,7 @@ abstract class ClusterStrategy implements StrategyInterface
     }
 
     /**
-     * Extracts the key from GEORADIUS command.
+     * Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
      *
      * @param CommandInterface $command Command instance.
      *
@@ -341,11 +342,12 @@ abstract class ClusterStrategy implements StrategyInterface
     {
         $arguments = $command->getArguments();
         $argc = count($arguments);
+        $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
 
-        if ($argc > 5) {
+        if ($argc > $startIndex) {
             $keys = array($arguments[0]);
 
-            for ($i = 5; $i < $argc; $i++) {
+            for ($i = $startIndex; $i < $argc; $i++) {
                 $argument = strtoupper($arguments[$i]);
                 if ($argument === 'STORE' || $argument === 'STOREDIST') {
                     $keys[] = $arguments[++$i];

+ 28 - 0
src/Command/GeospatialGeoRadiusByMember.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * 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/georadiusbymember
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'GEORADIUSBYMEMBER';
+    }
+}

+ 4 - 2
src/Command/Processor/KeyPrefixProcessor.php

@@ -165,6 +165,7 @@ class KeyPrefixProcessor implements ProcessorInterface
             'GEOPOS' => 'static::first',
             'GEODIST' => 'static::first',
             'GEORADIUS' => 'static::georadius',
+            'GEORADIUSBYMEMBER' => 'static::georadius',
         );
     }
 
@@ -429,9 +430,10 @@ class KeyPrefixProcessor implements ProcessorInterface
     {
         if ($arguments = $command->getArguments()) {
             $arguments[0] = "$prefix{$arguments[0]}";
+            $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
 
-            if (($count = count($arguments)) > 5) {
-                for ($i = 5; $i < $count; ++$i) {
+            if (($count = count($arguments)) > $startIndex) {
+                for ($i = $startIndex; $i < $count; ++$i) {
                     switch (strtoupper($arguments[$i])) {
                         case 'STORE':
                         case 'STOREDIST':

+ 1 - 0
src/Profile/RedisVersion320.php

@@ -275,6 +275,7 @@ class RedisVersion320 extends RedisProfile
             'GEOPOS' => 'Predis\Command\GeospatialGeoPos',
             'GEODIST' => 'Predis\Command\GeospatialGeoDist',
             'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius',
+            'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember',
         );
     }
 }

+ 4 - 2
src/Replication/ReplicationStrategy.php

@@ -151,9 +151,10 @@ class ReplicationStrategy
     {
         $arguments = $command->getArguments();
         $argc = count($arguments);
+        $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
 
-        if ($argc > 5) {
-            for ($i = 5; $i < $argc; $i++) {
+        if ($argc > $startIndex) {
+            for ($i = $startIndex; $i < $argc; $i++) {
                 $argument = strtoupper($arguments[$i]);
                 if ($argument === 'STORE' || $argument === 'STOREDIST') {
                     return false;
@@ -297,6 +298,7 @@ class ReplicationStrategy
             'GEOPOS' => true,
             'GEODIST' => true,
             'GEORADIUS' => array($this, 'isGeoradiusReadOnly'),
+            'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'),
         );
     }
 }

+ 18 - 0
tests/Predis/Cluster/PredisStrategyTest.php

@@ -187,6 +187,23 @@ class PredisStrategyTest extends PredisTestCase
         $this->assertNotNull($strategy->getSlot($command), $commandID);
     }
 
+    /**
+     * @group disconnected
+     */
+    public function testKeysForGeoradiusByMemberCommand()
+    {
+        $strategy = $this->getClusterStrategy();
+        $profile = Profile\Factory::getDevelopment();
+
+        $commandID = 'GEORADIUSBYMEMBER';
+
+        $command = $profile->createCommand($commandID, array('{key}:1', 'member', 1, 'km'));
+        $this->assertNotNull($strategy->getSlot($command), $commandID);
+
+        $command = $profile->createCommand($commandID, array('{key}:1', 'member', 1, 'km', 'store', '{key}:2', 'storedist', '{key}:3'));
+        $this->assertNotNull($strategy->getSlot($command), $commandID);
+    }
+
     /**
      * @group disconnected
      */
@@ -419,6 +436,7 @@ class PredisStrategyTest extends PredisTestCase
             'GEOPOS' => 'keys-first',
             'GEODIST' => 'keys-first',
             'GEORADIUS' => 'keys-georadius',
+            'GEORADIUSBYMEMBER' => 'keys-georadius',
         );
 
         if (isset($type)) {

+ 18 - 0
tests/Predis/Cluster/RedisStrategyTest.php

@@ -200,6 +200,23 @@ class RedisStrategyTest extends PredisTestCase
         $this->assertNotNull($strategy->getSlot($command), $commandID);
     }
 
+    /**
+     * @group disconnected
+     */
+    public function testKeysForGeoradiusByMemberCommand()
+    {
+        $strategy = $this->getClusterStrategy();
+        $profile = Profile\Factory::getDevelopment();
+
+        $commandID = 'GEORADIUSBYMEMBER';
+
+        $command = $profile->createCommand($commandID, array('{key}:1', 'member', 1, 'km'));
+        $this->assertNotNull($strategy->getSlot($command), $commandID);
+
+        $command = $profile->createCommand($commandID, array('{key}:1', 'member', 1, 'km', 'store', '{key}:2', 'storedist', '{key}:3'));
+        $this->assertNotNull($strategy->getSlot($command), $commandID);
+    }
+
     /**
      * @group disconnected
      */
@@ -429,6 +446,7 @@ class RedisStrategyTest extends PredisTestCase
             'GEOPOS' => 'keys-first',
             'GEODIST' => 'keys-first',
             'GEORADIUS' => 'keys-georadius',
+            'GEORADIUSBYMEMBER' => 'keys-georadius',
         );
 
         if (isset($type)) {

+ 168 - 0
tests/Predis/Command/GeospatialGeoRadiusByMemberTest.php

@@ -0,0 +1,168 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * 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-geospatial
+ */
+class GeospatialGeoRadiusByMemberTest extends PredisCommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Command\GeospatialGeoRadiusByMember';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'GEORADIUSBYMEMBER';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array(
+            'Sicily', 'Agrigento', 100, 'km',
+            'WITHCOORD', 'WITHDIST', 'WITHHASH', 'COUNT', 1, 'ASC', 'STORE', 'key:store', 'STOREDIST', 'key:storedist'
+        );
+
+        $expected = array(
+            'Sicily', 'Agrigento', 100, 'km',
+            'WITHCOORD', 'WITHDIST', 'WITHHASH', 'COUNT', 1, 'ASC', 'STORE', 'key:store', 'STOREDIST', 'key:storedist'
+        );
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsWithComplexOptions()
+    {
+        $arguments = array(
+            'Sicily', 'Agrigento', 100, 'km', array(
+                'store' => 'key:store',
+                'storedist' => 'key:storedist',
+                'withdist' => true,
+                'withcoord' => true,
+                'withhash' => true,
+                'count' => 1,
+                'sort' => 'asc',
+            ),
+        );
+
+        $expected = array(
+            'Sicily', 'Agrigento', 100, 'km',
+            'WITHCOORD', 'WITHDIST', 'WITHHASH', 'COUNT', 1, 'ASC', 'STORE', 'key:store', 'STOREDIST', 'key:storedist'
+        );
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsWithSpecificOptionsSetToFalse()
+    {
+        $arguments = array(
+            'Sicily', 'Agrigento', 100, 'km', array(
+                'store' => 'key:store',
+                'storedist' => 'key:storedist',
+                'withdist' => false,
+                'withcoord' => false,
+                'withhash' => false,
+                'count' => 1,
+                'sort' => 'asc',
+            ),
+        );
+
+        $expected = array('Sicily', 'Agrigento', 100, 'km', 'COUNT', 1, 'ASC', 'STORE', 'key:store', 'STOREDIST', 'key:storedist');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponseWithNoOptions()
+    {
+        $raw = array(
+            array('Agrigento', 'Palermo'),
+        );
+
+        $expected = array(
+            array('Agrigento', 'Palermo'),
+        );
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     */
+    public function testCommandReturnsGeoRadiusInfoWithNoOptions()
+    {
+        $redis = $this->getClient();
+
+        $redis->geoadd('Sicily', '13.361389', '38.115556', 'Palermo', '15.087269', '37.502669', 'Catania', '13.583333', '37.316667', 'Agrigento');
+        $this->assertEquals(array('Agrigento', 'Palermo'), $redis->georadiusbymember('Sicily', 'Agrigento', 100, 'km'));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     */
+    public function testCommandReturnsGeoRadiusInfoWithOptions()
+    {
+        $redis = $this->getClient();
+
+        $redis->geoadd('Sicily', '13.361389', '38.115556', 'Palermo', '15.087269', '37.502669', 'Catania', '13.583333', '37.316667', 'Agrigento');
+        $this->assertEquals(array(
+            array('Agrigento', '0.0000', array('13.5833314061164856', '37.31666804993816555')),
+            array('Palermo', '90.9778', array('13.361389338970184', '38.115556395496299')),
+        ), $redis->georadiusbymember('Sicily', 'Agrigento', 100, 'km', 'WITHDIST', 'WITHCOORD'));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     * @expectedException \Predis\Response\ServerException
+     * @expectedExceptionMessage Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->lpush('Sicily', 'Palermo');
+        $redis->georadiusbymember('Sicily', 'Agrigento', 200, 'km');
+    }
+}

+ 8 - 0
tests/Predis/Command/Processor/KeyPrefixProcessorTest.php

@@ -901,6 +901,14 @@ class KeyPrefixProcessorTest extends PredisTestCase
                 array('key', '15', '37', '200', 'km', 'WITHDIST', 'STORE', 'key:store', 'STOREDIST', 'key:storedist'),
                 array('prefix:key', '15', '37', '200', 'km', 'WITHDIST', 'STORE', 'prefix:key:store', 'STOREDIST', 'prefix:key:storedist'),
             ),
+            array('GEORADIUSBYMEMBER',
+                array('key', 'member', '100', 'km'),
+                array('prefix:key', 'member', '100', 'km'),
+            ),
+            array('GEORADIUSBYMEMBER',
+                array('key', 'member', '100', 'km', 'WITHDIST', 'STORE', 'key:store', 'STOREDIST', 'key:storedist'),
+                array('prefix:key', 'member', '100', 'km', 'WITHDIST', 'STORE', 'prefix:key:store', 'STOREDIST', 'prefix:key:storedist'),
+            ),
         );
     }
 }

+ 1 - 0
tests/Predis/Profile/RedisUnstableTest.php

@@ -196,6 +196,7 @@ class RedisUnstableTest extends PredisProfileTestCase
             155 => 'GEOPOS',
             156 => 'GEODIST',
             157 => 'GEORADIUS',
+            158 => 'GEORADIUSBYMEMBER',
         );
     }
 }

+ 1 - 0
tests/Predis/Profile/RedisVersion320Test.php

@@ -196,6 +196,7 @@ class RedisVersion320Test extends PredisProfileTestCase
             155 => 'GEOPOS',
             156 => 'GEODIST',
             157 => 'GEORADIUS',
+            158 => 'GEORADIUSBYMEMBER',
         );
     }
 }

+ 28 - 0
tests/Predis/Replication/ReplicationStrategyTest.php

@@ -166,6 +166,33 @@ class ReplicationStrategyTest extends PredisTestCase
         );
     }
 
+    /**
+     * @group disconnected
+     */
+    public function testGeoradiusByMemberCommand()
+    {
+        $profile = Profile\Factory::getDevelopment();
+        $strategy = new ReplicationStrategy();
+
+        $command = $profile->createCommand('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km'));
+        $this->assertTrue(
+            $strategy->isReadOperation($command),
+            'GEORADIUSBYMEMBER is expected to be a read operation.'
+        );
+
+        $command = $profile->createCommand('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km', 'store', 'key:store'));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'GEORADIUSBYMEMBER with STORE is expected to be a write operation.'
+        );
+
+        $command = $profile->createCommand('GEORADIUSBYMEMBER', array('key:geo', 15, 37, 200, 'km', 'storedist', 'key:storedist'));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'GEORADIUSBYMEMBER with STOREDIST is expected to be a write operation.'
+        );
+    }
+
     /**
      * @group disconnected
      * @expectedException \Predis\NotSupportedException
@@ -472,6 +499,7 @@ class ReplicationStrategyTest extends PredisTestCase
             'GEOPOS' => 'read',
             'GEODIST' => 'read',
             'GEORADIUS' => 'variable',
+            'GEORADIUSBYMEMBER' => 'variable',
         );
 
         if (isset($type)) {