Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Case insensitive indexing in Postgres with SQLAlchemy

Consider a declarative SQLAlchemy model with an indexed String field:

class User(Base):
    name = Column(String(100), index=True, nullable=False)

The name field is case sensitive, meaning the original case should be preserved, but efficient case-insensitive queries on the index should be supported.

What's the best way to achieve this and implement in SQLAlchemy?

Queries can use lower() if needed

session.query(User).filter_by(name=lower('SOME_name'))

but it doesn't matter too much, as long as the solution is elegant and performant.

Queries using ILIKE and Postgres-level lower() are unacceptable due to performance requirements, they've been tested and do not perform fast enough on large tables for my use case.

like image 365
Yuval Adam Avatar asked Feb 24 '26 06:02

Yuval Adam


1 Answers

Create a functional index that indexes the expression LOWER(name):

Index('idx_user_name_lower', func.lower(User.name))

With the index in place queries such as

session.query(User).filter(func.lower(User.name) == 'SOME_name'.lower())

may perform better, if LOWER(name) has high cardinality.

You could then encapsulate handling the lowercasing in a custom comparator:

# Verbatim from the documentation
class CaseInsensitiveComparator(Comparator):
    def __eq__(self, other):
        return func.lower(self.__clause_element__()) == func.lower(other)

class User(Base):
    ...
    @hybrid_property
    def name_insensitive(self):
        return self.name.lower()

    @name_insensitive.comparator
    def name_insensitive(cls):
        return CaseInsensitiveComparator(cls.name)

The comparator will apply func.lower() to both sides behind the scenes:

session.query(User).filter_by(name_insensitive='SOME_name')

is equivalent to

session.query(User).filter(func.lower(User.name) == func.lower('SOME_name'))
like image 114
Ilja Everilä Avatar answered Feb 26 '26 20:02

Ilja Everilä



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!