Added GEOSHAPE field and test polygon search (#1467)

* Added GEOSHAPE field and test polygon search

* Added version restriction for test

* Added edge to stack amtrix

* Added same values assertion

* Codestyle changes
This commit is contained in:
Vladyslav Vildanov
2024-07-05 16:16:16 +03:00
committed by GitHub
parent 5d22ed775a
commit 8c2aac4f66
3 changed files with 237 additions and 0 deletions
@@ -0,0 +1,57 @@
<?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\SchemaFields;
class GeoShapeField extends AbstractField
{
public const COORD_FLAT = 'FLAT';
/**
* @param string $identifier
* @param string $alias
* @param bool|string $sortable
* @param bool $noIndex
* @param string|null $coordSystem Constants that represents available systems available on a class level.
*/
public function __construct(
string $identifier,
string $alias = '',
$sortable = self::NOT_SORTABLE,
bool $noIndex = false,
?string $coordSystem = null
) {
$this->fieldArguments[] = $identifier;
if ($alias !== '') {
$this->fieldArguments[] = 'AS';
$this->fieldArguments[] = $alias;
}
$this->fieldArguments[] = 'GEOSHAPE';
if (null !== $coordSystem) {
$this->fieldArguments[] = $coordSystem;
}
if ($sortable === self::SORTABLE) {
$this->fieldArguments[] = 'SORTABLE';
} elseif ($sortable === self::SORTABLE_UNF) {
$this->fieldArguments[] = 'SORTABLE';
$this->fieldArguments[] = 'UNF';
}
if ($noIndex) {
$this->fieldArguments[] = 'NOINDEX';
}
}
}
@@ -0,0 +1,61 @@
<?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\SchemaFields;
use PHPUnit\Framework\TestCase;
class GeoShapeFieldTest extends TestCase
{
/**
* @dataProvider geoFieldsProvider
* @param array $arguments
* @param array $expectedSchema
* @return void
*/
public function testReturnsCorrectFieldArgumentsArray(
array $arguments,
array $expectedSchema
): void {
$this->assertSame($expectedSchema, (new GeoShapeField(...$arguments))->toArray());
}
public function geoFieldsProvider(): array
{
return [
'with default arguments' => [
['field_name'],
['field_name', 'GEOSHAPE'],
],
'with alias' => [
['field_name', 'fn'],
['field_name', 'AS', 'fn', 'GEOSHAPE'],
],
'with sortable - no UNF' => [
['field_name', '', AbstractField::SORTABLE],
['field_name', 'GEOSHAPE', 'SORTABLE'],
],
'with sortable - with UNF' => [
['field_name', '', AbstractField::SORTABLE_UNF],
['field_name', 'GEOSHAPE', 'SORTABLE', 'UNF'],
],
'with NOINDEX modifier' => [
['field_name', '', AbstractField::NOT_SORTABLE, true],
['field_name', 'GEOSHAPE', 'NOINDEX'],
],
'with FLAT modifier' => [
['field_name', '', AbstractField::NOT_SORTABLE, false, GeoShapeField::COORD_FLAT],
['field_name', 'GEOSHAPE', 'FLAT'],
],
];
}
}
@@ -13,6 +13,8 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\SchemaFields\AbstractField;
use Predis\Command\Argument\Search\SchemaFields\GeoShapeField;
use Predis\Command\Argument\Search\SchemaFields\NumericField;
use Predis\Command\Argument\Search\SchemaFields\TextField;
use Predis\Command\Argument\Search\SearchArguments;
@@ -125,6 +127,123 @@ class FTSEARCH_Test extends PredisCommandTestCase
$this->assertSame($expectedResponse, $actualResponse);
}
/**
* @group connected
* @group relay-resp3
* @return void
* @requiresRediSearchVersion >= 2.9.0
*/
public function testGeoSearchQueriesIntersectsAndDisjoint(): void
{
$redis = $this->getClient();
$redis->hset('geo:doc_point1', 'g', 'POINT (10 10)');
$redis->hset('geo:doc_point2', 'g', 'POINT (50 50)');
$redis->hset('geo:doc_polygon1', 'g', 'POLYGON ((20 20, 25 35, 35 25, 20 20))');
$redis->hset('geo:doc_polygon2', 'g', 'POLYGON ((60 60, 65 75, 70 70, 65 55, 60 60))');
$ftCreateArguments = new CreateArguments();
$ftCreateArguments->prefix(['geo:']);
$schema = [
new GeoShapeField('g', '', AbstractField::NOT_SORTABLE, false, GeoShapeField::COORD_FLAT),
];
$ftCreateResponse = $redis->ftcreate('idx_geo', $schema, $ftCreateArguments);
$this->assertEquals('OK', $ftCreateResponse);
$ftSearchArguments = new SearchArguments();
$ftSearchArguments->params(['shape', 'POLYGON ((15 15, 75 15, 50 70, 20 40, 15 15))']);
$ftSearchArguments->noContent();
$ftSearchArguments->dialect(3);
$actualResponse = $redis->ftsearch('idx_geo', '@g:[intersects $shape]', $ftSearchArguments);
$this->assertSameValues(
[
2,
'geo:doc_polygon1',
'geo:doc_point2',
], $actualResponse
);
$actualResponse = $redis->ftsearch('idx_geo', '@g:[disjoint $shape]', $ftSearchArguments);
$this->assertSameValues(
[
2,
'geo:doc_polygon2',
'geo:doc_point1',
], $actualResponse
);
}
/**
* @group connected
* @group relay-resp3
* @return void
* @requiresRediSearchVersion >= 2.9.0
*/
public function testGeoSearchQueriesContainsAndWithin(): void
{
$redis = $this->getClient();
$redis->hset('geo:doc_point1', 'g', 'POINT (10 10)');
$redis->hset('geo:doc_point2', 'g', 'POINT (50 50)');
$redis->hset('geo:doc_polygon1', 'g', 'POLYGON ((20 20, 25 35, 35 25, 20 20))');
$redis->hset('geo:doc_polygon2', 'g', 'POLYGON ((60 60, 65 75, 70 70, 65 55, 60 60))');
$ftCreateArguments = new CreateArguments();
$ftCreateArguments->prefix(['geo:']);
$schema = [
new GeoShapeField('g', '',
AbstractField::NOT_SORTABLE, false, GeoShapeField::COORD_FLAT
),
];
$ftCreateResponse = $redis->ftcreate('idx_geo', $schema, $ftCreateArguments);
$this->assertEquals('OK', $ftCreateResponse);
$ftSearchArguments = new SearchArguments();
$ftSearchArguments->params(['shape', 'POINT(25 25)']);
$ftSearchArguments->noContent();
$ftSearchArguments->dialect(3);
$actualResponse = $redis->ftsearch('idx_geo', '@g:[contains $shape]', $ftSearchArguments);
$this->assertSameValues(
[
1,
'geo:doc_polygon1',
], $actualResponse
);
$ftSearchArguments = new SearchArguments();
$ftSearchArguments->params(['shape', 'POLYGON((24 24, 24 26, 25 25, 24 24))']);
$ftSearchArguments->noContent();
$ftSearchArguments->dialect(3);
$actualResponse = $redis->ftsearch('idx_geo', '@g:[contains $shape]', $ftSearchArguments);
$this->assertSameValues(
[
1,
'geo:doc_polygon1',
], $actualResponse
);
$ftSearchArguments = new SearchArguments();
$ftSearchArguments->params(['shape', 'POLYGON((15 15, 75 15, 50 70, 20 40, 15 15))']);
$ftSearchArguments->noContent();
$ftSearchArguments->dialect(3);
$actualResponse = $redis->ftsearch('idx_geo', '@g:[within $shape]', $ftSearchArguments);
$this->assertSameValues(
[
2,
'geo:doc_polygon1',
'geo:doc_point2',
], $actualResponse
);
}
public function argumentsProvider(): array
{
return [