FTS5在通过SQLAlchemy上的SQLite中

huangapple go评论72阅读模式
英文:

FTS5 on sqlite through SQLalchemy

问题

以下是代码部分的翻译:

class CreateFtsTable(DDLElement):
    """表示用于索引给定表的CREATE VIRTUAL TABLE ... USING fts5语句。"""

    def __init__(self, table, version=5):
        self.table = table
        self.version = version


@compiles(CreateFtsTable)
def compile_create_fts_table(element, compiler, **kw):
    tbl = element.table
    version = element.version
    preparer = compiler.preparer

    vtbl_name = preparer.quote(tbl.__table__.name + "_idx")

    columns = [x.name for x in tbl.__mapper__.columns]
    columns.append('tokenize="porter unicode61"')
    columns = ', '.join(columns)

    return f"CREATE VIRTUAL TABLE IF NOT EXISTS {vtbl_name} USING FTS{version} ({columns})"

class WorkItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String, nullable=False)
    state = db.Column(db.String, nullable=False)
    title = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)

update_fts = DDL('''CREATE TRIGGER work_item_update AFTER INSERT ON work_item
  BEGIN
    INSERT INTO work_item_idx (id, type, state, title, description) 
    VALUES (new.id, new.type, new.state, new.title, new.description);
  END;''')
db.event.listen(WorkItem.__table__, 'after_create', CreateFtsTable(WorkItem))
db.event.listen(WorkItem.__table__, 'after_create', update_fts)

希望这对你有帮助。如果你有任何其他问题,可以随时提出。

英文:

My goal is to use SQLAlchemy to access a FTS5 table in sqlite3, with the virtual tables being updated at each insert. To this end, after doing some research (see this answer), I have coded the following:

class CreateFtsTable(DDLElement):
    """Represents a CREATE VIRTUAL TABLE ... USING fts5 statement, for indexing
    a given table.
    """

    def __init__(self, table, version=5):
        self.table = table
        self.version = version


@compiles(CreateFtsTable)
def compile_create_fts_table(element, compiler, **kw):
    tbl = element.table
    version = element.version
    preparer = compiler.preparer

    vtbl_name = preparer.quote(tbl.__table__.name + "_idx")

    columns = [x.name for x in tbl.__mapper__.columns]
    columns.append('tokenize="porter unicode61"')
    columns = ', '.join(columns)

    return f"CREATE VIRTUAL TABLE IF NOT EXISTS {vtbl_name} USING FTS{version} ({columns})"

class WorkItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String, nullable=False)
    state = db.Column(db.String, nullable=False)
    title = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)

update_fts = DDL('''CREATE TRIGGER work_item_update AFTER INSERT ON work_item
  BEGIN
    INSERT INTO work_item_idx (id, type, state, title, description) 
    VALUES (new.id, new.type, new.state, new.title, new.description);
  END;''')
db.event.listen(WorkItem.__table__, 'after_create', CreateFtsTable(WorkItem))
db.event.listen(WorkItem.__table__, 'after_create', update_fts)

With the SQLAlchemy echo enabled, I can see that the table gets properly created, as well as the virtual table and the trigger... but I do not see how to create a SQLAlchemy object that represents the FTS virtual table. On that answer there is a reference to using aliases, declaring a new table with a specific key... but I do not see why a new table would have to be created if the table is created through the CreateFtsTable.

How can I map an existing virtual table to an SQLAlchemy object?

答案1

得分: 2

以下是您要翻译的代码部分的翻译:

After the comment from Ilja Everil this is a version that works... together with some notes:
* flask_sqlalchemy creates all the tables at create_all
* the FTS tables can only be referenced after the tables have been created

This results in having a global module variable initialized to None, until the get_fts_references method gets called (which will set them)

therefore, in main.py I got:
db.init_app(app)
with app.app_context():
    db.create_all()

then, in the fts module, I have:

```python
class CreateFtsTable(DDLElement):
    """Represents a CREATE VIRTUAL TABLE ... USING fts5 statement, for indexing
    a given table.
    """

    def __init__(self, table, version=5):
        self.table = table
        self.version = version

@compiles(CreateFtsTable)
def compile_create_fts_table(element, compiler, **kw):
    tbl = element.table
    version = element.version
    preparer = compiler.preparer

    vtbl_name = preparer.quote(tbl.__table__.name + "_idx")

    columns = [x.name for x in tbl.__mapper__.columns]
    columns.append('tokenize="porter unicode61"')
    columns = ', '.join(columns)

    return f"CREATE VIRTUAL TABLE IF NOT EXISTS {vtbl_name} USING FTS{version} ({columns})"

WorkItemFts = None
def get_fts_references(target: db.Table, connection, **kwargs):
    base_model = next(c.entity for c in db.Model.registry.mappers if c.mapped_table.name == target.name)
    _temp_fts = db.Table(target.name+'_fts', db.metadata,
                         db.Column('id', db.Integer(), key='id', primary_key=True),
                         db.Column('type', db.Text()),
                         db.Column('state', db.Text()),
                         db.Column('title', db.Text()),
                         db.Column('description', db.Text()))
    globals()[base_model.__name__+'Fts'] = db.aliased(globals()[base_model.__name__], _temp_fts, adapt_on_names=True)

class WorkItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String, nullable=false)
    state = db.Column(db.String, nullable=false)
    title = db.Column(db.String, nullable=false)
    description = db.Column(db.String, nullable=false)

update_fts = DDL('''CREATE TRIGGER work_item_update AFTER INSERT ON work_item
  BEGIN
    INSERT INTO work_item_idx (id, type, state, title, description) 
    VALUES (new.id, new.type, new.state, new.title, new.description);
  END;''')
db.event.listen(WorkItem.__table__, 'after_create', CreateFtsTable(WorkItem))
db.event.listen(WorkItem.__table__, 'after_create', update_fts)
db.event.listen(WorkItem.__table__, 'after_create', get_fts_references)
英文:

After the comment from Ilja Everilä, this is a version that works... together with some notes:

  • flask_sqlalchemy creates all the tables at create_all
  • the FTS tables can only be referenced after the tables have been created

This results in having a global module variable initialized to None, until the get_fts_references method gets called (which will set them)

therefore, in main.py I got:

    db.init_app(app)
    with app.app_context():
        db.create_all()

then, in the fts module, I have:

class CreateFtsTable(DDLElement):
    """Represents a CREATE VIRTUAL TABLE ... USING fts5 statement, for indexing
    a given table.
    """

    def __init__(self, table, version=5):
        self.table = table
        self.version = version


@compiles(CreateFtsTable)
def compile_create_fts_table(element, compiler, **kw):
    tbl = element.table
    version = element.version
    preparer = compiler.preparer

    vtbl_name = preparer.quote(tbl.__table__.name + "_idx")

    columns = [x.name for x in tbl.__mapper__.columns]
    columns.append('tokenize="porter unicode61"')
    columns = ', '.join(columns)

    return f"CREATE VIRTUAL TABLE IF NOT EXISTS {vtbl_name} USING FTS{version} ({columns})"

WorkItemFts = None
def get_fts_references(target: db.Table, connection, **kwargs):
    base_model = next(c.entity for c in db.Model.registry.mappers if c.mapped_table.name == target.name)
    _temp_fts = db.Table(target.name+'_fts', db.metadata,
                         db.Column('id', db.Integer(), key='id', primary_key=True),
                         db.Column('type', db.Text()),
                         db.Column('state', db.Text()),
                         db.Column('title', db.Text()),
                         db.Column('description', db.Text()))
    globals()[base_model.__name__+'Fts'] = db.aliased(globals()[base_model.__name__], _temp_fts, adapt_on_names=True)

class WorkItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    type = db.Column(db.String, nullable=False)
    state = db.Column(db.String, nullable=False)
    title = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)

update_fts = DDL('''CREATE TRIGGER work_item_update AFTER INSERT ON work_item
  BEGIN
    INSERT INTO work_item_idx (id, type, state, title, description) 
    VALUES (new.id, new.type, new.state, new.title, new.description);
  END;''')
db.event.listen(WorkItem.__table__, 'after_create', CreateFtsTable(WorkItem))
db.event.listen(WorkItem.__table__, 'after_create', update_fts)
db.event.listen(WorkItem.__table__, 'after_create', get_fts_references)

huangapple
  • 本文由 发表于 2023年2月18日 16:54:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75492216.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定