61 lines
2 KiB
MySQL
61 lines
2 KiB
MySQL
|
|
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
|
||
|
|
);
|