【问题标题】:Create a Full Text Search index with SQLAlchemy on PostgreSQL在 PostgreSQL 上使用 SQLAlchemy 创建全文搜索索引
【发布时间】:2017-07-12 08:28:03
【问题描述】:

我需要使用 SQLAlchemy 在 Python 中创建一个 PostgreSQL 全文搜索索引。这是我在 SQL 中想要的:

CREATE TABLE person ( id INTEGER PRIMARY KEY, name TEXT );
CREATE INDEX person_idx ON person USING GIN (to_tsvector('simple', name));

现在在使用 ORM 时如何使用 SQLAlchemy 完成第二部分:

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)

【问题讨论】:

    标签: python postgresql python-3.x sqlalchemy flask-sqlalchemy


    【解决方案1】:

    您可以在__table_args__ 中使用Index 创建索引。如果需要多个字段,我还使用一个函数来创建 ts_vector 以使其更整洁和可重用。如下所示:

    from sqlalchemy.dialects import postgresql
    
    def create_tsvector(*args):
        exp = args[0]
        for e in args[1:]:
            exp += ' ' + e
        return func.to_tsvector('english', exp)
    
    class Person(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
    
        __ts_vector__ = create_tsvector(
            cast(func.coalesce(name, ''), postgresql.TEXT)
        )
    
        __table_args__ = (
            Index(
                'idx_person_fts',
                __ts_vector__,
                postgresql_using='gin'
            )
        )
    

    更新: 使用索引的示例查询(根据 cmets 进行了更正):

    people = Person.query.filter(Person.__ts_vector__.match(expressions, postgresql_regconfig='english')).all()
    

    【讨论】:

    • 能否举例说明如何查询向量?谢谢。
    • @sharez 最新版本,query.all() 抛出错误:Neither 'BinaryExpression' object nor 'Comparator' object has an attribute 'all'。你能提供替代方案吗?
    • @apaleja match 是一个运算符,因此它应该在 filter 方法中,例如:Person.query.filter(Person.__ts_vector__.match(expressions, postgresql_regconfig='english')).all()
    【解决方案2】:

    @sharez 的答案非常有用(尤其是当您需要连接索引中的列时)。对于希望在单个列上创建 tsvector GIN 索引的任何人,您可以使用以下方式简化原始答案方法:

    from sqlalchemy import Column, Index, Integer, String
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.sql import func
    
    
    Base = declarative_base()
    
    class Example(Base):
        __tablename__ = 'examples'
    
        id = Column(Integer, primary_key=True)
        textsearch = Column(String)
    
        __table_args__ = (
            Index(
                'ix_examples_tsv',
                func.to_tsvector('english', textsearch),
                postgresql_using='gin'
                ),
            )
    

    注意__table_args__Index(...) 后面的逗号不是样式选择,__table_args__ 的值必须是元组、字典或None

    如果您确实需要在多列上创建 tsvector GIN 索引,这里是使用text() 的另一种方法。

    from sqlalchemy import Column, Index, Integer, String, text
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.sql import func
    
    
    Base = declarative_base()
    
    def to_tsvector_ix(*columns):
        s = " || ' ' || ".join(columns)
        return func.to_tsvector('english', text(s))
    
    class Example(Base):
        __tablename__ = 'examples'
    
        id = Column(Integer, primary_key=True)
        atext = Column(String)
        btext = Column(String)
    
        __table_args__ = (
            Index(
                'ix_examples_tsv',
                to_tsvector_ix('atext', 'btext'),
                postgresql_using='gin'
                ),
            )
    

    【讨论】:

    【解决方案3】:

    @sharez 和@benvc 已经回答了这个问题。我需要让它与重量一起工作。这是我根据他们的回答做的:

    from sqlalchemy import Column, func, Index, String
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.sql.operators import op
    
    CONFIG = 'english'
    
    Base = declarative_base()
    
    def create_tsvector(*args):
        field, weight = args[0]
        exp = func.setweight(func.to_tsvector(CONFIG, field), weight)
        for field, weight in args[1:]:
            exp = op(exp, '||', func.setweight(func.to_tsvector(CONFIG, field), weight))
        return exp
    
    class Example(Base):
        __tablename__ = 'example'
    
        foo = Column(String)
        bar = Column(String)
    
        __ts_vector__ = create_tsvector(
            (foo, 'A'),
            (bar, 'B')
        )
    
        __table_args__ = (
            Index('my_index', __ts_vector__, postgresql_using='gin'),
        )
    

    【讨论】:

    • 感谢您使用op :D
    • 你能举例说明如何查询吗?我正在使用query.filter(Example.__ts_vector__.op("@@")(func.websearch_to_tsquery(FTS_CONFIG, search_term)).all()。但是,我总是得到一个空列表。
    【解决方案4】:

    感谢这个问题和答案。

    我想补充一点,以防人们使用 alembic 来管理版本 使用autogenerate 似乎没有检测到哪个创建索引。

    我们最终可能会编写自己的修改脚本,看起来像。

    """add fts idx
    
    Revision ID: e3ce1ce23d7a
    Revises: 079c4455d54d
    Create Date: 
    
    """
    
    # revision identifiers, used by Alembic.
    revision = 'e3ce1ce23d7a'
    down_revision = '079c4455d54d'
    
    from alembic import op
    import sqlalchemy as sa
    
    
    def upgrade():
        op.create_index('idx_content_fts', 'table_name',
                [sa.text("to_tsvector('english', content)")],
                postgresql_using='gin')
    
    
    def downgrade():
        op.drop_index('idx_content_fts')
    

    【讨论】:

      猜你喜欢
      • 2012-11-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      • 2018-07-22
      • 2021-11-13
      • 2018-04-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多