feedsignal/crates/db/migrations/2026-08-21-000000_init/up.sql
Austin Schaefer a8d4599a03
Some checks failed
CI / check (push) Failing after 10s
Replace sqlx with Diesel + diesel-async for compile-time-checked queries
sqlx's query!/query_as! macros only check raw SQL strings against the live
schema; Diesel's table!-derived DSL type-checks query structure itself at
compile time. SQLite has no native async driver, so diesel-async wraps a
blocking SqliteConnection via SyncConnectionWrapper, pooled with bb8.

Migrations move from sqlx's single-file-per-migration format to Diesel's
up.sql/down.sql pairs, run transactionally via diesel_migrations against a
throwaway sync connection at boot (MigrationHarness needs a sync
Connection), giving revertable migrations that sqlx::migrate! doesn't
support.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 11:04:38 +02:00

60 lines
2 KiB
SQL

CREATE TABLE feeds (
id TEXT PRIMARY KEY,
url TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
last_fetched_at TEXT
);
CREATE TABLE articles (
id TEXT PRIMARY KEY,
feed_id TEXT NOT NULL REFERENCES feeds(id),
url TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
summary TEXT NOT NULL,
content TEXT,
published_at TEXT,
fetched_at TEXT NOT NULL,
topics TEXT NOT NULL DEFAULT '[]', -- JSON array of strings
embedding TEXT, -- JSON array of floats
embedding_score REAL,
llm_score REAL,
llm_rationale TEXT,
final_score REAL,
estimated_read_seconds INTEGER
);
CREATE INDEX idx_articles_feed_id ON articles(feed_id);
CREATE INDEX idx_articles_final_score ON articles(final_score);
-- Append-only interaction log. Affinities and any future scoring model are
-- derived from this; it is never mutated except to backfill dwell_seconds
-- once known.
CREATE TABLE reading_events (
id TEXT PRIMARY KEY,
article_id TEXT NOT NULL REFERENCES articles(id),
occurred_at TEXT NOT NULL,
outcome TEXT NOT NULL, -- 'impression' | 'opened' | 'starred' | 'dismissed'
dwell_seconds INTEGER
);
CREATE INDEX idx_reading_events_article_id ON reading_events(article_id);
-- Single-row-per-user table (multi-user support can add a user_id column
-- later); holds the current TopicAffinities snapshot as JSON so it doesn't
-- need to be recomputed from the full event log on every request. Rebuild
-- from reading_events at any time if the scoring model changes.
CREATE TABLE topic_affinities (
user_id TEXT PRIMARY KEY DEFAULT 'default',
affinities TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL
);
-- Free-text preferences the user writes explicitly (e.g. "I care about
-- distributed systems and Rust internals, not general AI news"), embedded
-- once and used as the anchor for the embedding-similarity stage.
CREATE TABLE preferences (
user_id TEXT PRIMARY KEY DEFAULT 'default',
description TEXT NOT NULL DEFAULT '',
embedding TEXT,
updated_at TEXT NOT NULL
);