-- ===================================================================== -- Schema: Paper knowledge base -- Layer 1 papers, chunks -> semantic search (pgvector) -- Layer 2 citations -> citation graph / discovery -- Layer 3 code_links -> provenance (C++ symbol <-> paper) -- -- Adjust EMBEDDING_DIM to match your model (see .env.example): -- BGE-M3 = 1024 | Qwen3-Embedding-0.6B = 1024 | Jina v4 = 2048 -- ===================================================================== CREATE EXTENSION IF NOT EXISTS vector; -- --------------------------------------------------------------------- -- Layer 1: Papers + full-text chunks -- --------------------------------------------------------------------- CREATE TABLE papers ( id TEXT PRIMARY KEY, -- canonical ID: arXiv ID or DOI openalex_id TEXT UNIQUE, -- e.g. W2741809807 bibkey TEXT UNIQUE, -- BibTeX key -> .bib + Doxygen @cite title TEXT NOT NULL, authors TEXT[], year INT, abstract TEXT, source_path TEXT, -- path to parsed .tex / .mmd file abstract_emb vector(1024), -- paper-level embedding (similarity) added_at TIMESTAMPTZ DEFAULT now() ); -- HNSW index for fast approximate nearest-neighbour search on abstracts. CREATE INDEX papers_abstract_emb_idx ON papers USING hnsw (abstract_emb vector_cosine_ops); CREATE TABLE chunks ( id BIGSERIAL PRIMARY KEY, paper_id TEXT REFERENCES papers(id) ON DELETE CASCADE, ord INT NOT NULL, -- position within the paper content TEXT NOT NULL, embedding vector(1024) ); CREATE INDEX chunks_emb_idx ON chunks USING hnsw (embedding vector_cosine_ops); CREATE INDEX chunks_paper_idx ON chunks (paper_id); -- Sparse / keyword hits for exact mathematical terminology (hybrid search). -- Dense (above) + full-text (below) combined = robust against math terms. CREATE INDEX chunks_fts_idx ON chunks USING gin (to_tsvector('english', content)); -- --------------------------------------------------------------------- -- Layer 2: Citation graph -- cited_id has NO foreign key ON PURPOSE: -- edges to not-yet-ingested papers are intentionally preserved — -- those are your discovery leads. -- --------------------------------------------------------------------- CREATE TABLE citations ( citing_id TEXT REFERENCES papers(id) ON DELETE CASCADE, cited_id TEXT NOT NULL, -- arXiv/DOI/OpenAlex ID of the target context TEXT, -- optional: citation context (S2) PRIMARY KEY (citing_id, cited_id) ); CREATE INDEX citations_cited_idx ON citations (cited_id); -- --------------------------------------------------------------------- -- Layer 3: Provenance (the "cleanly couple" goal) -- --------------------------------------------------------------------- CREATE TABLE code_links ( id BIGSERIAL PRIMARY KEY, symbol TEXT NOT NULL, -- e.g. 'dec::hodge_star' or 'file.cpp:120' paper_id TEXT REFERENCES papers(id) ON DELETE SET NULL, role TEXT, -- e.g. 'implements Thm 3.2', 'uses Eq 5' note TEXT, added_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX code_links_symbol_idx ON code_links (symbol); CREATE INDEX code_links_paper_idx ON code_links (paper_id); -- --------------------------------------------------------------------- -- Example query: discovery leads -- Papers referenced by multiple already-ingested papers but not yet -- collected themselves — ranked by how often they are cited locally. -- --------------------------------------------------------------------- -- SELECT cited_id, count(*) AS pull -- FROM citations -- WHERE cited_id NOT IN (SELECT id FROM papers) -- GROUP BY cited_id -- ORDER BY pull DESC -- LIMIT 20;