From 0d25c17096f8377296e842e33458ed7b8b4a6b79 Mon Sep 17 00:00:00 2001 From: huwaizatahir2 Date: Mon, 9 Dec 2024 17:10:53 +0500 Subject: [PATCH 1/2] fix: resolve VectorField syntax error in JsonModel and add unit tests --- aredis_om/model/model.py | 5 ++- tests/test_json_model.py | 81 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index bee37591..44b24e62 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -2162,7 +2162,10 @@ def schema_for_type( # a proper type, we can pull the type information from the origin of the first argument. if not isinstance(typ, type): type_args = typing_get_args(field_info.annotation) - typ = type_args[0].__origin__ + if type_args and hasattr(type_args[0], "__origin__"): + typ = type_args[0].__origin__ + else: + typ = type_args[0] if type_args else typ # TODO: GEO field if is_vector and vector_options: diff --git a/tests/test_json_model.py b/tests/test_json_model.py index d963647b..d7cdc58f 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -12,6 +12,7 @@ import pytest import pytest_asyncio +from redis.exceptions import ResponseError from aredis_om import ( EmbeddedJsonModel, @@ -21,6 +22,7 @@ NotFoundError, QueryNotSupportedError, RedisModelError, + VectorFieldOptions, ) # We need to run this check as sync code (during tests) even in async mode @@ -1173,3 +1175,82 @@ class Game(JsonModel): ) print(q.query) assert q.query == "(@player1_username:{username})| (@player2_username:{username})" + + +@py_test_mark_asyncio +def test_vector_field_definition(redis): + """ + Test the definition and behavior of a vector field in a JsonModel. + This test verifies: + 1. The model schema includes "VECTOR" for a vector field with specified options. + 2. Instances with vector fields can be saved and retrieved accurately. + 3. Vector field values remain consistent after persistence. + + Args: + redis: Redis connection fixture for testing. + """ + + class Group(JsonModel): + articles: List[str] + tender_text: str = Field(index=False) + tender_embedding: List[float] = Field( + index=True, + vector_options=VectorFieldOptions( + algorithm=VectorFieldOptions.ALGORITHM.FLAT, + type=VectorFieldOptions.TYPE.FLOAT32, + dimension=3, + distance_metric=VectorFieldOptions.DISTANCE_METRIC.COSINE, + ), + ) + + schema = Group.redisearch_schema() + assert "VECTOR" in schema + + group = Group( + articles=["article_1", "article_2"], + tender_text="Sample text", + tender_embedding=[0.1, 0.2, 0.3], + ) + group.save() + + retrieved_group = await Group.get(group.pk) + assert retrieved_group.tender_embedding == [0.1, 0.2, 0.3] + + retrieved_group = Group.get(group.pk) + assert retrieved_group.tender_embedding == [0.1, 0.2, 0.3] + + +def test_vector_field_schema_debug(redis): + """ + Test and debug the schema definition for a vector field in a JsonModel. + + This test ensures: + 1. The schema for a vector field is generated correctly. + 2. No syntax errors occur when saving a model instance. + 3. The Redis schema syntax is valid and debugged if issues arise. + + Steps: + - Define a `TestModel` with a vector field `embedding`. + - Attempt to save an instance and print the schema. + - Handle and fail gracefully if Redis raises a ResponseError. + + Args: + redis: Redis connection fixture for testing. + """ + + class TestModel(JsonModel): + embedding: List[float] = Field( + index=True, + vector_options=VectorFieldOptions( + algorithm=VectorFieldOptions.ALGORITHM.FLAT, + type=VectorFieldOptions.TYPE.FLOAT32, + dimension=3, + distance_metric=VectorFieldOptions.DISTANCE_METRIC.COSINE, + ), + ) + + try: + TestModel(embedding=[0.1, 0.2, 0.3]).save() + print(TestModel.redisearch_schema()) + except ResponseError as e: + pytest.fail(f"Redis rejected the schema with error: {e}") From 14ab9dc25586c7b5aee6ac93817914b9cd82b6a1 Mon Sep 17 00:00:00 2001 From: huwaizatahir2 Date: Mon, 9 Dec 2024 17:14:47 +0500 Subject: [PATCH 2/2] Refactor type resolution logic for vector fields to improve Pythonic style --- aredis_om/model/model.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index 44b24e62..188df99e 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -2162,10 +2162,11 @@ def schema_for_type( # a proper type, we can pull the type information from the origin of the first argument. if not isinstance(typ, type): type_args = typing_get_args(field_info.annotation) - if type_args and hasattr(type_args[0], "__origin__"): - typ = type_args[0].__origin__ - else: - typ = type_args[0] if type_args else typ + typ = ( + getattr(type_args[0], "__origin__", type_args[0]) + if type_args + else typ + ) # TODO: GEO field if is_vector and vector_options: