Skip to content

Commit 5ef3d27

Browse files
ninosekislorello89
andauthored
feat: add casesensitive support (#608)
* feat: add casesensitive support * snake_case --------- Co-authored-by: slorello89 <[email protected]>
1 parent f1ed5b2 commit 5ef3d27

File tree

3 files changed

+45
-7
lines changed

3 files changed

+45
-7
lines changed

aredis_om/model/model.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,12 +1032,14 @@ class FieldInfo(PydanticFieldInfo):
10321032
def __init__(self, default: Any = Undefined, **kwargs: Any) -> None:
10331033
primary_key = kwargs.pop("primary_key", False)
10341034
sortable = kwargs.pop("sortable", Undefined)
1035+
case_sensitive = kwargs.pop("case_sensitive", Undefined)
10351036
index = kwargs.pop("index", Undefined)
10361037
full_text_search = kwargs.pop("full_text_search", Undefined)
10371038
vector_options = kwargs.pop("vector_options", None)
10381039
super().__init__(default=default, **kwargs)
10391040
self.primary_key = primary_key
10401041
self.sortable = sortable
1042+
self.case_sensitive = case_sensitive
10411043
self.index = index
10421044
self.full_text_search = full_text_search
10431045
self.vector_options = vector_options
@@ -1169,6 +1171,7 @@ def Field(
11691171
regex: Optional[str] = None,
11701172
primary_key: bool = False,
11711173
sortable: Union[bool, UndefinedType] = Undefined,
1174+
case_sensitive: Union[bool, UndefinedType] = Undefined,
11721175
index: Union[bool, UndefinedType] = Undefined,
11731176
full_text_search: Union[bool, UndefinedType] = Undefined,
11741177
vector_options: Optional[VectorFieldOptions] = None,
@@ -1197,6 +1200,7 @@ def Field(
11971200
regex=regex,
11981201
primary_key=primary_key,
11991202
sortable=sortable,
1203+
case_sensitive=case_sensitive,
12001204
index=index,
12011205
full_text_search=full_text_search,
12021206
vector_options=vector_options,
@@ -1764,6 +1768,7 @@ def schema_for_type(cls, name, typ: Any, field_info: PydanticFieldInfo):
17641768
# TODO: Abstract string-building logic for each type (TAG, etc.) into
17651769
# classes that take a field name.
17661770
sortable = getattr(field_info, "sortable", False)
1771+
case_sensitive = getattr(field_info, "case_sensitive", False)
17671772

17681773
if is_supported_container_type(typ):
17691774
embedded_cls = get_args(typ)
@@ -1804,6 +1809,9 @@ def schema_for_type(cls, name, typ: Any, field_info: PydanticFieldInfo):
18041809
schema = f"{name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
18051810
if schema and sortable is True:
18061811
schema += " SORTABLE"
1812+
if schema and case_sensitive is True:
1813+
schema += " CASESENSITIVE"
1814+
18071815
return schema
18081816

18091817

@@ -2046,6 +2054,7 @@ def schema_for_type(
20462054
else:
20472055
path = f"{json_path}.{name}"
20482056
sortable = getattr(field_info, "sortable", False)
2057+
case_sensitive = getattr(field_info, "case_sensitive", False)
20492058
full_text_search = getattr(field_info, "full_text_search", False)
20502059
sortable_tag_error = RedisModelError(
20512060
"In this Preview release, TAG fields cannot "
@@ -2076,6 +2085,8 @@ def schema_for_type(
20762085
schema = f"{path} AS {index_field_name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
20772086
if sortable is True:
20782087
raise sortable_tag_error
2088+
if case_sensitive is True:
2089+
schema += " CASESENSITIVE"
20792090
elif any(issubclass(typ, t) for t in NUMERIC_TYPES):
20802091
schema = f"{path} AS {index_field_name} NUMERIC"
20812092
elif issubclass(typ, str):
@@ -2091,14 +2102,19 @@ def schema_for_type(
20912102
# search queries can be sorted, but not exact match
20922103
# queries.
20932104
schema += " SORTABLE"
2105+
if case_sensitive is True:
2106+
raise RedisModelError("Text fields cannot be case-sensitive.")
20942107
else:
20952108
schema = f"{path} AS {index_field_name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
20962109
if sortable is True:
20972110
raise sortable_tag_error
2111+
if case_sensitive is True:
2112+
schema += " CASESENSITIVE"
20982113
else:
20992114
schema = f"{path} AS {index_field_name} TAG SEPARATOR {SINGLE_VALUE_TAG_FIELD_SEPARATOR}"
21002115
if sortable is True:
21012116
raise sortable_tag_error
2117+
21022118
return schema
21032119
return ""
21042120

tests/test_hash_model.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class Order(BaseHashModel):
4848

4949
class Member(BaseHashModel):
5050
id: int = Field(index=True, primary_key=True)
51-
first_name: str = Field(index=True)
51+
first_name: str = Field(index=True, case_sensitive=True)
5252
last_name: str = Field(index=True)
5353
email: str = Field(index=True)
5454
join_date: datetime.date
@@ -385,6 +385,17 @@ async def test_sorting(members, m):
385385
await m.Member.find().sort_by("join_date").all()
386386

387387

388+
@py_test_mark_asyncio
389+
async def test_case_sensitive(members, m):
390+
member1, member2, member3 = members
391+
392+
actual = await m.Member.find(m.Member.first_name == "Andrew").all()
393+
assert actual == [member1, member3]
394+
395+
actual = await m.Member.find(m.Member.first_name == "andrew").all()
396+
assert actual == []
397+
398+
388399
def test_validates_required_fields(m):
389400
# Raises ValidationError: last_name is required
390401
# TODO: Test the error value

tests/test_json_model.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Order(EmbeddedJsonModel):
6767
created_on: datetime.datetime
6868

6969
class Member(BaseJsonModel):
70-
first_name: str = Field(index=True)
70+
first_name: str = Field(index=True, case_sensitive=True)
7171
last_name: str = Field(index=True)
7272
email: Optional[EmailStr] = Field(index=True, default=None)
7373
join_date: datetime.date
@@ -454,15 +454,15 @@ async def test_in_query(members, m):
454454
)
455455
assert actual == [member2, member1, member3]
456456

457+
457458
@py_test_mark_asyncio
458459
async def test_not_in_query(members, m):
459460
member1, member2, member3 = members
460461
actual = await (
461-
m.Member.find(m.Member.pk >> [member2.pk, member3.pk])
462-
.sort_by("age")
463-
.all()
462+
m.Member.find(m.Member.pk >> [member2.pk, member3.pk]).sort_by("age").all()
464463
)
465-
assert actual == [ member1]
464+
assert actual == [member1]
465+
466466

467467
@py_test_mark_asyncio
468468
async def test_update_query(members, m):
@@ -749,6 +749,17 @@ async def test_sorting(members, m):
749749
await m.Member.find().sort_by("join_date").all()
750750

751751

752+
@py_test_mark_asyncio
753+
async def test_case_sensitive(members, m):
754+
member1, member2, member3 = members
755+
756+
actual = await m.Member.find(m.Member.first_name == "Andrew").all()
757+
assert actual == [member1, member3]
758+
759+
actual = await m.Member.find(m.Member.first_name == "andrew").all()
760+
assert actual == []
761+
762+
752763
@py_test_mark_asyncio
753764
async def test_not_found(m):
754765
with pytest.raises(NotFoundError):
@@ -873,7 +884,7 @@ async def test_schema(m, key_prefix):
873884
key_prefix = m.Member.make_key(m.Member._meta.primary_key_pattern.format(pk=""))
874885
assert (
875886
m.Member.redisearch_schema()
876-
== f"ON JSON PREFIX 1 {key_prefix} SCHEMA $.pk AS pk TAG SEPARATOR | $.first_name AS first_name TAG SEPARATOR | $.last_name AS last_name TAG SEPARATOR | $.email AS email TAG SEPARATOR | $.age AS age NUMERIC $.bio AS bio TAG SEPARATOR | $.bio AS bio_fts TEXT $.address.pk AS address_pk TAG SEPARATOR | $.address.city AS address_city TAG SEPARATOR | $.address.postal_code AS address_postal_code TAG SEPARATOR | $.address.note.pk AS address_note_pk TAG SEPARATOR | $.address.note.description AS address_note_description TAG SEPARATOR | $.orders[*].pk AS orders_pk TAG SEPARATOR | $.orders[*].items[*].pk AS orders_items_pk TAG SEPARATOR | $.orders[*].items[*].name AS orders_items_name TAG SEPARATOR |"
887+
== f"ON JSON PREFIX 1 {key_prefix} SCHEMA $.pk AS pk TAG SEPARATOR | $.first_name AS first_name TAG SEPARATOR | CASESENSITIVE $.last_name AS last_name TAG SEPARATOR | $.email AS email TAG SEPARATOR | $.age AS age NUMERIC $.bio AS bio TAG SEPARATOR | $.bio AS bio_fts TEXT $.address.pk AS address_pk TAG SEPARATOR | $.address.city AS address_city TAG SEPARATOR | $.address.postal_code AS address_postal_code TAG SEPARATOR | $.address.note.pk AS address_note_pk TAG SEPARATOR | $.address.note.description AS address_note_description TAG SEPARATOR | $.orders[*].pk AS orders_pk TAG SEPARATOR | $.orders[*].items[*].pk AS orders_items_pk TAG SEPARATOR | $.orders[*].items[*].name AS orders_items_name TAG SEPARATOR |"
877888
)
878889

879890

0 commit comments

Comments
 (0)