4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TursoはベクトルDBとしても使える

4
Last updated at Posted at 2026-01-26

RAG(Retrieval-Augmented Generation)やAIエージェントを開発する際、「リレーショナルDB(ユーザーデータ)」「ベクトルDB(埋め込みデータ)」 の二重管理に疲れていませんか?

Edgeで動作するSQLite互換DBである Turso は、ネイティブでベクトル検索機能(Native Vector Search)をサポートしています。これにより、別途PineconeやQdrantを契約・管理することなく、Turso一本でアプリのデータ基盤を完結させることが可能です。

本記事では、TursoをベクトルDBとして使用する方法と、そのメリットについて解説します。

なぜTursoでベクトル検索なのか?

最大のメリットは 「SQLでメタデータフィルタリングができる」 点です。

通常のベクトルDBでは、日付やユーザーIDで検索範囲を絞り込む際に独自のJSON構文などを覚える必要がありますが、Tursoなら慣れ親しんだ WHERE 句や JOIN がそのまま使えます。

  • アーキテクチャの簡素化: RDBとVector DBのデータ同期(Consistency)を考える必要がない。
  • Edgeでの低レイテンシ: Tursoの強みであるEdge Replicaにより、ユーザーに近い場所で高速に検索が可能。
  • コスト効率: 既存のTursoプラン内で利用でき、追加のインフラコストがかからない。

実装手順

Turso(libSQL)でのベクトル検索の実装は非常にシンプルです。今回はOpenAIの埋め込みモデル(text-embedding-3-smallなど)で一般的な 1536次元 のベクトルを扱う想定で進めます。

1. テーブルの作成

ベクトルを格納するカラムには F32_BLOB という型を使用します。引数には次元数を指定します。

CREATE TABLE documents (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  content TEXT NOT NULL,
  user_id INTEGER NOT NULL,
  created_at INTEGER DEFAULT (unixepoch()),
  embedding F32_BLOB(1536)
);

2. ベクトルインデックスの作成

検索速度を向上させるため、ベクトルカラムに対してインデックスを作成します。これがないとフルスキャン(全件探索)になり、データ量が増えると遅くなります。

CREATE INDEX documents_idx ON documents (libsql_vector_idx(embedding));
  • libsql_vector_idx: ベクトルインデックスを作成するための内部関数です。

3. データの挿入(Insert)

データ挿入時は、アプリケーション側(TypeScriptやPythonなど)で生成したベクトル配列を、vector() 関数、あるいはバイナリとして渡します。

SQLでの例:

INSERT INTO documents (content, user_id, embedding)
VALUES (
  'Tursoはエッジで動くSQLite互換のDBです。',
  1,
  vector('[0.012, -0.003, 0.45, ...]') -- 実際の1536次元の配列
);

4. ベクトル検索(RAGのRetrieve)

ここがTursoの真骨頂です。vector_top_k 関数を使用して類似検索を行い、その結果を元のテーブルと JOIN します。

SELECT
  d.id,
  d.content,
  d.created_at,
  -- 類似度スコア(コサイン類似度ではない場合は距離)
  vector_distance_cos(d.embedding, vector('[0.015, ...]')) as distance
FROM vector_top_k('documents_idx', vector('[0.015, ...]'), 5) AS v
JOIN documents AS d ON d.id = v.id
WHERE d.user_id = 1; -- ★ここが重要

ポイント:

  • vector_top_k('インデックス名', クエリベクトル, 取得件数): 上位K件のIDを高速に返します。
  • JOIN documents ...: 取得したIDを元に、テキスト本文を取得します。
  • WHERE d.user_id = 1: SQLのWHERE句で検索範囲を特定のユーザーに限定しています。 これにより、「他人のデータが検索にヒットする」事故をSQLレベルで防げます。

TypeScript (LibSQL Client) での実装例

Node.js / TypeScript環境(@libsql/client)からは以下のように記述できます。

import { createClient } from "@libsql/client";

const client = createClient({
  url: "libsql://your-db.turso.io",
  authToken: "your-auth-token",
});

async function searchDocuments(queryVector: number[], userId: number) {
  const result = await client.execute({
    sql: `
      SELECT d.content, d.created_at
      FROM vector_top_k('documents_idx', vector(?), 5) AS v
      JOIN documents AS d ON d.id = v.id
      WHERE d.user_id = ?
    `,
    args: [JSON.stringify(queryVector), userId], // ベクトルは文字列化して渡す
  });

  return result.rows;
}

注意点

  • ハイブリッド検索: キーワード検索(Full Text Search)とベクトル検索を組み合わせる専用の関数はまだありません。SQLを駆使してスコアを計算するか、アプリ側でマージする必要があります。
  • 次元数: F32_BLOB で指定した次元数と、挿入/検索するベクトルの次元数は必ず一致させる必要があります。

まとめ

Tursoを使うことで、「通常のデータ管理」と「AI用のベクトル検索」を単一のSQLiteデータベースで完結できます。

特に、「特定のユーザーのデータ内だけでRAGを行いたい」「最新のデータだけを検索対象にしたい」といったメタデータフィルタリングが必須の要件において、SQLの表現力は圧倒的な武器になります。

これからRAGアプリやAIエージェントを作るなら、構成をシンプルに保てるTursoは非常に有力な選択肢です。


参考:

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?