Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8d369f4

Browse filesBrowse files
Implement GEOSEARCH[STORE] BYPOLYGON.
Valkey 9.0.0 implemented a new variant of `GEOSEARCH` where you supply the verticies to an arbitrary polygon. Since we can't modify the `geosearch` prototype using it is a little wonky (you need to just pass empty strings for position and unit). ```php $redis->geosearch('ca:cities', '', [ -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80 ], ''); $redis->geosearchstore('ca:cities', 'dst', '', [ -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80 ], ''); ```
1 parent b1b0c19 commit 8d369f4
Copy full SHA for 8d369f4

4 files changed

+138-49Lines changed: 138 additions & 49 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎redis_commands.c‎

Copy file name to clipboardExpand all lines: redis_commands.c
+106-49Lines changed: 106 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4475,38 +4475,56 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
44754475
char **cmd, int *cmd_len, short *slot, void **ctx)
44764476
{
44774477
char *key, *unit;
4478-
int argc = 2;
4478+
int argc = 0;
44794479
size_t keylen, unitlen;
44804480
geoOptions gopts = {0};
44814481
smart_string cmdstr = {0};
44824482
zval *position, *shape, *opts = NULL, *z_ele;
44834483
zend_string *zkey, *zstr;
4484+
zend_bool bypolygon = 0;
4485+
HashTable *ht;
44844486

4485-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a",
4486-
&key, &keylen, &position, &shape,
4487-
&unit, &unitlen, &opts) == FAILURE)
4487+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", &key, &keylen,
4488+
&position, &shape, &unit, &unitlen,
4489+
&opts) == FAILURE)
44884490
{
44894491
return FAILURE;
44904492
}
44914493

4492-
if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
4493-
argc += 2;
4494-
} else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) {
4495-
argc += 3;
4496-
} else {
4497-
php_error_docref(NULL, E_WARNING, "Invalid position");
4498-
return FAILURE;
4499-
}
4500-
45014494
if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) {
45024495
argc += 2;
4503-
} else if (Z_TYPE_P(shape) == IS_ARRAY) {
4496+
} else if (Z_TYPE_P(shape) == IS_ARRAY &&
4497+
zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2)
4498+
{
4499+
// BYBOX
45044500
argc += 3;
4501+
} else if (Z_TYPE_P(shape) == IS_ARRAY &&
4502+
zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 &&
4503+
zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0)
4504+
{
4505+
// BYPOLYGON N verticies
4506+
argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape));
4507+
bypolygon = 1;
45054508
} else {
45064509
php_error_docref(NULL, E_WARNING, "Invalid shape dimensions");
45074510
return FAILURE;
45084511
}
45094512

4513+
if (!bypolygon) {
4514+
if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
4515+
argc += 2;
4516+
} else if (Z_TYPE_P(position) == IS_ARRAY &&
4517+
zend_hash_num_elements(Z_ARRVAL_P(position)) == 2)
4518+
{
4519+
argc += 3;
4520+
} else {
4521+
php_error_docref(NULL, E_WARNING, "Invalid position");
4522+
return FAILURE;
4523+
}
4524+
}
4525+
4526+
argc += bypolygon ? 1 : 2;
4527+
45104528
/* Attempt to parse our options array */
45114529
if (opts != NULL) {
45124530
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
@@ -4539,28 +4557,40 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
45394557
REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH");
45404558
redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot);
45414559

4542-
if (Z_TYPE_P(position) == IS_ARRAY) {
4543-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
4544-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
4545-
ZVAL_DEREF(z_ele);
4546-
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
4547-
} ZEND_HASH_FOREACH_END();
4548-
} else {
4549-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
4550-
redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
4560+
if (!bypolygon) {
4561+
if (Z_TYPE_P(position) == IS_ARRAY) {
4562+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
4563+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
4564+
ZVAL_DEREF(z_ele);
4565+
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
4566+
} ZEND_HASH_FOREACH_END();
4567+
} else {
4568+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
4569+
redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
4570+
}
45514571
}
45524572

45534573
if (Z_TYPE_P(shape) == IS_ARRAY) {
4554-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
4555-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) {
4574+
ht = Z_ARRVAL_P(shape);
4575+
if (bypolygon) {
4576+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON");
4577+
redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2);
4578+
} else {
4579+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
4580+
}
4581+
4582+
ZEND_HASH_FOREACH_VAL(ht, z_ele) {
45564583
ZVAL_DEREF(z_ele);
45574584
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
45584585
} ZEND_HASH_FOREACH_END();
45594586
} else {
45604587
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS");
45614588
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape));
45624589
}
4563-
redis_cmd_append_sstr(&cmdstr, unit, unitlen);
4590+
4591+
if (!bypolygon) {
4592+
redis_cmd_append_sstr(&cmdstr, unit, unitlen);
4593+
}
45644594

45654595
/* Append optional arguments */
45664596
if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD");
@@ -4597,14 +4627,16 @@ int
45974627
redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
45984628
char **cmd, int *cmd_len, short *slot, void **ctx)
45994629
{
4600-
int argc = 3;
46014630
char *dest, *src, *unit;
46024631
size_t destlen, srclen, unitlen;
46034632
geoOptions gopts = {0};
46044633
smart_string cmdstr = {0};
46054634
zval *position, *shape, *opts = NULL, *z_ele;
4635+
zend_bool bypolygon = 0;
46064636
zend_string *zkey;
4637+
HashTable *ht;
46074638
short s2 = 0;
4639+
int argc = 0;
46084640

46094641
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a",
46104642
&dest, &destlen, &src, &srclen, &position, &shape,
@@ -4613,24 +4645,38 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
46134645
return FAILURE;
46144646
}
46154647

4616-
if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
4617-
argc += 2;
4618-
} else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) {
4619-
argc += 3;
4620-
} else {
4621-
php_error_docref(NULL, E_WARNING, "Invalid position");
4622-
return FAILURE;
4623-
}
4624-
46254648
if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) {
46264649
argc += 2;
4627-
} else if (Z_TYPE_P(shape) == IS_ARRAY) {
4650+
} else if (Z_TYPE_P(shape) == IS_ARRAY &&
4651+
zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2)
4652+
{
46284653
argc += 3;
4654+
} else if (Z_TYPE_P(shape) == IS_ARRAY &&
4655+
zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 &&
4656+
zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0)
4657+
{
4658+
argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape));
4659+
bypolygon = 1;
46294660
} else {
46304661
php_error_docref(NULL, E_WARNING, "Invalid shape dimensions");
46314662
return FAILURE;
46324663
}
46334664

4665+
argc += bypolygon ? 2 : 3;
4666+
4667+
if (!bypolygon) {
4668+
if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) {
4669+
argc += 2;
4670+
} else if (Z_TYPE_P(position) == IS_ARRAY &&
4671+
zend_hash_num_elements(Z_ARRVAL_P(position)) == 2)
4672+
{
4673+
argc += 3;
4674+
} else {
4675+
php_error_docref(NULL, E_WARNING, "Invalid position");
4676+
return FAILURE;
4677+
}
4678+
}
4679+
46344680
/* Attempt to parse our options array */
46354681
if (opts != NULL) {
46364682
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
@@ -4671,28 +4717,39 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
46714717
return FAILURE;
46724718
}
46734719

4674-
if (Z_TYPE_P(position) == IS_ARRAY) {
4675-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
4676-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
4677-
ZVAL_DEREF(z_ele);
4678-
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
4679-
} ZEND_HASH_FOREACH_END();
4680-
} else {
4681-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
4682-
redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
4720+
if (!bypolygon) {
4721+
if (Z_TYPE_P(position) == IS_ARRAY) {
4722+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT");
4723+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) {
4724+
ZVAL_DEREF(z_ele);
4725+
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
4726+
} ZEND_HASH_FOREACH_END();
4727+
} else {
4728+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER");
4729+
redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position));
4730+
}
46834731
}
46844732

46854733
if (Z_TYPE_P(shape) == IS_ARRAY) {
4686-
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
4687-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) {
4734+
ht = Z_ARRVAL_P(shape);
4735+
if (bypolygon) {
4736+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON");
4737+
redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2);
4738+
} else {
4739+
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX");
4740+
}
4741+
ZEND_HASH_FOREACH_VAL(ht, z_ele) {
46884742
ZVAL_DEREF(z_ele);
46894743
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele));
46904744
} ZEND_HASH_FOREACH_END();
46914745
} else {
46924746
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS");
46934747
redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape));
46944748
}
4695-
redis_cmd_append_sstr(&cmdstr, unit, unitlen);
4749+
4750+
if (!bypolygon) {
4751+
redis_cmd_append_sstr(&cmdstr, unit, unitlen);
4752+
}
46964753

46974754
/* Append sort if it's not GEO_NONE */
46984755
if (gopts.sort == SORT_ASC) {
Collapse file

‎tests/RedisClusterTest.php‎

Copy file name to clipboardExpand all lines: tests/RedisClusterTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ public function setUp() {
137137
$this->version = $info['redis_version'] ?? '0.0.0';
138138
$this->is_keydb = $this->detectKeyDB($info);
139139
$this->is_valkey = $this->detectValkey($info);
140+
$this->valkey_version = $info['valkey_version'] ?? '0.0.0';
140141
}
141142

142143
private function findCliExe() {
Collapse file

‎tests/RedisTest.php‎

Copy file name to clipboardExpand all lines: tests/RedisTest.php
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public function setUp() {
7777
$this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
7878
$this->is_keydb = $this->detectKeyDB($info);
7979
$this->is_valkey = $this->detectValKey($info);
80+
$this->valkey_version = $info['valkey_version'] ?? '0.0.0';
8081
}
8182

8283
protected function haveCommand(string $cmd): bool {
@@ -90,6 +91,10 @@ protected function minVersionCheck($version) {
9091
return version_compare($this->version, $version) >= 0;
9192
}
9293

94+
protected function minValkeyVersionCheck($version) {
95+
return $this->is_valkey && version_compare($this->valkey_version, $version) >= 0;
96+
}
97+
9398
protected function mstime() {
9499
return round(microtime(true)*1000);
95100
}
@@ -6913,6 +6918,31 @@ public function testGeoSearch() {
69136918
});
69146919
}
69156920

6921+
public function testGeoSearchByPolygon() {
6922+
if ( ! $this->minValkeyVersionCheck('9.0.0'))
6923+
$this->markTestSkipped();
6924+
6925+
$this->addCities('ca:cities');
6926+
6927+
$res = $this->redis->geosearch('ca:cities', '', [
6928+
-121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80
6929+
], '');
6930+
6931+
$this->assertEquals(['Chico'], $res);
6932+
}
6933+
6934+
public function testGeoSearchStoreByPolygon() {
6935+
if ( ! $this->minValkeyVersionCheck('9.0.0'))
6936+
$this->markTestSkipped();
6937+
6938+
$this->addCities('ca:cities');
6939+
$this->assertEquals(1, $this->redis->geosearchstore('{gk}dst', 'ca:cities', '', [
6940+
-121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80
6941+
], ''));
6942+
6943+
$this->assertEquals(['Chico'], $this->redis->geosearch('{gk}dst', 'Chico', 1, 'm'));
6944+
}
6945+
69166946
public function testGeoSearchStore() {
69176947
if ( ! $this->minVersionCheck('6.2.0'))
69186948
$this->markTestSkipped();
Collapse file

‎tests/TestSuite.php‎

Copy file name to clipboardExpand all lines: tests/TestSuite.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class TestSuite
1515

1616
/* Redis server version */
1717
protected $version;
18+
protected string $valkey_version;
1819
protected bool $is_keydb;
1920
protected bool $is_valkey;
2021

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.