import { createClient, type Client } from '@libsql/client';
import type { DanbooruPost } from '../types.js';
export class TursoService {
private client: Client;
constructor(url: string, authToken: string) {
this.client = createClient({
url,
authToken
});
}
/**
* Initialize database (create table if not exists)
*/
async initialize(): Promise<void> {
await this.client.execute(`
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY,
tags TEXT NOT NULL,
tag_count INTEGER NOT NULL,
rating TEXT NOT NULL,
score INTEGER NOT NULL,
fav_count INTEGER NOT NULL,
file_url TEXT NOT NULL,
pixiv_id INTEGER
)
`);
// Create indexes for efficient filtering
await this.client.execute(`
CREATE INDEX IF NOT EXISTS idx_rating ON posts(rating)
`);
await this.client.execute(`
CREATE INDEX IF NOT EXISTS idx_score ON posts(score)
`);
await this.client.execute(`
CREATE INDEX IF NOT EXISTS idx_fav_count ON posts(fav_count)
`);
}
/**
* Upsert posts (INSERT OR REPLACE)
*/
async upsertPosts(posts: DanbooruPost[]): Promise<{ new_posts: number, updated_posts: number }> {
if (posts.length === 0) {
return { new_posts: 0, updated_posts: 0 };
}
// Get existing post IDs in a single query
const postIds = posts.map(p => p.id);
const placeholders = postIds.map(() => '?').join(',');
const existing = await this.client.execute({
sql: `SELECT id FROM posts WHERE id IN (${placeholders})`,
args: postIds
});
const existingIds = new Set(existing.rows.map(row => row.id));
// Use batch transaction for better performance
const batch = posts.map(post => ({
sql: `INSERT OR REPLACE INTO posts
(id, tags, tag_count, rating, score, fav_count, file_url, pixiv_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
args: [
post.id,
post.tag_string || '',
post.tag_count || 0,
post.rating || 'q',
post.score || 0,
post.fav_count || 0,
post.file_url || '',
post.pixiv_id || null
]
}));
await this.client.batch(batch, 'write');
// Count new vs updated posts
const newPosts = posts.filter(p => !existingIds.has(p.id)).length;
const updatedPosts = posts.length - newPosts;
return { new_posts: newPosts, updated_posts: updatedPosts };
}
}