From 169bf710f027178e2ec95104767984273dd8a61d Mon Sep 17 00:00:00 2001 From: d3vyce Date: Sun, 26 Jul 2026 09:51:09 -0400 Subject: [PATCH] fix: skip unnecessary String cast on already-string search columns --- src/fastapi_toolsets/crud/search.py | 7 +++++-- tests/test_crud_search.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/fastapi_toolsets/crud/search.py b/src/fastapi_toolsets/crud/search.py index 4c3979c..a7ad80c 100644 --- a/src/fastapi_toolsets/crud/search.py +++ b/src/fastapi_toolsets/crud/search.py @@ -159,8 +159,11 @@ def build_search_filters( else: column = field - # Build the filter (cast to String for non-text columns) - column_as_string = column.cast(String) + # Build the filter (cast to String only when needed, to preserve + # pg_trgm GIN index usability on already-String columns) + column_as_string = ( + column if isinstance(column.type, String) else column.cast(String) + ) if config.case_sensitive: filters.append(column_as_string.like(f"%{query}%")) else: diff --git a/tests/test_crud_search.py b/tests/test_crud_search.py index 63faffa..f3f2f96 100644 --- a/tests/test_crud_search.py +++ b/tests/test_crud_search.py @@ -372,6 +372,22 @@ class TestBuildSearchFilters: assert len(joins) == 1 + def test_skips_cast_on_string_column(self): + """String columns are filtered directly, without a CAST (keeps pg_trgm indexable).""" + from fastapi_toolsets.crud.search import build_search_filters + + filters, _ = build_search_filters(User, "john", search_fields=[User.username]) + + assert "CAST" not in str(filters[0]) + + def test_casts_non_string_column(self): + """Non-string columns (e.g. UUID) still get cast to String so ilike works.""" + from fastapi_toolsets.crud.search import build_search_filters + + filters, _ = build_search_filters(User, "123", search_fields=[User.id]) + + assert "CAST" in str(filters[0]) + class TestSearchConfig: """Tests for SearchConfig options."""