使用 SQLAlchemy 在 Postgres 中建立不区分大小写的索引

Yuv*_*dam 3 python postgresql sqlalchemy

考虑一个带有索引String字段的声明式 SQLAlchemy 模型:

class User(Base):
    name = Column(String(100), index=True, nullable=False)
Run Code Online (Sandbox Code Playgroud)

name字段区分大小写,这意味着应保留原始大小写,但应支持对索引进行高效的不区分大小写的查询。

实现这一目标并在 SQLAlchemy 中实现的最佳方法是什么?

lower()如果需要可以使用查询

session.query(User).filter_by(name=lower('SOME_name'))
Run Code Online (Sandbox Code Playgroud)

但这并不重要,只要解决方案优雅且高性能即可。

ILIKE由于性能要求,使用Postgres 级别的查询lower()是不可接受的,它们已经过测试,对于我的用例来说,在大型表上执行得不够快。

Ilj*_*ilä 6

创建一个对表达式 进行索引的函数索引LOWER(name)

Index('idx_user_name_lower', func.lower(User.name))
Run Code Online (Sandbox Code Playgroud)

使用索引进行查询,例如

session.query(User).filter(func.lower(User.name) == 'SOME_name'.lower())
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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

session.query(User).filter_by(name_insensitive='SOME_name')
Run Code Online (Sandbox Code Playgroud)

相当于

session.query(User).filter(func.lower(User.name) == func.lower('SOME_name'))
Run Code Online (Sandbox Code Playgroud)