Extended RediSearch support by implementing FT.AGGREGATE command (#1176)

* 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.AGGREGATE command

* Added command usage example

---------

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:
Vladyslav Vildanov
2023-03-14 12:41:54 +02:00
committed by GitHub
parent 684bb34cf2
commit bc42b3bcc5
13 changed files with 822 additions and 121 deletions
+49
View File
@@ -0,0 +1,49 @@
<?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\AggregateArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\Schema;
require __DIR__ . '/../../shared.php';
// Example of FT.AGGREGATE command usage:
// 1. Create index
$client = new Client();
$ftCreateArguments = (new CreateArguments())->prefix(['user:']);
$schema = (new Schema())
->addTextField('name')
->addTextField('country')
->addNumericField('dob', '', Schema::SORTABLE);
$client->ftcreate('idx', $schema, $ftCreateArguments);
// 2. Add documents
$client->hset('user:0', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
$client->hset('user:1', 'name', 'Vlad', 'country', 'Israel', 'dob', 782265600);
$client->hset('user:2', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
// 3. Execute aggregation query
$ftAggregateArguments = (new AggregateArguments())
->apply('year(@dob)', 'birth')
->groupBy('@country', '@birth')
->reduce('COUNT', true, 'country_birth_Vlad_count')
->sortBy(0, '@birth', 'DESC');
$response = $client->ftaggregate('idx', '@name: "Vlad"', $ftAggregateArguments);
// Response grouped by user country and birth year, with users count in each group, sorted by birth year from DESC.
echo 'Response:' . "\n";
print_r($response);
+2
View File
@@ -14,6 +14,7 @@ namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
@@ -95,6 +96,7 @@ use Predis\Command\Container\Search\FTCONFIG;
* @method $this decrby($key, $decrement)
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method $this fcall(string $function, array $keys, ...$args)
* @method $this ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
* @method $this ftaliasadd(string $alias, string $index)
* @method $this ftaliasdel(string $alias)
* @method $this ftaliasupdate(string $alias, string $index)
+2
View File
@@ -14,6 +14,7 @@ namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
@@ -104,6 +105,7 @@ use Predis\Response\Status;
* @method int decrby(string $key, int $decrement)
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method mixed fcall(string $function, array $keys, ...$args)
* @method array ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
* @method Status ftaliasadd(string $alias, string $index)
* @method Status ftaliasdel(string $alias)
* @method Status ftaliasupdate(string $alias, string $index)
@@ -0,0 +1,161 @@
<?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 AggregateArguments extends CommonArguments
{
/**
* @var string[]
*/
private $sortingEnum = [
'asc' => 'ASC',
'desc' => 'DESC',
];
/**
* Loads document attributes from the source document.
*
* @param string ...$fields Could be just '*' to load all fields
* @return $this
*/
public function load(string ...$fields): self
{
$arguments = func_get_args();
$this->arguments[] = 'LOAD';
if ($arguments[0] === '*') {
$this->arguments[] = '*';
return $this;
}
$this->arguments[] = count($arguments);
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Loads document attributes from the source document.
*
* @param string ...$properties
* @return $this
*/
public function groupBy(string ...$properties): self
{
$arguments = func_get_args();
array_push($this->arguments, 'GROUPBY', count($arguments));
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Groups the results in the pipeline based on one or more properties.
*
* If you want to add alias property to your argument just add "true" value in arguments enumeration,
* next value will be considered as alias to previous one.
*
* Example: 'argument', true, 'name' => 'argument' AS 'name'
*
* @param string $function
* @param string|bool ...$argument
* @return $this
*/
public function reduce(string $function, ...$argument): self
{
$arguments = func_get_args();
$functionValue = array_shift($arguments);
$argumentsCounter = 0;
for ($i = 0, $iMax = count($arguments); $i < $iMax; $i++) {
if (true === $arguments[$i]) {
$arguments[$i] = 'AS';
$i++;
continue;
}
$argumentsCounter++;
}
array_push($this->arguments, 'REDUCE', $functionValue);
$this->arguments = array_merge($this->arguments, [$argumentsCounter], $arguments);
return $this;
}
/**
* Sorts the pipeline up until the point of SORTBY, using a list of properties.
*
* @param int $max
* @param string ...$properties Enumeration of properties, including sorting direction (ASC, DESC)
* @return $this
*/
public function sortBy(int $max = 0, ...$properties): self
{
$arguments = func_get_args();
$maxValue = array_shift($arguments);
$this->arguments[] = 'SORTBY';
$this->arguments = array_merge($this->arguments, [count($arguments)], $arguments);
if ($maxValue !== 0) {
array_push($this->arguments, 'MAX', $maxValue);
}
return $this;
}
/**
* Applies a 1-to-1 transformation on one or more properties and either stores the result
* as a new property down the pipeline or replaces any property using this transformation.
*
* @param string $expression
* @param string $as
* @return $this
*/
public function apply(string $expression, string $as = ''): self
{
array_push($this->arguments, 'APPLY', $expression);
if ($as !== '') {
array_push($this->arguments, 'AS', $as);
}
return $this;
}
/**
* Scan part of the results with a quicker alternative than LIMIT.
*
* @param int $readSize
* @param int $idleTime
* @return $this
*/
public function withCursor(int $readSize = 0, int $idleTime = 0): self
{
$this->arguments[] = 'WITHCURSOR';
if ($readSize !== 0) {
array_push($this->arguments, 'COUNT', $readSize);
}
if ($idleTime !== 0) {
array_push($this->arguments, 'MAXIDLE', $idleTime);
}
return $this;
}
}
@@ -101,6 +101,77 @@ class CommonArguments implements ArrayableArgument
return $this;
}
/**
* Does not try to use stemming for query expansion but searches the query terms verbatim.
*
* @return $this
*/
public function verbatim(): self
{
$this->arguments[] = 'VERBATIM';
return $this;
}
/**
* Overrides the timeout parameter of the module.
*
* @param int $timeout
* @return $this
*/
public function timeout(int $timeout): self
{
$this->arguments[] = 'TIMEOUT';
$this->arguments[] = $timeout;
return $this;
}
/**
* Adds an arbitrary, binary safe payload that is exposed to custom scoring functions.
*
* @param int $offset
* @param int $num
* @return $this
*/
public function limit(int $offset, int $num): self
{
array_push($this->arguments, 'LIMIT', $offset, $num);
return $this;
}
/**
* Adds filter expression into index.
*
* @param string $filter
* @return $this
*/
public function filter(string $filter): self
{
$this->arguments[] = 'FILTER';
$this->arguments[] = $filter;
return $this;
}
/**
* Defines one or more value parameters. Each parameter has a name and a value.
*
* Example: ['name1', 'value1', 'name2', 'value2'...]
*
* @param array $nameValuesDictionary
* @return $this
*/
public function params(array $nameValuesDictionary): self
{
$this->arguments[] = 'PARAMS';
$this->arguments[] = count($nameValuesDictionary);
$this->arguments = array_merge($this->arguments, $nameValuesDictionary);
return $this;
}
/**
* {@inheritDoc}
*/
@@ -58,20 +58,6 @@ class CreateArguments extends CommonArguments
return $this;
}
/**
* Adds filter expression into index.
*
* @param string $filter
* @return $this
*/
public function filter(string $filter): self
{
$this->arguments[] = 'FILTER';
$this->arguments[] = $filter;
return $this;
}
/**
* Document attribute set as document language.
*
@@ -36,18 +36,6 @@ class SearchArguments extends CommonArguments
return $this;
}
/**
* Does not try to use stemming for query expansion but searches the query terms verbatim.
*
* @return $this
*/
public function verbatim(): self
{
$this->arguments[] = 'VERBATIM';
return $this;
}
/**
* Returns the value of the sorting key, right after the id and score and/or payload, if requested.
*
@@ -237,20 +225,6 @@ class SearchArguments extends CommonArguments
return $this;
}
/**
* Overrides the timeout parameter of the module.
*
* @param int $timeout
* @return $this
*/
public function timeout(int $timeout): self
{
$this->arguments[] = 'TIMEOUT';
$this->arguments[] = $timeout;
return $this;
}
/**
* Puts the query terms in the same order in the document as in the query, regardless of the offsets between them.
* Typically used in conjunction with SLOP.
@@ -329,35 +303,4 @@ class SearchArguments extends CommonArguments
return $this;
}
/**
* Adds an arbitrary, binary safe payload that is exposed to custom scoring functions.
*
* @param int $offset
* @param int $num
* @return $this
*/
public function limit(int $offset, int $num): self
{
array_push($this->arguments, 'LIMIT', $offset, $num);
return $this;
}
/**
* Defines one or more value parameters. Each parameter has a name and a value.
*
* Example: ['name1', 'value1', 'name2', 'value2'...]
*
* @param array $nameValuesDictionary
* @return $this
*/
public function params(array $nameValuesDictionary): self
{
$this->arguments[] = 'PARAMS';
$this->arguments[] = count($nameValuesDictionary);
$this->arguments = array_merge($this->arguments, $nameValuesDictionary);
return $this;
}
}
+59
View File
@@ -0,0 +1,59 @@
<?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.aggregate/
*
* Run a search query on an index, and perform aggregate transformations
* on the results, extracting statistics etc. from them
*/
class FTAGGREGATE extends RedisCommand
{
public function getId()
{
return 'FT.AGGREGATE';
}
public function setArguments(array $arguments)
{
[$index, $query] = $arguments;
$commandArguments = (!empty($arguments[2])) ? $arguments[2]->toArray() : [];
parent::setArguments(array_merge(
[$index, $query],
$commandArguments
));
}
public function parseResponse($data)
{
if (count($data) > 1) {
$result = [$data[0]];
for ($i = 1, $iMax = count($data); $i < $iMax; ++$i) {
for ($j = 0, $jMax = count($data[$i]); $j < $jMax; ++$j) {
if (array_key_exists($j + 1, $data[$i])) {
$result[$i][(string) $data[$i][$j]] = $data[$i][++$j];
}
}
}
return $result;
}
return $data;
}
}
@@ -0,0 +1,189 @@
<?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 AggregateArgumentsTest extends TestCase
{
/**
* @var AggregateArguments
*/
private $arguments;
protected function setUp(): void
{
$this->arguments = new AggregateArguments();
}
/**
* @dataProvider loadProvider
* @param array $arguments
* @param array $expectedResponse
* @return void
*/
public function testCreatesArgumentsWithLoadModifier(array $arguments, array $expectedResponse): void
{
$this->arguments->load(...$arguments);
$this->assertSame($expectedResponse, $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithGroupByModifier(): void
{
$this->arguments->groupBy('property1', 'property2');
$this->assertSame(['GROUPBY', 2, 'property1', 'property2'], $this->arguments->toArray());
}
/**
* @dataProvider reduceProvider
* @param array $arguments
* @param array $expectedResponse
* @return void
*/
public function testCreatesArgumentsWithReduceModifier(array $arguments, array $expectedResponse): void
{
$this->arguments->reduce(...$arguments);
$this->assertSame($expectedResponse, $this->arguments->toArray());
}
/**
* @dataProvider sortByProvider
* @param array $arguments
* @param array $expectedResponse
* @return void
*/
public function testCreatesArgumentsWithSortByModifier(array $arguments, array $expectedResponse): void
{
$this->arguments->sortBy(...$arguments);
$this->assertSame($expectedResponse, $this->arguments->toArray());
}
/**
* @dataProvider applyProvider
* @param array $arguments
* @param array $expectedResponse
* @return void
*/
public function testCreatesArgumentsWithApplyByModifier(array $arguments, array $expectedResponse): void
{
$this->arguments->apply(...$arguments);
$this->assertSame($expectedResponse, $this->arguments->toArray());
}
/**
* @dataProvider withCursorProvider
* @param array $arguments
* @param array $expectedResponse
* @return void
*/
public function testCreatesArgumentsWithWithCursorByModifier(array $arguments, array $expectedResponse): void
{
$this->arguments->withCursor(...$arguments);
$this->assertSame($expectedResponse, $this->arguments->toArray());
}
public function loadProvider(): array
{
return [
'with given fields' => [
['field1', 'field2'],
['LOAD', 2, 'field1', 'field2'],
],
'with all fields' => [
['*'],
['LOAD', '*'],
],
];
}
public function reduceProvider(): array
{
return [
'without aliases' => [
['function', 'arg1', 'arg2'],
['REDUCE', 'function', 2, 'arg1', 'arg2'],
],
'with aliases' => [
['function', 'arg1', true, 'alias1', 'arg2', true, 'alias2'],
['REDUCE', 'function', 2, 'arg1', 'AS', 'alias1', 'arg2', 'AS', 'alias2'],
],
];
}
public function sortByProvider(): array
{
return [
'without sorting direction and max value' => [
[0, 'property1', 'property2'],
['SORTBY', 2, 'property1', 'property2'],
],
'with sorting direction' => [
[0, 'property1', 'ASC', 'property2', 'DESC'],
['SORTBY', 4, 'property1', 'ASC', 'property2', 'DESC'],
],
'with max value' => [
[2, 'property1', 'property2'],
['SORTBY', 2, 'property1', 'property2', 'MAX', 2],
],
'with sorting direction and max value' => [
[2, 'property1', 'ASC', 'property2', 'DESC'],
['SORTBY', 4, 'property1', 'ASC', 'property2', 'DESC', 'MAX', 2],
],
];
}
public function applyProvider(): array
{
return [
'with default arguments' => [
['expression'],
['APPLY', 'expression'],
],
'with alias' => [
['expression', 'name'],
['APPLY', 'expression', 'AS', 'name'],
],
];
}
public function withCursorProvider(): array
{
return [
'with default arguments' => [
[],
['WITHCURSOR'],
],
'with readSize argument' => [
[2],
['WITHCURSOR', 'COUNT', 2],
],
'with maxIdle argument' => [
[0, 2],
['WITHCURSOR', 'MAXIDLE', 2],
],
'with readSize and maxIdle arguments' => [
[3, 2],
['WITHCURSOR', 'COUNT', 3, 'MAXIDLE', 2],
],
];
}
}
@@ -85,4 +85,54 @@ class CommonArgumentsTest extends TestCase
$this->assertSame(['WITHPAYLOADS'], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithVerbatimModifier(): void
{
$this->arguments->verbatim();
$this->assertSame(['VERBATIM'], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithTimeoutModifier(): void
{
$this->arguments->timeout(2);
$this->assertSame(['TIMEOUT', 2], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithLimitModifier(): void
{
$this->arguments->limit(2, 2);
$this->assertSame(['LIMIT', 2, 2], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithFilterModifier(): void
{
$this->arguments->filter('@age>16');
$this->assertSame(['FILTER', '@age>16'], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithParamsModifier(): void
{
$this->arguments->params(['name1', 'value1', 'name2', 'value2']);
$this->assertSame(['PARAMS', 4, 'name1', 'value1', 'name2', 'value2'], $this->arguments->toArray());
}
}
@@ -58,16 +58,6 @@ class CreateArgumentsTest extends TestCase
$this->assertSame(['PREFIX', 2, 'prefix:', 'prefix1:'], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithFilterModifier(): void
{
$this->arguments->filter('@age>16');
$this->assertSame(['FILTER', '@age>16'], $this->arguments->toArray());
}
/**
* @return void
*/
@@ -37,16 +37,6 @@ class SearchArgumentsTest extends TestCase
$this->assertSame(['NOCONTENT'], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithVerbatimModifier(): void
{
$this->arguments->verbatim();
$this->assertSame(['VERBATIM'], $this->arguments->toArray());
}
/**
* @return void
*/
@@ -155,16 +145,6 @@ class SearchArgumentsTest extends TestCase
$this->assertSame(['SLOP', 2], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithTimeoutModifier(): void
{
$this->arguments->timeout(2);
$this->assertSame(['TIMEOUT', 2], $this->arguments->toArray());
}
/**
* @return void
*/
@@ -229,26 +209,6 @@ class SearchArgumentsTest extends TestCase
$this->arguments->sortBy('sort_attribute', 'wrong');
}
/**
* @return void
*/
public function testCreatesArgumentsWithLimitModifier(): void
{
$this->arguments->limit(2, 2);
$this->assertSame(['LIMIT', 2, 2], $this->arguments->toArray());
}
/**
* @return void
*/
public function testCreatesArgumentsWithParamsModifier(): void
{
$this->arguments->params(['name1', 'value1', 'name2', 'value2']);
$this->assertSame(['PARAMS', 4, 'name1', 'value1', 'name2', 'value2'], $this->arguments->toArray());
}
/**
* @return void
*/
@@ -0,0 +1,239 @@
<?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\AggregateArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\SchemaFields\AbstractField;
use Predis\Command\Argument\Search\SchemaFields\NumericField;
use Predis\Command\Argument\Search\SchemaFields\TextField;
use Predis\Command\Redis\PredisCommandTestCase;
use Predis\Response\ServerException;
class FTAGGREGATE_Test extends PredisCommandTestCase
{
/**
* {@inheritDoc}
*/
protected function getExpectedCommand(): string
{
return FTAGGREGATE::class;
}
/**
* {@inheritDoc}
*/
protected function getExpectedId(): string
{
return 'FTAGGREGATE';
}
/**
* @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
* @dataProvider responsesProvider
*/
public function testParseResponse(array $actualResponse, array $expectedResponse): void
{
$this->assertSame($expectedResponse, $this->getCommand()->parseResponse($actualResponse));
}
/**
* @group connected
* @return void
* @requiresRediSearchVersion >= 1.1.0
*/
public function testReturnsAggregatedSearchResultWithGivenModifiers(): void
{
$redis = $this->getClient();
$expectedResponse = [
2,
[
'country' => 'Ukraine',
'birth' => '1995',
'country_birth_Vlad_count' => '2',
],
[
'country' => 'Israel',
'birth' => '1994',
'country_birth_Vlad_count' => '1',
],
];
$ftCreateArguments = (new CreateArguments())->prefix(['user:']);
$schema = [
new TextField('name'),
new TextField('country'),
new NumericField('dob', '', AbstractField::SORTABLE),
];
$this->assertEquals('OK', $redis->ftcreate('idx', $schema, $ftCreateArguments));
$this->assertSame(
3,
$redis->hset('user:0', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600)
);
$this->assertSame(
3,
$redis->hset('user:1', 'name', 'Vlad', 'country', 'Israel', 'dob', 782265600)
);
$this->assertSame(
3,
$redis->hset('user:2', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600)
);
$ftAggregateArguments = (new AggregateArguments())
->apply('year(@dob)', 'birth')
->groupBy('@country', '@birth')
->reduce('COUNT', true, 'country_birth_Vlad_count')
->sortBy(0, '@birth', 'DESC');
$this->assertSame(
$expectedResponse,
$redis->ftaggregate('idx', '@name: "Vlad"', $ftAggregateArguments)
);
}
/**
* @group connected
* @return void
* @requiresRediSearchVersion >= 1.1.0
*/
public function testThrowsExceptionOnNonExistingIndex(): void
{
$redis = $this->getClient();
$this->expectException(ServerException::class);
$this->expectExceptionMessage('index: no such index');
$redis->ftaggregate('index', 'query');
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
['index', 'query'],
['index', 'query'],
],
'with VERBATIM modifier' => [
['index', 'query', (new AggregateArguments())->verbatim()],
['index', 'query', 'VERBATIM'],
],
'with LOAD modifier - specified fields' => [
['index', 'query', (new AggregateArguments())->load('field1', 'field2')],
['index', 'query', 'LOAD', 2, 'field1', 'field2'],
],
'with LOAD modifier - all fields' => [
['index', 'query', (new AggregateArguments())->load('*')],
['index', 'query', 'LOAD', '*'],
],
'with TIMEOUT modifier' => [
['index', 'query', (new AggregateArguments())->timeout(2)],
['index', 'query', 'TIMEOUT', 2],
],
'with GROUPBY modifier' => [
['index', 'query', (new AggregateArguments())->groupBy('property1', 'property2')],
['index', 'query', 'GROUPBY', 2, 'property1', 'property2'],
],
'with REDUCE modifier' => [
['index', 'query', (new AggregateArguments())->reduce('function', 'arg1', true, 'alias1', 'arg2')],
['index', 'query', 'REDUCE', 'function', 2, 'arg1', 'AS', 'alias1', 'arg2'],
],
'with SORTBY modifier' => [
['index', 'query', (new AggregateArguments())->sortBy(2, 'property1', 'ASC', 'property2', 'DESC')],
['index', 'query', 'SORTBY', 2, 'property1', 'ASC', 'property2', 'DESC', 'MAX', 2],
],
'with APPLY modifier' => [
['index', 'query', (new AggregateArguments())->apply('expression', 'name')],
['index', 'query', 'APPLY', 'expression', 'AS', 'name'],
],
'with LIMIT modifier' => [
['index', 'query', (new AggregateArguments())->limit(2, 3)],
['index', 'query', 'LIMIT', 2, 3],
],
'with FILTER modifier' => [
['index', 'query', (new AggregateArguments())->filter('filter')],
['index', 'query', 'FILTER', 'filter'],
],
'with WITHCURSOR modifier' => [
['index', 'query', (new AggregateArguments())->withCursor(10, 20)],
['index', 'query', 'WITHCURSOR', 'COUNT', 10, 'MAXIDLE', 20],
],
'with PARAMS modifier' => [
['index', 'query', (new AggregateArguments())->params(['name1', 'value1', 'name2', 'value2'])],
['index', 'query', 'PARAMS', 4, 'name1', 'value1', 'name2', 'value2'],
],
'with DIALECT modifier' => [
['index', 'query', (new AggregateArguments())->dialect('dialect')],
['index', 'query', 'DIALECT', 'dialect'],
],
'with chain of arguments' => [
[
'index',
'@name: "test"',
(new AggregateArguments())
->apply('year(@dob)', 'birth')
->groupBy('@birth', '@country')
->reduce('COUNT', true, 'num_visits')
->sortBy(0, '@day'),
],
[
'index', '@name: "test"', 'APPLY', 'year(@dob)', 'AS', 'birth', 'GROUPBY', 2, '@birth', '@country',
'REDUCE', 'COUNT', 0, 'AS', 'num_visits', 'SORTBY', 1, '@day',
],
],
];
}
public function responsesProvider(): array
{
return [
'with one element response' => [
[100],
[100],
],
'with many elements response' => [
[
1,
[
'Country',
'Ukraine',
'Birth',
'1995',
'Count',
2,
],
],
[
1,
[
'Country' => 'Ukraine',
'Birth' => '1995',
'Count' => 2,
],
],
],
];
}
}