2

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.

4

1 回答 1

4

创建索引表达式的功能索引LOWER(name)

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

使用索引到位查询,例如

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

LOWER(name)如果具有高基数,可能会表现更好。

然后,您可以在自定义比较器中封装处理小写:

# 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)

比较器将应用于func.lower()幕后双方:

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

相当于

session.query(User).filter(func.lower(User.name) == func.lower('SOME_name'))
于 2018-10-30T12:10:11.940 回答