mirror of
https://github.com/predis/predis.git
synced 2026-08-31 12:43:31 +00:00
Extended RediSearch support by implementing FT.SUGGET command (#1185)
* Added support for new arguments for BITPOS, BITCOUNT commands (#1045) * Added support for new arguments for EXPIRE, EXPIREAT commands (#1046) * Extended core support by implementing SORT_RO command (#1044) * Added support for SORT_RO command * Codestyle fixes * Added command description --------- Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local> * fix deprecated call * Added support for container commands (#1049) * Added support for container commands FUNCTION LOAD, FUNCTION DELETE and FCALL * Changed ContainerInterface and AbstractContainer * Re-implement logic of abstract methods --------- Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local> * Added stream commands to KeyPrefixProcessor (#1051) Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local> * Fix return type of ReplicationInterface::getSlaves (#1111) * Codestyle fixes * Changed return annotation * Added support for FT.SUGGET command, added examples for suggestion API usage * Include SUGGET command files to codespell ignore * Added file extension --------- Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local> Co-authored-by: Till Krüss <till@kruss.io> Co-authored-by: Stephan <glaubinix@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
4b63d39c55
commit
5a9f62ef78
+1
-1
@@ -1,5 +1,5 @@
|
||||
[codespell]
|
||||
skip=./.git
|
||||
skip=./.git,./src/Command/Redis/Search/FTSUGGET.php,./examples/Commands/Search/ft_sug_add_get_del.php
|
||||
check-hidden=
|
||||
check-filenames=
|
||||
builtin=clear,rare,informal,usage,code,names
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.SUGADD, FT.SUGGET, FT.SUGDEL commands usage:
|
||||
|
||||
// 1. Add suggestion to key with payload
|
||||
$client = new Client();
|
||||
|
||||
$client->ftsugadd('key', 'hello', 2, (new SugAddArguments())->payload('payload'));
|
||||
|
||||
// 2. Perform fuzzy search by prefix to get previous suggestion with payload
|
||||
$response = $client->ftsugget('key', 'hellp', (new SugGetArguments())->fuzzy()->withPayloads());
|
||||
|
||||
echo 'Suggestion for "hellp" prefix:' . "\n";
|
||||
print_r($response);
|
||||
|
||||
// 3. Removes previous suggestion from key
|
||||
$client->ftsugdel('key', 'hello');
|
||||
$response = $client->ftsugget('key', 'hello');
|
||||
|
||||
echo 'Suggestions, after removing "hello" suggestion:' . "\n";
|
||||
print_r($response);
|
||||
@@ -21,6 +21,7 @@ use Predis\Command\Argument\Search\ProfileArguments;
|
||||
use Predis\Command\Argument\Search\Schema;
|
||||
use Predis\Command\Argument\Search\SearchArguments;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
use Predis\Command\Argument\Search\SynUpdateArguments;
|
||||
use Predis\Command\Argument\Server\LimitOffsetCount;
|
||||
use Predis\Command\Argument\Server\To;
|
||||
@@ -109,6 +110,7 @@ use Predis\Command\Container\Search\FTCONFIG;
|
||||
* @method $this ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method $this ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
|
||||
* @method $this ftsugdel(string $key, string $string)
|
||||
* @method $this ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
|
||||
* @method $this ftsyndump(string $index)
|
||||
* @method $this ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method $this fttagvals(string $index, string $fieldName)
|
||||
|
||||
@@ -21,6 +21,7 @@ use Predis\Command\Argument\Search\ProfileArguments;
|
||||
use Predis\Command\Argument\Search\Schema;
|
||||
use Predis\Command\Argument\Search\SearchArguments;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
use Predis\Command\Argument\Search\SynUpdateArguments;
|
||||
use Predis\Command\Argument\Server\LimitOffsetCount;
|
||||
use Predis\Command\Argument\Server\To;
|
||||
@@ -118,6 +119,7 @@ use Predis\Response\Status;
|
||||
* @method array ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
|
||||
* @method int ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
|
||||
* @method int ftsugdel(string $key, string $string)
|
||||
* @method array ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
|
||||
* @method array ftsyndump(string $index)
|
||||
* @method Status ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method array fttagvals(string $index, string $fieldName)
|
||||
|
||||
@@ -77,6 +77,30 @@ class CommonArguments implements ArrayableArgument
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Also returns the relative internal score of each document.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withScores(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHSCORES';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves optional document payloads.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withPayloads(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHPAYLOADS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
||||
@@ -48,30 +48,6 @@ class SearchArguments extends CommonArguments
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Also returns the relative internal score of each document.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withScores(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHSCORES';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves optional document payloads.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function withPayloads(): self
|
||||
{
|
||||
$this->arguments[] = 'WITHPAYLOADS';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the sorting key, right after the id and score and/or payload, if requested.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
class SugGetArguments extends CommonArguments
|
||||
{
|
||||
/**
|
||||
* Performs a fuzzy prefix search, including prefixes at Levenshtein distance of 1 from the prefix sent.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fuzzy(): self
|
||||
{
|
||||
$this->arguments[] = 'FUZZY';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the results to a maximum of num (default: 5).
|
||||
*
|
||||
* @param int $num
|
||||
* @return $this
|
||||
*/
|
||||
public function max(int $num): self
|
||||
{
|
||||
array_push($this->arguments, 'MAX', $num);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis\Search;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
/**
|
||||
* @see https://redis.io/commands/ft.sugget/
|
||||
*
|
||||
* Get completion suggestions for a prefix.
|
||||
*/
|
||||
class FTSUGGET extends RedisCommand
|
||||
{
|
||||
public function getId()
|
||||
{
|
||||
return 'FT.SUGGET';
|
||||
}
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
[$key, $prefix] = $arguments;
|
||||
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];
|
||||
|
||||
parent::setArguments(array_merge(
|
||||
[$key, $prefix],
|
||||
$commandArguments
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -65,4 +65,24 @@ class CommonArgumentsTest extends TestCase
|
||||
|
||||
$this->assertSame(['SKIPINITIALSCAN'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithWithScoresModifier(): void
|
||||
{
|
||||
$this->arguments->withScores();
|
||||
|
||||
$this->assertSame(['WITHSCORES'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithWithPayloadsModifier(): void
|
||||
{
|
||||
$this->arguments->withPayloads();
|
||||
|
||||
$this->assertSame(['WITHPAYLOADS'], $this->arguments->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,26 +47,6 @@ class SearchArgumentsTest extends TestCase
|
||||
$this->assertSame(['VERBATIM'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithWithScoresModifier(): void
|
||||
{
|
||||
$this->arguments->withScores();
|
||||
|
||||
$this->assertSame(['WITHSCORES'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithWithPayloadsModifier(): void
|
||||
{
|
||||
$this->arguments->withPayloads();
|
||||
|
||||
$this->assertSame(['WITHPAYLOADS'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Argument\Search;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SugGetArgumentsTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var SugGetArguments
|
||||
*/
|
||||
private $arguments;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->arguments = new SugGetArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithFuzzyModifier(): void
|
||||
{
|
||||
$this->arguments->fuzzy();
|
||||
|
||||
$this->assertSame(['FUZZY'], $this->arguments->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testCreatesArgumentsWithMaxModifier(): void
|
||||
{
|
||||
$this->arguments->max(5);
|
||||
|
||||
$this->assertSame(['MAX', 5], $this->arguments->toArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2023 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis\Search;
|
||||
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
use Predis\Command\Redis\PredisCommandTestCase;
|
||||
|
||||
class FTSUGGET_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return FTSUGGET::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'FTSUGGET';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider argumentsProvider
|
||||
*/
|
||||
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($actualArguments);
|
||||
|
||||
$this->assertSameValues($expectedArguments, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @dataProvider suggestionProvider
|
||||
* @param array $addArguments
|
||||
* @param array $getArguments
|
||||
* @param array $expectedResponse
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.0.0
|
||||
*/
|
||||
public function testGetSuggestionsForGivenPrefix(
|
||||
array $addArguments,
|
||||
array $getArguments,
|
||||
array $expectedResponse
|
||||
): void {
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->ftsugadd(...$addArguments);
|
||||
|
||||
$actualResponse = $redis->ftsugget(...$getArguments);
|
||||
|
||||
$this->assertSame($expectedResponse, $actualResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.0.0
|
||||
*/
|
||||
public function testGetReturnsLimitedResultsWithMaxModifier(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->ftsugadd('key', 'hello', 2);
|
||||
$redis->ftsugadd('key', 'hell', 2);
|
||||
|
||||
$actualResponse = $redis->ftsugget('key', 'hel', (new SugGetArguments())->max(1));
|
||||
|
||||
$this->assertSame(['hell'], $actualResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.0.0
|
||||
*/
|
||||
public function testGetReturnsNullOnNonExistingKey(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertNull($redis->ftsugget('key', 'hel'));
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with default arguments' => [
|
||||
['key', 'prefix'],
|
||||
['key', 'prefix'],
|
||||
],
|
||||
'with FUZZY modifier' => [
|
||||
['key', 'prefix', (new SugGetArguments())->fuzzy()],
|
||||
['key', 'prefix', 'FUZZY'],
|
||||
],
|
||||
'with WITHSCORES modifier' => [
|
||||
['key', 'prefix', (new SugGetArguments())->withScores()],
|
||||
['key', 'prefix', 'WITHSCORES'],
|
||||
],
|
||||
'with WITHPAYLOADS modifier' => [
|
||||
['key', 'prefix', (new SugGetArguments())->withPayloads()],
|
||||
['key', 'prefix', 'WITHPAYLOADS'],
|
||||
],
|
||||
'with MAX modifier' => [
|
||||
['key', 'prefix', (new SugGetArguments())->max(5)],
|
||||
['key', 'prefix', 'MAX', 5],
|
||||
],
|
||||
'with all arguments' => [
|
||||
['key', 'prefix', (new SugGetArguments())->fuzzy()->withScores()->withPayloads()->max(5)],
|
||||
['key', 'prefix', 'FUZZY', 'WITHSCORES', 'WITHPAYLOADS', 'MAX', 5],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function suggestionProvider(): array
|
||||
{
|
||||
return [
|
||||
'with default arguments' => [
|
||||
['key', 'hello', 2],
|
||||
['key', 'hell'],
|
||||
['hello'],
|
||||
],
|
||||
'with FUZZY search' => [
|
||||
['key', 'hello', 2],
|
||||
['key', 'help', (new SugGetArguments())->fuzzy()],
|
||||
['hello'],
|
||||
],
|
||||
'with WITHSCORES modifier' => [
|
||||
['key', 'hello', 2],
|
||||
['key', 'hell', (new SugGetArguments())->withScores()],
|
||||
['hello', '1.4142135381698608'],
|
||||
],
|
||||
'with WITHPAYLOADS modifier' => [
|
||||
['key', 'hello', 2, (new SugAddArguments())->payload('payload')],
|
||||
['key', 'hell', (new SugGetArguments())->withPayloads()],
|
||||
['hello', 'payload'],
|
||||
],
|
||||
'with all modifiers' => [
|
||||
['key', 'hello', 2, (new SugAddArguments())->payload('payload')],
|
||||
['key', 'hellp', (new SugGetArguments())->fuzzy()->withScores()->withPayloads()],
|
||||
['hello', '290630304', 'payload'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user