-- AppView database schema (initial) CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ===================================================== -- posts -- ===================================================== CREATE TABLE posts ( uri TEXT PRIMARY KEY, -- at://did/rkey/app.twi.post did TEXT NOT NULL, handle TEXT NOT NULL, rkey TEXT NOT NULL, collection TEXT NOT NULL, -- app.twi.post | app.bsky.feed.post text TEXT NOT NULL, cid TEXT NOT NULL, parent_uri TEXT, -- reply parent root_uri TEXT, -- thread root langs TEXT[], indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(), created_at TIMESTAMPTZ NOT NULL ); CREATE INDEX posts_did_idx ON posts (did); CREATE INDEX posts_created_at_idx ON posts (created_at DESC); CREATE INDEX posts_collection_idx ON posts (collection); CREATE INDEX posts_parent_idx ON posts (parent_uri) WHERE parent_uri IS NOT NULL; CREATE INDEX posts_root_idx ON posts (root_uri) WHERE root_uri IS NOT NULL; -- ===================================================== -- likes -- ===================================================== CREATE TABLE likes ( uri TEXT PRIMARY KEY, did TEXT NOT NULL, post_uri TEXT NOT NULL, post_cid TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL, indexed_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX likes_post_idx ON likes (post_uri); CREATE INDEX likes_did_idx ON likes (did); -- ===================================================== -- reposts -- ===================================================== CREATE TABLE reposts ( uri TEXT PRIMARY KEY, did TEXT NOT NULL, post_uri TEXT NOT NULL, post_cid TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL, indexed_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX reposts_post_idx ON reposts (post_uri); CREATE INDEX reposts_did_idx ON reposts (did); -- ===================================================== -- follows -- ===================================================== CREATE TABLE follows ( follower_did TEXT NOT NULL, subject_did TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL, indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (follower_did, subject_did) ); CREATE INDEX follows_subject_idx ON follows (subject_did); -- ===================================================== -- timeline cache (materialized per-user timelines) -- ===================================================== CREATE TABLE timeline_cache ( did TEXT NOT NULL, post_uri TEXT NOT NULL, score DOUBLE PRECISION NOT NULL, ranked_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (did, post_uri) ); CREATE INDEX timeline_did_score_idx ON timeline_cache (did, score DESC, ranked_at DESC); -- ===================================================== -- search (simple trigram) -- ===================================================== CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE INDEX posts_text_trgm_idx ON posts USING GIN (text gin_trgm_ops); -- ===================================================== -- jetstream cursor -- ===================================================== CREATE TABLE jetstream_cursor ( id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1), cursor BIGINT NOT NULL DEFAULT 0, updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); INSERT INTO jetstream_cursor (id, cursor) VALUES (1, 0);