From 283664e6f39ab7727b0e57efcb479055e54ad30b Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 14:05:39 +0300 Subject: [PATCH 1/7] first pass of geoshape index type --- redis/commands/search/field.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/redis/commands/search/field.py b/redis/commands/search/field.py index 6f31ce1fc2..77630df84d 100644 --- a/redis/commands/search/field.py +++ b/redis/commands/search/field.py @@ -14,6 +14,7 @@ class Field: SORTABLE = "SORTABLE" NOINDEX = "NOINDEX" AS = "AS" + GEOSHAPE = "GEOSHAPE" def __init__( self, @@ -92,6 +93,21 @@ def __init__(self, name: str, **kwargs): Field.__init__(self, name, args=[Field.NUMERIC], **kwargs) +class GeoShapeField(Field): + """ + GeoShapeField is used to enable within/contain indexing/searching + """ + + SPHERICAL = "SPHERICAL" + FLAT = "FLAT" + + def __init__(self, name: str, coord_system=None, **kwargs): + args = [Field.GEOSHAPE] + if coord_system: + args.extend(["COORD_SYSTEM", coord_system]) + Field.__init__(self, name, args=args, **kwargs) + + class GeoField(Field): """ GeoField is used to define a geo-indexing field in a schema definition From aec64dc87deb565def35968448911fc301ad4a80 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 14:27:41 +0300 Subject: [PATCH 2/7] first attempt at test, but demonstrates the initial commit is broken --- tests/test_search.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_search.py b/tests/test_search.py index 7612332470..12bf29497c 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -17,6 +17,7 @@ TagField, TextField, VectorField, + GeoShapeField, ) from redis.commands.search.indexDefinition import IndexDefinition, IndexType from redis.commands.search.query import GeoFilter, NumericFilter, Query @@ -2269,3 +2270,12 @@ def test_query_timeout(r: redis.Redis): q2 = Query("foo").timeout("not_a_number") with pytest.raises(redis.ResponseError): r.ft().search(q2) + + +def test_geoshape(client: redis.Redis): + client.ft().create_index((GeoShapeField("geom", GeoShapeField.FLAT))) + waitForIndex(client, getattr(client.ft(), "index_name", "idx")) + client.hset("small", "geom", 'POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))') + client.hset("large", "geom", 'POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))') + result = client.ft().search('@geom:[WITHIN $poly]', {'poly': 'POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))'}) + result = client.ft().search('@geom:[CONTAINS $poly]', {'poly': 'POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))'}) From 3b58c35e4c859806c742a6cc6c8630ba8fd65869 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 16:25:23 +0300 Subject: [PATCH 3/7] fix new field + fix tests --- redis/commands/search/field.py | 2 +- tests/test_search.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/redis/commands/search/field.py b/redis/commands/search/field.py index 77630df84d..7e0cef4ef3 100644 --- a/redis/commands/search/field.py +++ b/redis/commands/search/field.py @@ -104,7 +104,7 @@ class GeoShapeField(Field): def __init__(self, name: str, coord_system=None, **kwargs): args = [Field.GEOSHAPE] if coord_system: - args.extend(["COORD_SYSTEM", coord_system]) + args.append(coord_system) Field.__init__(self, name, args=args, **kwargs) diff --git a/tests/test_search.py b/tests/test_search.py index 12bf29497c..a66ba06b91 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2277,5 +2277,12 @@ def test_geoshape(client: redis.Redis): waitForIndex(client, getattr(client.ft(), "index_name", "idx")) client.hset("small", "geom", 'POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))') client.hset("large", "geom", 'POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))') - result = client.ft().search('@geom:[WITHIN $poly]', {'poly': 'POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))'}) - result = client.ft().search('@geom:[CONTAINS $poly]', {'poly': 'POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))'}) + q1 = Query("@geom:[WITHIN $poly]").dialect(3) + qp1 = {'poly': 'POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))'} + q2 = Query("@geom:[CONTAINS $poly]").dialect(3) + qp2 = {'poly': 'POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))'} + result = client.ft().search(q1, query_params=qp1) + assert len(result.docs) == 1 + assert result.docs[0]["id"] == "small" + result = client.ft().search(q2, query_params=qp2) + assert len(result.docs) == 2 From a3f1e59282cb408d64e3c63ff6491fbd7971c3f2 Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 16:28:13 +0300 Subject: [PATCH 4/7] work on linter --- tests/test_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index a66ba06b91..80852d0b9f 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -13,11 +13,11 @@ from redis.commands.search import Search from redis.commands.search.field import ( GeoField, + GeoShapeField, NumericField, TagField, TextField, VectorField, - GeoShapeField, ) from redis.commands.search.indexDefinition import IndexDefinition, IndexType from redis.commands.search.query import GeoFilter, NumericFilter, Query From ffab846d269b621fe8254a2750304c396604a6de Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 16:32:35 +0300 Subject: [PATCH 5/7] more linter --- tests/test_search.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_search.py b/tests/test_search.py index 1e6a9a12a9..735eaa3627 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2272,12 +2272,12 @@ def test_query_timeout(r: redis.Redis): def test_geoshape(client: redis.Redis): client.ft().create_index((GeoShapeField("geom", GeoShapeField.FLAT))) waitForIndex(client, getattr(client.ft(), "index_name", "idx")) - client.hset("small", "geom", 'POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))') - client.hset("large", "geom", 'POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))') + client.hset("small", "geom", "POLYGON((1 1, 1 100, 100 100, 100 1, 1 1))") + client.hset("large", "geom", "POLYGON((1 1, 1 200, 200 200, 200 1, 1 1))") q1 = Query("@geom:[WITHIN $poly]").dialect(3) - qp1 = {'poly': 'POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))'} + qp1 = {"poly": "POLYGON((0 0, 0 150, 150 150, 150 0, 0 0))"} q2 = Query("@geom:[CONTAINS $poly]").dialect(3) - qp2 = {'poly': 'POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))'} + qp2 = {"poly": "POLYGON((2 2, 2 50, 50 50, 50 2, 2 2))"} result = client.ft().search(q1, query_params=qp1) assert len(result.docs) == 1 assert result.docs[0]["id"] == "small" From 28ab410f64f8a45150f9238686de5069b8cda90f Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 16:45:09 +0300 Subject: [PATCH 6/7] try to mark test with correct fixture --- tests/test_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index 735eaa3627..388f3fc4ee 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2268,7 +2268,7 @@ def test_query_timeout(r: redis.Redis): with pytest.raises(redis.ResponseError): r.ft().search(q2) - +@pytest.mark.redismod def test_geoshape(client: redis.Redis): client.ft().create_index((GeoShapeField("geom", GeoShapeField.FLAT))) waitForIndex(client, getattr(client.ft(), "index_name", "idx")) From 7c9c6bc4ea892d6ca2be2cc21dbfe1a9a53ebffd Mon Sep 17 00:00:00 2001 From: Shaya Potter Date: Thu, 21 Sep 2023 16:48:44 +0300 Subject: [PATCH 7/7] fix linter --- tests/test_search.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_search.py b/tests/test_search.py index 388f3fc4ee..7469123453 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2268,6 +2268,7 @@ def test_query_timeout(r: redis.Redis): with pytest.raises(redis.ResponseError): r.ft().search(q2) + @pytest.mark.redismod def test_geoshape(client: redis.Redis): client.ft().create_index((GeoShapeField("geom", GeoShapeField.FLAT)))