From 23dbb0d571e9c2edacc3cbe9c20a5e1c53fdd7a3 Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Mon, 30 Dec 2024 12:19:29 +0200 Subject: [PATCH 1/7] Added Redis 8.0 to test matrix --- .github/workflows/integration.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 7c74de5290..c32029e6f9 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -74,7 +74,7 @@ jobs: max-parallel: 15 fail-fast: false matrix: - redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}', '7.2.6', '6.2.16'] + redis-version: ['8.0-M02', '${{ needs.redis_version.outputs.CURRENT }}', '7.2.6', '6.2.16'] python-version: ['3.8', '3.12'] parser-backend: ['plain'] event-loop: ['asyncio'] From a82559eb886904deb31f4b953c78d32d2f0094bc Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Tue, 14 Jan 2025 12:30:22 +0200 Subject: [PATCH 2/7] Fixed test cases --- tests/test_asyncio/test_search.py | 15 ++++++++++++++- tests/test_commands.py | 26 ++++++++++++++++++++++++++ tests/test_search.py | 19 ++++++++++++++++--- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/tests/test_asyncio/test_search.py b/tests/test_asyncio/test_search.py index cc75e4b4a4..19bd1a572c 100644 --- a/tests/test_asyncio/test_search.py +++ b/tests/test_asyncio/test_search.py @@ -27,7 +27,7 @@ is_resp2_connection, skip_if_redis_enterprise, skip_if_resp_version, - skip_ifmodversion_lt, + skip_ifmodversion_lt, skip_if_server_version_gte, skip_if_server_version_lt, ) WILL_PLAY_TEXT = os.path.abspath( @@ -1111,6 +1111,7 @@ async def test_get(decoded_r: redis.Redis): @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_ifmodversion_lt("2.2.0", "search") +@skip_if_server_version_gte("7.9.0") async def test_config(decoded_r: redis.Redis): assert await decoded_r.ft().config_set("TIMEOUT", "100") with pytest.raises(redis.ResponseError): @@ -1120,6 +1121,18 @@ async def test_config(decoded_r: redis.Redis): res = await decoded_r.ft().config_get("TIMEOUT") assert "100" == res["TIMEOUT"] +@pytest.mark.redismod +@pytest.mark.onlynoncluster +@skip_if_server_version_lt("7.9.0") +async def test_config_with_removed_ftconfig(decoded_r: redis.Redis): + assert await decoded_r.config_set("timeout", "100") + with pytest.raises(redis.ResponseError): + await decoded_r.config_set("timeout", "null") + res = await decoded_r.config_get("*") + assert "100" == res["timeout"] + res = await decoded_r.config_get("timeout") + assert "100" == res["timeout"] + @pytest.mark.redismod @pytest.mark.onlynoncluster diff --git a/tests/test_commands.py b/tests/test_commands.py index 4cad4c14b6..2681b8eaf0 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1823,6 +1823,7 @@ def try_delete_libs(self, r, *lib_names): @pytest.mark.onlynoncluster @skip_if_server_version_lt("7.1.140") + @skip_if_server_version_gte("7.9.0") def test_tfunction_load_delete(self, stack_r): self.try_delete_libs(stack_r, "lib1") lib_code = self.generate_lib_code("lib1") @@ -1831,6 +1832,7 @@ def test_tfunction_load_delete(self, stack_r): @pytest.mark.onlynoncluster @skip_if_server_version_lt("7.1.140") + @skip_if_server_version_gte("7.9.0") def test_tfunction_list(self, stack_r): self.try_delete_libs(stack_r, "lib1", "lib2", "lib3") assert stack_r.tfunction_load(self.generate_lib_code("lib1")) @@ -1861,6 +1863,7 @@ def test_tfunction_list(self, stack_r): @pytest.mark.onlynoncluster @skip_if_server_version_lt("7.1.140") + @skip_if_server_version_gte("7.9.0") def test_tfcall(self, stack_r): self.try_delete_libs(stack_r, "lib1") assert stack_r.tfunction_load(self.generate_lib_code("lib1")) @@ -4329,6 +4332,7 @@ def test_xgroup_create_mkstream(self, r): assert r.xinfo_groups(stream) == expected @skip_if_server_version_lt("7.0.0") + @skip_if_server_version_gte("7.9.0") def test_xgroup_create_entriesread(self, r: redis.Redis): stream = "stream" group = "group" @@ -4350,6 +4354,28 @@ def test_xgroup_create_entriesread(self, r: redis.Redis): ] assert r.xinfo_groups(stream) == expected + @skip_if_server_version_lt("7.9.0") + def test_xgroup_create_entriesread_with_fixed_lag_field(self, r: redis.Redis): + stream = "stream" + group = "group" + r.xadd(stream, {"foo": "bar"}) + + # no group is setup yet, no info to obtain + assert r.xinfo_groups(stream) == [] + + assert r.xgroup_create(stream, group, 0, entries_read=7) + expected = [ + { + "name": group.encode(), + "consumers": 0, + "pending": 0, + "last-delivered-id": b"0-0", + "entries-read": 7, + "lag": 1, + } + ] + assert r.xinfo_groups(stream) == expected + @skip_if_server_version_lt("5.0.0") def test_xgroup_delconsumer(self, r): stream = "stream" diff --git a/tests/test_search.py b/tests/test_search.py index a257484425..d5cf917287 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -31,7 +31,7 @@ skip_if_redis_enterprise, skip_if_resp_version, skip_if_server_version_lt, - skip_ifmodversion_lt, + skip_ifmodversion_lt, skip_if_server_version_gte, ) WILL_PLAY_TEXT = os.path.abspath( @@ -1007,6 +1007,7 @@ def test_get(client): @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_ifmodversion_lt("2.2.0", "search") +@skip_if_server_version_gte("7.9.0") def test_config(client): assert client.ft().config_set("TIMEOUT", "100") with pytest.raises(redis.ResponseError): @@ -1016,6 +1017,18 @@ def test_config(client): res = client.ft().config_get("TIMEOUT") assert "100" == res["TIMEOUT"] +@pytest.mark.redismod +@pytest.mark.onlynoncluster +@skip_if_server_version_lt("7.9.0") +def test_config_with_removed_ftconfig(client): + assert client.config_set("timeout", "100") + with pytest.raises(redis.ResponseError): + client.config_set("timeout", "null") + res = client.config_get("*") + assert "100" == res["timeout"] + res = client.config_get("timeout") + assert "100" == res["timeout"] + @pytest.mark.redismod @pytest.mark.onlynoncluster @@ -1573,11 +1586,11 @@ def test_index_definition(client): @skip_if_redis_enterprise() def test_expire(client): client.ft().create_index((TextField("txt", sortable=True),), temporary=4) - ttl = client.execute_command("ft.debug", "TTL", "idx") + ttl = client.execute_command("_ft.debug", "TTL", "idx") assert ttl > 2 while ttl > 2: - ttl = client.execute_command("ft.debug", "TTL", "idx") + ttl = client.execute_command("_ft.debug", "TTL", "idx") time.sleep(0.01) From 99c50e64ca8f0305065a2340f6dc1d52f36b18a3 Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Tue, 14 Jan 2025 12:39:31 +0200 Subject: [PATCH 3/7] Added version annotation --- tests/test_search.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_search.py b/tests/test_search.py index d5cf917287..d0f4bcbcd4 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -1584,13 +1584,14 @@ def test_index_definition(client): @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_if_redis_enterprise() +@skip_if_server_version_gte("7.9.0") def test_expire(client): client.ft().create_index((TextField("txt", sortable=True),), temporary=4) - ttl = client.execute_command("_ft.debug", "TTL", "idx") + ttl = client.execute_command("ft.debug", "TTL", "idx") assert ttl > 2 while ttl > 2: - ttl = client.execute_command("_ft.debug", "TTL", "idx") + ttl = client.execute_command("ft.debug", "TTL", "idx") time.sleep(0.01) From 95adbfc34e3024887089fec13fa237fedfb3659e Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Tue, 14 Jan 2025 14:01:19 +0200 Subject: [PATCH 4/7] Changed FT.PROFILE response type --- redis/commands/search/commands.py | 9 +++-- redis/commands/search/profileInformation.py | 14 ++++++++ tests/test_asyncio/test_search.py | 5 ++- tests/test_search.py | 40 +++++++++++---------- 4 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 redis/commands/search/profileInformation.py diff --git a/redis/commands/search/commands.py b/redis/commands/search/commands.py index da79016ad4..f37d73bdc0 100644 --- a/redis/commands/search/commands.py +++ b/redis/commands/search/commands.py @@ -5,12 +5,13 @@ from redis.client import NEVER_DECODE, Pipeline from redis.utils import deprecated_function -from ..helpers import get_protocol_version, parse_to_dict +from ..helpers import get_protocol_version from ._util import to_string from .aggregation import AggregateRequest, AggregateResult, Cursor from .document import Document from .field import Field from .indexDefinition import IndexDefinition +from .profileInformation import ProfileInformation from .query import Query from .result import Result from .suggestion import SuggestionParser @@ -66,8 +67,10 @@ class SearchCommands: """Search commands.""" def _parse_results(self, cmd, res, **kwargs): - if get_protocol_version(self.client) in ["3", 3]: + if get_protocol_version(self.client) in ["3", 3] and cmd != "FT.PROFILE": return res + elif get_protocol_version(self.client) in ["3", 3] and cmd == "FT.PROFILE": + return ProfileInformation(res) else: return self._RESP2_MODULE_CALLBACKS[cmd](res, **kwargs) @@ -101,7 +104,7 @@ def _parse_profile(self, res, **kwargs): with_scores=query._with_scores, ) - return result, parse_to_dict(res[1]) + return result, ProfileInformation(res[1]) def _parse_spellcheck(self, res, **kwargs): corrections = {} diff --git a/redis/commands/search/profileInformation.py b/redis/commands/search/profileInformation.py new file mode 100644 index 0000000000..23551be27f --- /dev/null +++ b/redis/commands/search/profileInformation.py @@ -0,0 +1,14 @@ +from typing import Any + + +class ProfileInformation: + """ + Wrapper around FT.PROFILE response + """ + + def __init__(self, info: Any) -> None: + self._info: Any = info + + @property + def info(self) -> Any: + return self._info diff --git a/tests/test_asyncio/test_search.py b/tests/test_asyncio/test_search.py index 19bd1a572c..f690ee0585 100644 --- a/tests/test_asyncio/test_search.py +++ b/tests/test_asyncio/test_search.py @@ -27,7 +27,9 @@ is_resp2_connection, skip_if_redis_enterprise, skip_if_resp_version, - skip_ifmodversion_lt, skip_if_server_version_gte, skip_if_server_version_lt, + skip_if_server_version_gte, + skip_if_server_version_lt, + skip_ifmodversion_lt, ) WILL_PLAY_TEXT = os.path.abspath( @@ -1121,6 +1123,7 @@ async def test_config(decoded_r: redis.Redis): res = await decoded_r.ft().config_get("TIMEOUT") assert "100" == res["TIMEOUT"] + @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_if_server_version_lt("7.9.0") diff --git a/tests/test_search.py b/tests/test_search.py index d0f4bcbcd4..29de097f86 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -30,8 +30,9 @@ is_resp2_connection, skip_if_redis_enterprise, skip_if_resp_version, + skip_if_server_version_gte, skip_if_server_version_lt, - skip_ifmodversion_lt, skip_if_server_version_gte, + skip_ifmodversion_lt, ) WILL_PLAY_TEXT = os.path.abspath( @@ -1017,6 +1018,7 @@ def test_config(client): res = client.ft().config_get("TIMEOUT") assert "100" == res["TIMEOUT"] + @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_if_server_version_lt("7.9.0") @@ -2048,10 +2050,10 @@ def test_profile(client): q = Query("hello|world").no_content() if is_resp2_connection(client): res, det = client.ft().profile(q) - assert det["Iterators profile"]["Counter"] == 2.0 - assert len(det["Iterators profile"]["Child iterators"]) == 2 - assert det["Iterators profile"]["Type"] == "UNION" - assert det["Parsing time"] < 0.5 + det = det.info + assert det[4][1][7] == 2.0 + assert det[4][1][1] == "UNION" + assert float(det[1][1]) < 0.5 assert len(res.docs) == 2 # check also the search result # check using AggregateRequest @@ -2061,12 +2063,13 @@ def test_profile(client): .apply(prefix="startswith(@t, 'hel')") ) res, det = client.ft().profile(req) - assert det["Iterators profile"]["Counter"] == 2 - assert det["Iterators profile"]["Type"] == "WILDCARD" - assert isinstance(det["Parsing time"], float) + det = det.info + assert det[4][1][5] == 2 + assert det[4][1][1] == "WILDCARD" assert len(res.rows) == 2 # check also the search result else: res = client.ft().profile(q) + res = res.info assert res["profile"]["Iterators profile"][0]["Counter"] == 2.0 assert res["profile"]["Iterators profile"][0]["Type"] == "UNION" assert res["profile"]["Parsing time"] < 0.5 @@ -2079,6 +2082,7 @@ def test_profile(client): .apply(prefix="startswith(@t, 'hel')") ) res = client.ft().profile(req) + res = res.info assert res["profile"]["Iterators profile"][0]["Counter"] == 2 assert res["profile"]["Iterators profile"][0]["Type"] == "WILDCARD" assert isinstance(res["profile"]["Parsing time"], float) @@ -2097,18 +2101,14 @@ def test_profile_limited(client): q = Query("%hell% hel*") if is_resp2_connection(client): res, det = client.ft().profile(q, limited=True) - assert ( - det["Iterators profile"]["Child iterators"][0]["Child iterators"] - == "The number of iterators in the union is 3" - ) - assert ( - det["Iterators profile"]["Child iterators"][1]["Child iterators"] - == "The number of iterators in the union is 4" - ) - assert det["Iterators profile"]["Type"] == "INTERSECT" + det = det.info + assert det[4][1][7][9] == "The number of iterators in the union is 3" + assert det[4][1][8][9] == "The number of iterators in the union is 4" + assert det[4][1][1] == "INTERSECT" assert len(res.docs) == 3 # check also the search result else: res = client.ft().profile(q, limited=True) + res = res.info iterators_profile = res["profile"]["Iterators profile"] assert ( iterators_profile[0]["Child iterators"][0]["Child iterators"] @@ -2139,13 +2139,15 @@ def test_profile_query_params(client): q = Query(query).return_field("__v_score").sort_by("__v_score", True) if is_resp2_connection(client): res, det = client.ft().profile(q, query_params={"vec": "aaaaaaaa"}) - assert det["Iterators profile"]["Counter"] == 2.0 - assert det["Iterators profile"]["Type"] == "VECTOR" + det = det.info + assert det[4][1][5] == 2.0 + assert det[4][1][1] == "VECTOR" assert res.total == 2 assert "a" == res.docs[0].id assert "0" == res.docs[0].__getattribute__("__v_score") else: res = client.ft().profile(q, query_params={"vec": "aaaaaaaa"}) + res = res.info assert res["profile"]["Iterators profile"][0]["Counter"] == 2 assert res["profile"]["Iterators profile"][0]["Type"] == "VECTOR" assert res["total_results"] == 2 From 84193c2934cbacf0a40adfaa5d27f645af3f0054 Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Tue, 14 Jan 2025 14:41:43 +0200 Subject: [PATCH 5/7] Added version restrictions --- tests/test_search.py | 94 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/test_search.py b/tests/test_search.py index 29de097f86..469ab05ab7 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -2041,6 +2041,8 @@ def test_json_with_jsonpath(client): @pytest.mark.redismod @pytest.mark.onlynoncluster @skip_if_redis_enterprise() +@skip_if_server_version_gte("7.9.0") +@skip_if_server_version_lt("6.3.0") def test_profile(client): client.ft().create_index((TextField("t"),)) client.ft().client.hset("1", "t", "hello") @@ -2091,6 +2093,96 @@ def test_profile(client): @pytest.mark.redismod @pytest.mark.onlynoncluster +@skip_if_redis_enterprise() +@skip_if_server_version_lt("7.9.0") +def test_profile_with_coordinator(client): + client.ft().create_index((TextField("t"),)) + client.ft().client.hset("1", "t", "hello") + client.ft().client.hset("2", "t", "world") + + # check using Query + q = Query("hello|world").no_content() + if is_resp2_connection(client): + res, det = client.ft().profile(q) + det = det.info + assert det[0] == "Shards" + assert det[2] == "Coordinator" + assert det[1][0][9][7] == 2.0 + assert det[1][0][9][1] == "UNION" + assert float(det[1][0][3]) < 0.5 + assert len(res.docs) == 2 # check also the search result + + # check using AggregateRequest + req = ( + aggregations.AggregateRequest("*") + .load("t") + .apply(prefix="startswith(@t, 'hel')") + ) + res, det = client.ft().profile(req) + det = det.info + assert det[0] == "Shards" + assert det[2] == "Coordinator" + assert det[1][0][9][5] == 2 + assert det[1][0][9][1] == "WILDCARD" + assert len(res.rows) == 2 # check also the search result + else: + res = client.ft().profile(q) + res = res.info + assert res["Profile"]["Shards"][0]["Iterators profile"]["Counter"] == 2.0 + assert res["Profile"]["Shards"][0]["Iterators profile"]["Type"] == "UNION" + assert res["Profile"]["Shards"][0]["Parsing time"] < 0.5 + assert len(res["Results"]["results"]) == 2 # check also the search result + + # check using AggregateRequest + req = ( + aggregations.AggregateRequest("*") + .load("t") + .apply(prefix="startswith(@t, 'hel')") + ) + res = client.ft().profile(req) + res = res.info + assert res["Profile"]["Shards"][0]["Iterators profile"]["Counter"] == 2 + assert res["Profile"]["Shards"][0]["Iterators profile"]["Type"] == "WILDCARD" + assert isinstance(res["Profile"]["Shards"][0]["Parsing time"], float) + assert len(res["Results"]["results"]) == 2 # check also the search result + + +@pytest.mark.redismod +@pytest.mark.onlynoncluster +@skip_if_redis_enterprise() +@skip_if_server_version_gte("6.3.0") +def test_profile_with_no_warnings(client): + client.ft().create_index((TextField("t"),)) + client.ft().client.hset("1", "t", "hello") + client.ft().client.hset("2", "t", "world") + + # check using Query + q = Query("hello|world").no_content() + res, det = client.ft().profile(q) + det = det.info + print(det) + assert det[3][1][7] == 2.0 + assert det[3][1][1] == "UNION" + assert float(det[1][1]) < 0.5 + assert len(res.docs) == 2 # check also the search result + + # check using AggregateRequest + req = ( + aggregations.AggregateRequest("*") + .load("t") + .apply(prefix="startswith(@t, 'hel')") + ) + res, det = client.ft().profile(req) + det = det.info + assert det[3][1][5] == 2 + assert det[3][1][1] == "WILDCARD" + assert len(res.rows) == 2 # check also the search result + + +@pytest.mark.redismod +@pytest.mark.onlynoncluster +@skip_if_server_version_gte("7.9.0") +@skip_if_server_version_lt("6.3.0") def test_profile_limited(client): client.ft().create_index((TextField("t"),)) client.ft().client.hset("1", "t", "hello") @@ -2124,6 +2216,8 @@ def test_profile_limited(client): @pytest.mark.redismod @skip_ifmodversion_lt("2.4.3", "search") +@skip_if_server_version_gte("7.9.0") +@skip_if_server_version_lt("6.3.0") def test_profile_query_params(client): client.ft().create_index( ( From 98df6e822aaed09224c7ce95d223d7c0163d592d Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Mon, 20 Jan 2025 11:21:29 +0200 Subject: [PATCH 6/7] Updated file names, fixed tests assertions --- doctests/query_agg.py | 2 +- doctests/query_combined.py | 2 +- doctests/query_em.py | 2 +- doctests/query_ft.py | 2 +- doctests/query_geo.py | 2 +- doctests/query_range.py | 2 +- doctests/search_quickstart.py | 2 +- doctests/search_vss.py | 2 +- redis/commands/search/commands.py | 10 ++-- ...indexDefinition.py => index_definition.py} | 0 ...eInformation.py => profile_information.py} | 0 tests/test_asyncio/test_search.py | 2 +- tests/test_search.py | 51 ++++++++----------- 13 files changed, 33 insertions(+), 46 deletions(-) rename redis/commands/search/{indexDefinition.py => index_definition.py} (100%) rename redis/commands/search/{profileInformation.py => profile_information.py} (100%) diff --git a/doctests/query_agg.py b/doctests/query_agg.py index 4fa8f14b84..4d81ddbcda 100644 --- a/doctests/query_agg.py +++ b/doctests/query_agg.py @@ -6,7 +6,7 @@ from redis.commands.search import Search from redis.commands.search.aggregation import AggregateRequest from redis.commands.search.field import NumericField, TagField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType import redis.commands.search.reducers as reducers r = redis.Redis(decode_responses=True) diff --git a/doctests/query_combined.py b/doctests/query_combined.py index a17f19417c..e6dd5a2cb5 100644 --- a/doctests/query_combined.py +++ b/doctests/query_combined.py @@ -6,7 +6,7 @@ import warnings from redis.commands.json.path import Path from redis.commands.search.field import NumericField, TagField, TextField, VectorField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import Query from sentence_transformers import SentenceTransformer diff --git a/doctests/query_em.py b/doctests/query_em.py index a00ff11150..91cc5ae940 100644 --- a/doctests/query_em.py +++ b/doctests/query_em.py @@ -4,7 +4,7 @@ import redis from redis.commands.json.path import Path from redis.commands.search.field import TextField, NumericField, TagField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import NumericFilter, Query r = redis.Redis(decode_responses=True) diff --git a/doctests/query_ft.py b/doctests/query_ft.py index 182a5b2bd3..6272cdab25 100644 --- a/doctests/query_ft.py +++ b/doctests/query_ft.py @@ -5,7 +5,7 @@ import redis from redis.commands.json.path import Path from redis.commands.search.field import TextField, NumericField, TagField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import NumericFilter, Query r = redis.Redis(decode_responses=True) diff --git a/doctests/query_geo.py b/doctests/query_geo.py index dcb7db6ee7..ed8c9a5f99 100644 --- a/doctests/query_geo.py +++ b/doctests/query_geo.py @@ -5,7 +5,7 @@ import redis from redis.commands.json.path import Path from redis.commands.search.field import GeoField, GeoShapeField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import Query r = redis.Redis(decode_responses=True) diff --git a/doctests/query_range.py b/doctests/query_range.py index 4ef957acfb..674afc492a 100644 --- a/doctests/query_range.py +++ b/doctests/query_range.py @@ -5,7 +5,7 @@ import redis from redis.commands.json.path import Path from redis.commands.search.field import TextField, NumericField, TagField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import NumericFilter, Query r = redis.Redis(decode_responses=True) diff --git a/doctests/search_quickstart.py b/doctests/search_quickstart.py index e190393b16..cde4caa84a 100644 --- a/doctests/search_quickstart.py +++ b/doctests/search_quickstart.py @@ -10,7 +10,7 @@ import redis.commands.search.reducers as reducers from redis.commands.json.path import Path from redis.commands.search.field import NumericField, TagField, TextField -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import Query # HIDE_END diff --git a/doctests/search_vss.py b/doctests/search_vss.py index 8b4884727a..a1132971db 100644 --- a/doctests/search_vss.py +++ b/doctests/search_vss.py @@ -20,7 +20,7 @@ TextField, VectorField, ) -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import Query from sentence_transformers import SentenceTransformer diff --git a/redis/commands/search/commands.py b/redis/commands/search/commands.py index f37d73bdc0..2447959922 100644 --- a/redis/commands/search/commands.py +++ b/redis/commands/search/commands.py @@ -10,8 +10,8 @@ from .aggregation import AggregateRequest, AggregateResult, Cursor from .document import Document from .field import Field -from .indexDefinition import IndexDefinition -from .profileInformation import ProfileInformation +from .index_definition import IndexDefinition +from .profile_information import ProfileInformation from .query import Query from .result import Result from .suggestion import SuggestionParser @@ -67,10 +67,8 @@ class SearchCommands: """Search commands.""" def _parse_results(self, cmd, res, **kwargs): - if get_protocol_version(self.client) in ["3", 3] and cmd != "FT.PROFILE": - return res - elif get_protocol_version(self.client) in ["3", 3] and cmd == "FT.PROFILE": - return ProfileInformation(res) + if get_protocol_version(self.client) in ["3", 3]: + return ProfileInformation(res) if cmd == "FT.PROFILE" else res else: return self._RESP2_MODULE_CALLBACKS[cmd](res, **kwargs) diff --git a/redis/commands/search/indexDefinition.py b/redis/commands/search/index_definition.py similarity index 100% rename from redis/commands/search/indexDefinition.py rename to redis/commands/search/index_definition.py diff --git a/redis/commands/search/profileInformation.py b/redis/commands/search/profile_information.py similarity index 100% rename from redis/commands/search/profileInformation.py rename to redis/commands/search/profile_information.py diff --git a/tests/test_asyncio/test_search.py b/tests/test_asyncio/test_search.py index f690ee0585..4f5a4c2f04 100644 --- a/tests/test_asyncio/test_search.py +++ b/tests/test_asyncio/test_search.py @@ -19,7 +19,7 @@ TextField, VectorField, ) -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import GeoFilter, NumericFilter, Query from redis.commands.search.result import Result from redis.commands.search.suggestion import Suggestion diff --git a/tests/test_search.py b/tests/test_search.py index 469ab05ab7..ee1ba66434 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -20,7 +20,7 @@ TextField, VectorField, ) -from redis.commands.search.indexDefinition import IndexDefinition, IndexType +from redis.commands.search.index_definition import IndexDefinition, IndexType from redis.commands.search.query import GeoFilter, NumericFilter, Query from redis.commands.search.result import Result from redis.commands.search.suggestion import Suggestion @@ -2053,9 +2053,8 @@ def test_profile(client): if is_resp2_connection(client): res, det = client.ft().profile(q) det = det.info - assert det[4][1][7] == 2.0 - assert det[4][1][1] == "UNION" - assert float(det[1][1]) < 0.5 + + assert isinstance(det, list) assert len(res.docs) == 2 # check also the search result # check using AggregateRequest @@ -2066,15 +2065,13 @@ def test_profile(client): ) res, det = client.ft().profile(req) det = det.info - assert det[4][1][5] == 2 - assert det[4][1][1] == "WILDCARD" + assert isinstance(det, list) assert len(res.rows) == 2 # check also the search result else: res = client.ft().profile(q) res = res.info - assert res["profile"]["Iterators profile"][0]["Counter"] == 2.0 - assert res["profile"]["Iterators profile"][0]["Type"] == "UNION" - assert res["profile"]["Parsing time"] < 0.5 + + assert isinstance(res, dict) assert len(res["results"]) == 2 # check also the search result # check using AggregateRequest @@ -2085,9 +2082,8 @@ def test_profile(client): ) res = client.ft().profile(req) res = res.info - assert res["profile"]["Iterators profile"][0]["Counter"] == 2 - assert res["profile"]["Iterators profile"][0]["Type"] == "WILDCARD" - assert isinstance(res["profile"]["Parsing time"], float) + + assert isinstance(res, dict) assert len(res["results"]) == 2 # check also the search result @@ -2105,11 +2101,8 @@ def test_profile_with_coordinator(client): if is_resp2_connection(client): res, det = client.ft().profile(q) det = det.info - assert det[0] == "Shards" - assert det[2] == "Coordinator" - assert det[1][0][9][7] == 2.0 - assert det[1][0][9][1] == "UNION" - assert float(det[1][0][3]) < 0.5 + + assert isinstance(det, list) assert len(res.docs) == 2 # check also the search result # check using AggregateRequest @@ -2120,17 +2113,16 @@ def test_profile_with_coordinator(client): ) res, det = client.ft().profile(req) det = det.info + + assert isinstance(det, list) assert det[0] == "Shards" assert det[2] == "Coordinator" - assert det[1][0][9][5] == 2 - assert det[1][0][9][1] == "WILDCARD" assert len(res.rows) == 2 # check also the search result else: res = client.ft().profile(q) res = res.info - assert res["Profile"]["Shards"][0]["Iterators profile"]["Counter"] == 2.0 - assert res["Profile"]["Shards"][0]["Iterators profile"]["Type"] == "UNION" - assert res["Profile"]["Shards"][0]["Parsing time"] < 0.5 + + assert isinstance(res, dict) assert len(res["Results"]["results"]) == 2 # check also the search result # check using AggregateRequest @@ -2141,9 +2133,8 @@ def test_profile_with_coordinator(client): ) res = client.ft().profile(req) res = res.info - assert res["Profile"]["Shards"][0]["Iterators profile"]["Counter"] == 2 - assert res["Profile"]["Shards"][0]["Iterators profile"]["Type"] == "WILDCARD" - assert isinstance(res["Profile"]["Shards"][0]["Parsing time"], float) + + assert isinstance(res, dict) assert len(res["Results"]["results"]) == 2 # check also the search result @@ -2160,10 +2151,8 @@ def test_profile_with_no_warnings(client): q = Query("hello|world").no_content() res, det = client.ft().profile(q) det = det.info - print(det) - assert det[3][1][7] == 2.0 - assert det[3][1][1] == "UNION" - assert float(det[1][1]) < 0.5 + + assert isinstance(det, list) assert len(res.docs) == 2 # check also the search result # check using AggregateRequest @@ -2174,8 +2163,8 @@ def test_profile_with_no_warnings(client): ) res, det = client.ft().profile(req) det = det.info - assert det[3][1][5] == 2 - assert det[3][1][1] == "WILDCARD" + + assert isinstance(det, list) assert len(res.rows) == 2 # check also the search result From 1eaa90c6314b776873c0841abe02b55303efa76d Mon Sep 17 00:00:00 2001 From: vladvildanov Date: Mon, 20 Jan 2025 11:39:42 +0200 Subject: [PATCH 7/7] Removed unused API --- redis/commands/helpers.py | 23 ----------------------- tests/test_helpers.py | 35 ----------------------------------- 2 files changed, 58 deletions(-) diff --git a/redis/commands/helpers.py b/redis/commands/helpers.py index 1ea02a60cf..7d9095ea41 100644 --- a/redis/commands/helpers.py +++ b/redis/commands/helpers.py @@ -79,29 +79,6 @@ def parse_list_to_dict(response): return res -def parse_to_dict(response): - if response is None: - return {} - - res = {} - for det in response: - if not isinstance(det, list) or not det: - continue - if len(det) == 1: - res[det[0]] = True - elif isinstance(det[1], list): - res[det[0]] = parse_list_to_dict(det[1]) - else: - try: # try to set the attribute. may be provided without value - try: # try to convert the value to float - res[det[0]] = float(det[1]) - except (TypeError, ValueError): - res[det[0]] = det[1] - except IndexError: - pass - return res - - def random_string(length=10): """ Returns a random N character long string. diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 66ee1c5390..06265d382e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -4,7 +4,6 @@ delist, list_or_args, nativestr, - parse_to_dict, parse_to_list, quote_string, random_string, @@ -26,40 +25,6 @@ def test_parse_to_list(): assert parse_to_list(r) == ["hello", "my name", 45, 555.55, "is simon!", None] -def test_parse_to_dict(): - assert parse_to_dict(None) == {} - r = [ - ["Some number", "1.0345"], - ["Some string", "hello"], - [ - "Child iterators", - [ - "Time", - "0.2089", - "Counter", - 3, - "Child iterators", - ["Type", "bar", "Time", "0.0729", "Counter", 3], - ["Type", "barbar", "Time", "0.058", "Counter", 3], - ["Type", "barbarbar", "Time", "0.0234", "Counter", 3], - ], - ], - ] - assert parse_to_dict(r) == { - "Child iterators": { - "Child iterators": [ - {"Counter": 3.0, "Time": 0.0729, "Type": "bar"}, - {"Counter": 3.0, "Time": 0.058, "Type": "barbar"}, - {"Counter": 3.0, "Time": 0.0234, "Type": "barbarbar"}, - ], - "Counter": 3.0, - "Time": 0.2089, - }, - "Some number": 1.0345, - "Some string": "hello", - } - - def test_nativestr(): assert nativestr("teststr") == "teststr" assert nativestr(b"teststr") == "teststr"