import Database from "better-sqlite3";
import knex from "knex";
import * as sqlite_vec from "sqlite-vec";
import { CONFIG } from "./config.js";
import { Logger } from "./logger.js";
let db = null;
let knexDb = null;
/**
* SQLite Vector を拡張ライブラリとして読み込む
* @throws {Error} sqlite-vec の読み込みに失敗した場合
*/
function loadVectorExtension() {
try {
sqlite_vec.load(db);
Logger.debug('sqlite-vec extension loaded');
} catch (error) {
Logger.error('Failed to load sqlite-vec extension', error);
throw error;
}
}
/**
* テーブルスキーマを初期化
* @throws {Error} スキーマの初期化に失敗した場合
*/
function initializeSchema() {
try {
const sql = `
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY,
content TEXT NOT NULL,
path TEXT,
created_at TEXT DEFAULT (datetime('now', 'localtime')),
updated_at TEXT DEFAULT (datetime('now', 'localtime'))
);
CREATE VIRTUAL TABLE IF NOT EXISTS vec_items USING vec0(
id INTEGER PRIMARY KEY,
embedding FLOAT[${CONFIG.database.embeddingDim}]
);
CREATE INDEX IF NOT EXISTS idx_items_created_at ON items(created_at);
CREATE INDEX IF NOT EXISTS idx_items_path ON items(path);
`;
db.exec(sql);
Logger.debug('Database schema initialized', { embeddingDim: CONFIG.database.embeddingDim });
} catch (error) {
Logger.error('Failed to initialize database schema', error);
throw error;
}
}
/**
* データベースを初期化
* @throws {Error} データベースの初期化に失敗した場合
*/
export function initDb() {
try {
Logger.info('Initializing database');
// SQLite データベース接続
db = new Database(CONFIG.database.filename);
db.pragma('journal_mode = WAL');
Logger.debug('SQLite database connected', { filename: CONFIG.database.filename });
// sqlite-vec 拡張を読み込む
loadVectorExtension();
// スキーマを初期化
initializeSchema();
// knex 接続を作成
knexDb = knex({
client: "better-sqlite3",
connection: {
filename: CONFIG.database.filename,
},
useNullAsDefault: true,
});
Logger.info('Database initialization completed');
} catch (error) {
Logger.error('Failed to initialize database', error);
throw error;
}
}
/**
* エンベディング次元数を取得
* @returns {number} エンベディング次元数
*/
export function getEmbeddingDim() {
return CONFIG.database.embeddingDim;
}
/**
* データベース接続を取得(better-sqlite3)
* @returns {Database} SQLiteデータベース接続
* @throws {Error} データベースが未初期化の場合
*/
export function getDb() {
if (!db) {
throw new Error('Database not initialized. Call initDb() first.');
}
return db;
}
/**
* Knex クエリビルダを取得
* @returns {Object} Knex クエリビルダ
* @throws {Error} データベースが未初期化の場合
*/
export function getKnexDb() {
if (!knexDb) {
throw new Error('Knex database not initialized. Call initDb() first.');
}
return knexDb;
}
// エクスポート(後方互換性)
export { db as default };