You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

better-sqlite3虚拟表不支持异步生成器函数的替代方案咨询

解决better-sqlite3虚拟表不支持异步生成器的替代方案

better-sqlite3的虚拟表rows属性仅支持同步生成器,无法直接在其中执行异步操作(如文本分割、LLM嵌入生成)。以下是两种可行的替代方案:

方案1:同步触发器+异步任务队列

通过触发器将待处理数据写入队列表,再用独立的异步进程消费队列完成异步操作,避免阻塞SQLite的同步逻辑。

步骤1:创建相关数据表和触发器

-- 待处理队列表:存储需要异步处理的文本
CREATE TABLE IF NOT EXISTS PendingText (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  cleaned_text TEXT NOT NULL,
  processed BOOLEAN DEFAULT 0 -- 0=未处理,1=已处理,-1=处理失败
);

-- 目标存储表:最终保存分割后的文本块和嵌入向量
CREATE TABLE IF NOT EXISTS SplitText (
  chunk TEXT NOT NULL,
  embedding BLOB NOT NULL -- 用BLOB存储嵌入向量的Buffer
);

-- 假设主表为Documents,插入后触发写入待处理队列
CREATE TRIGGER IF NOT EXISTS after_document_insert
AFTER INSERT ON Documents
FOR EACH ROW
BEGIN
  INSERT INTO PendingText (cleaned_text) VALUES (NEW.cleaned_text);
END;

步骤2:实现异步队列消费逻辑

async function processPendingQueue(db) {
  while (true) {
    // 取出一条未处理的记录(用FOR UPDATE SKIP LOCKED避免并发冲突)
    const getPendingStmt = db.prepare(`
      SELECT id, cleaned_text FROM PendingText 
      WHERE processed = 0 
      LIMIT 1 
      FOR UPDATE SKIP LOCKED
    `);
    const pendingTask = getPendingStmt.get();

    if (!pendingTask) {
      // 无待处理任务时,短暂休眠后再轮询
      await new Promise(resolve => setTimeout(resolve, 1000));
      continue;
    }

    try {
      // 执行异步文本分割和嵌入生成
      const chunks = await splitter.splitText(pendingTask.cleaned_text);
      const res = await ollama.embed({
        model: 'nomic-embed-text',
        input: chunks
      });
      const embeddings = res.embeddings;

      // 批量插入结果到目标表(用事务保证原子性)
      const insertStmt = db.prepare('INSERT INTO SplitText (chunk, embedding) VALUES (?, ?)');
      db.transaction(() => {
        chunks.forEach((chunk, index) => {
          // 将嵌入向量转为Buffer存储
          insertStmt.run(chunk, Buffer.from(JSON.stringify(embeddings[index])));
        });
      })();

      // 标记任务为已处理
      db.prepare('UPDATE PendingText SET processed = 1 WHERE id = ?').run(pendingTask.id);
    } catch (err) {
      console.error(`处理任务ID ${pendingTask.id} 失败:`, err);
      // 标记任务为处理失败,可后续添加重试逻辑
      db.prepare('UPDATE PendingText SET processed = -1 WHERE id = ?').run(pendingTask.id);
    }
  }
}

// 启动异步队列处理器
processPendingQueue(db);

方案2:手动触发异步处理逻辑

放弃触发器和虚拟表,在插入主表数据后直接调用异步处理函数,适用于不需要自动触发、可控制处理时机的场景。

// 插入主表并触发异步处理的函数
async function addDocumentWithProcessing(db, cleanedText) {
  // 同步插入主表数据
  db.prepare('INSERT INTO Documents (cleaned_text) VALUES (?)').run(cleanedText);
  
  // 立即执行异步文本分割和嵌入生成
  await processTextToEmbeddings(db, cleanedText);
}

async function processTextToEmbeddings(db, cleanedText) {
  const chunks = await splitter.splitText(cleanedText);
  const res = await ollama.embed({
    model: 'nomic-embed-text',
    input: chunks
  });
  const embeddings = res.embeddings;

  // 批量插入结果到SplitText表
  const insertStmt = db.prepare('INSERT INTO SplitText (chunk, embedding) VALUES (?, ?)');
  db.transaction(() => {
    chunks.forEach((chunk, index) => {
      insertStmt.run(chunk, Buffer.from(JSON.stringify(embeddings[index])));
    });
  })();
}

核心原因说明

better-sqlite3的虚拟表基于SQLite的同步虚拟表机制,SQLite本身不支持异步操作,因此rows属性仅接受同步生成器,无法在其中执行await异步调用。上述方案通过将异步逻辑剥离到SQLite同步流程之外,完美绕过了这个限制。

内容的提问来源于stack exchange,提问作者punkish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:39:50