Strategy/projects/files/papers_db/papers_tech_stack.md
+

papers_tech_stack

Scientific Papers Storage โ€” Detailed Tech Stack & Architecture

System Architecture (Detailed)

Layer 1: Storage Layer

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         S3-Compatible Object Storage         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Option A: AWS S3 Standard (MVP)             โ”‚
โ”‚  โ€ข โŸจMATH:0โŸฉ0.09/GB egress           โ”‚
โ”‚  โ€ข 99.99% durability, multi-AZ              โ”‚
โ”‚                                              โ”‚
โ”‚ Option B: Yandex Object Storage (EU/Russia) โ”‚
โ”‚  โ€ข S3-API compatible                        โ”‚
โ”‚  โ€ข GDPR compliant, local data residency     โ”‚
โ”‚  โ€ข ~$0.015/GB/mo (cheaper than AWS)         โ”‚
โ”‚                                              โ”‚
โ”‚ Option C: MinIO (Self-hosted, scale)        โ”‚
โ”‚  โ€ข No egress charges (internal network)     โ”‚
โ”‚  โ€ข Breakeven vs AWS at 7 months             โ”‚
โ”‚  โ€ข 2x redundancy (RAID-6): 3TB โ†’ 6TB HW    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ†“
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ†“                             โ†“
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚ PDFs & Blobs โ”‚          โ”‚ Metadata Cache   โ”‚
   โ”‚ (300GB-3TB)  โ”‚          โ”‚ (JSON, thumbnails)
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Recommendation: Start AWS S3, migrate to MinIO at 20TB+ with predictable access patterns.


Layer 2: Metadata & Indexing Layer

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ PostgreSQL 15 (Primary Metadata & Full-Text Search Index)โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Schema:                                                   โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚ โ”‚ papers (core table)                 โ”‚                 โ”‚
โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค                 โ”‚
โ”‚ โ”‚ โ€ข id (UUID PK)                      โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข doi (unique, indexed)             โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข title (text, FTS index)           โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข abstract (text, FTS index)        โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข authors (JSONB):                  โ”‚                 โ”‚
โ”‚ โ”‚   [{name, orcid, h_index, aff}]    โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข pub_year (int, indexed)           โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข journal (varchar)                 โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข keywords (text[], GIN index)      โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข s3_key (varchar โ†’ URL)            โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข citation_count (int)              โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข fwci (float) โ€” field norm. impact โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข normalized_citations (float)      โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข h_index_contrib (int)             โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข embedding_id (UUID โ†’ Qdrant)      โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข language (varchar: en, ru, zh)    โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข full_text_tsvector (tsvector FTS) โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข created_at, updated_at (timestamp)โ”‚                 โ”‚
โ”‚ โ”‚ โ€ข openaccess (boolean)              โ”‚                 โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚                                                          โ”‚
โ”‚ Indices:                                                โ”‚
โ”‚ โ€ข UNIQUE idx_doi                                        โ”‚
โ”‚ โ€ข GIN idx_keywords (text[])                             โ”‚
โ”‚ โ€ข GIST idx_full_text (tsvector)                         โ”‚
โ”‚ โ€ข IDX idx_pub_year                                      โ”‚
โ”‚ โ€ข IDX idx_fwci DESC (for ranking)                       โ”‚
โ”‚ โ€ข IDX idx_embedding_id                                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Instance: t3.medium (MVP) โ†’ r6i.xlarge (1M papers)      โ”‚
โ”‚ Storage: 100GB (MVP) โ†’ 500GB (production)               โ”‚
โ”‚ Backup: Automated snapshots (30-day retention)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Layer 3: Vector Search Layer

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Qdrant (Vector Database for Semantic Search)              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Collection: papers_embeddings                              โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚ โ”‚ Vector Payload Structure:                           โ”‚  โ”‚
โ”‚ โ”‚ {                                                    โ”‚  โ”‚
โ”‚ โ”‚   "id": "uuid",           // Qdrant point ID        โ”‚  โ”‚
โ”‚ โ”‚   "vector": [768],        // Dense embedding        โ”‚  โ”‚
โ”‚ โ”‚   "paper_id": "uuid",     // Link to PostgreSQL    โ”‚  โ”‚
โ”‚ โ”‚   "doi": "string",        // For filtering          โ”‚  โ”‚
โ”‚ โ”‚   "title": "string",      // Metadata              โ”‚  โ”‚
โ”‚ โ”‚   "year": 2024,           // For temporal search    โ”‚  โ”‚
โ”‚ โ”‚   "field": "AI",          // For sharding           โ”‚  โ”‚
โ”‚ โ”‚   "citation_count": 42    // For ranking boost      โ”‚  โ”‚
โ”‚ โ”‚ }                                                    โ”‚  โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                            โ”‚
โ”‚ Configuration:                                            โ”‚
โ”‚ โ€ข Vector dimension: 768 (sentence-transformers)           โ”‚
โ”‚ โ€ข Distance metric: cosine (optimal for embeddings)        โ”‚
โ”‚ โ€ข Index type: HNSW (Hierarchical Navigable Small World)   โ”‚
โ”‚ โ€ข Shard count: 1 (MVP) โ†’ 8+ (1M vectors)                 โ”‚
โ”‚ โ€ข Replication factor: 1 (MVP) โ†’ 3 (production)            โ”‚
โ”‚                                                            โ”‚
โ”‚ Performance (Qdrant benchmarks @ 50M vectors):             โ”‚
โ”‚ โ€ข Query latency (p99): ~100ms                             โ”‚
โ”‚ โ€ข Throughput: 41 QPS @ 99% recall                         โ”‚
โ”‚ โ€ข Memory: ~500GB for 50M vectors (384-dim)                โ”‚
โ”‚                                                            โ”‚
โ”‚ Instance: t3.large (MVP) โ†’ 3ร—t3.xlarge cluster (prod)     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Alternative: pgvector (if <100K vectors) โ€” simpler ops, integrated with PostgreSQL, slower @ scale


Layer 4: Ingestion Pipeline

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Ingestion Pipeline (Async, Fault-Tolerant)                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚  Upload โ†’ S3 โ†’ SQS/Redis Queue โ†’ Worker Tasks               โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 1: PDF Upload Handler                           โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ Input: PDF file (multipart/form-data)                โ”‚  โ”‚
โ”‚  โ”‚ Actions:                                             โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Validate mimetype (application/pdf)                โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Generate checksum (SHA-256)                        โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Upload to S3: /papers/{year}/{doi}.pdf             โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Queue extraction task (Redis)                      โ”‚  โ”‚
โ”‚  โ”‚ Output: S3 key, checksum, file_id                    โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                            โ†“                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 2: Metadata Extraction (PyMuPDF)                โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ Tool: PyMuPDF (fitz) โ€” 2-3ร— faster than PDFMiner     โ”‚  โ”‚
โ”‚  โ”‚ Extract:                                             โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Text (all pages)                                   โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Metadata: title, author, creation date            โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Page count, PDF version                            โ”‚  โ”‚
โ”‚  โ”‚ Retry: 3 attempts, exponential backoff               โ”‚  โ”‚
โ”‚  โ”‚ Fallback: Tesseract OCR (if corrupted/scanned PDF)   โ”‚  โ”‚
โ”‚  โ”‚ Output: structured dict โ†’ store temp in Redis        โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                            โ†“                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 3: Semantic Metadata Enrichment                 โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ Input: extracted text + metadata                     โ”‚  โ”‚
โ”‚  โ”‚ LLM Prompt: Extract structured metadata:             โ”‚  โ”‚
โ”‚  โ”‚   - Canonical title (normalize)                      โ”‚  โ”‚
โ”‚  โ”‚   - Authors (full names, affiliations)               โ”‚  โ”‚
โ”‚  โ”‚   - DOI / arXiv ID                                   โ”‚  โ”‚
โ”‚  โ”‚   - Abstract (first 500 chars if not in PDF)         โ”‚  โ”‚
โ”‚  โ”‚   - Keywords (5-10)                                  โ”‚  โ”‚
โ”‚  โ”‚ Model: Claude 3.5 Haiku (cheap) or GPT-4 (accurate)  โ”‚  โ”‚
โ”‚  โ”‚ Cost: ~$0.002/paper                                  โ”‚  โ”‚
โ”‚  โ”‚ Caching: Check CrossRef API first (free!)            โ”‚  โ”‚
โ”‚  โ”‚ Output: normalized metadata JSON                     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                            โ†“                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 4: Cross-Reference Enrichment                   โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ Lookup: CrossRef API (free tier: 50 req/sec)         โ”‚  โ”‚
โ”‚  โ”‚ Enrich:                                              โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Canonical DOI                                      โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Published journal/venue                            โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Publication year (authoritative)                   โ”‚  โ”‚
โ”‚  โ”‚ โ€ข ISSN/ISBN                                          โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Open access status (via unpaywall.org)             โ”‚  โ”‚
โ”‚  โ”‚ Fallback: arXiv API (for preprints)                  โ”‚  โ”‚
โ”‚  โ”‚ Timeout: 5 sec per paper (skip if slow)              โ”‚  โ”‚
โ”‚  โ”‚ Output: enriched_metadata                            โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                            โ†“                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 5: Embedding Generation                         โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ Input: title + abstract (max 512 tokens)             โ”‚  โ”‚
โ”‚  โ”‚ Model: sentence-transformers/all-MiniLM-L6-v2        โ”‚  โ”‚
โ”‚  โ”‚   (384-dim, fast, good recall on academic texts)     โ”‚  โ”‚
โ”‚  โ”‚ Alternative: all-mpnet-base-v2 (768-dim, slower)     โ”‚  โ”‚
โ”‚  โ”‚ Batch processing: 32 papers/batch for efficiency     โ”‚  โ”‚
โ”‚  โ”‚ Output: 384-dim vector [float32]                     โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                            โ†“                                 โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ STEP 6: Database & Vector Index Update               โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ PostgreSQL insert:                                   โ”‚  โ”‚
โ”‚  โ”‚ INSERT INTO papers (doi, title, authors, ...) ...    โ”‚  โ”‚
โ”‚  โ”‚ Qdrant upsert:                                       โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Point ID = paper.id (UUID)                         โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Vector = embedding                                 โ”‚  โ”‚
โ”‚  โ”‚ โ€ข Payload = {doi, title, year, fwci, ...}            โ”‚  โ”‚
โ”‚  โ”‚ Atomicity: Qdrant first (revertible), then PG        โ”‚  โ”‚
โ”‚  โ”‚ Output: paper_id (for search)                        โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                              โ”‚
โ”‚ Worker Configuration:                                       โ”‚
โ”‚ โ€ข Framework: Celery (async tasks) + Redis (queue)           โ”‚
โ”‚ โ€ข Workers: 4 (MVP) โ†’ 16 (production, per CPU)               โ”‚
โ”‚ โ€ข Timeout: 60 sec per task (with retry)                     โ”‚
โ”‚ โ€ข Concurrency: 2 concurrent papers/worker (GPU shared)      โ”‚
โ”‚ โ€ข Cost: ~$0.003 per paper (LLM + compute)                   โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Throughput: 500โ€“1000 papers/hour with 4 workers


Layer 5: Search & Query Layer

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ FastAPI Application (Search & Retrieval)                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                           โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚ โ”‚ Endpoint: POST /search (Hybrid)                    โ”‚  โ”‚
โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚ โ”‚ Request:                                           โ”‚  โ”‚
โ”‚ โ”‚ {                                                  โ”‚  โ”‚
โ”‚ โ”‚   "q": "transformer architectures",                โ”‚  โ”‚
โ”‚ โ”‚   "limit": 20,                                     โ”‚  โ”‚
โ”‚ โ”‚   "mode": "hybrid",     // full-text|semantic|... โ”‚  โ”‚
โ”‚ โ”‚   "filters": {                                     โ”‚  โ”‚
โ”‚ โ”‚     "year": [2020, 2026],     // range filter     โ”‚  โ”‚
โ”‚ โ”‚     "journal": ["Nature", "Science"],              โ”‚  โ”‚
โ”‚ โ”‚     "min_citations": 10,                           โ”‚  โ”‚
โ”‚ โ”‚     "open_access": true                            โ”‚  โ”‚
โ”‚ โ”‚   },                                               โ”‚  โ”‚
โ”‚ โ”‚   "sort_by": "relevance"  // or: citations|date   โ”‚  โ”‚
โ”‚ โ”‚ }                                                  โ”‚  โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                           โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚ โ”‚ Processing Logic:                                  โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 1. Query Parsing & Normalization                   โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Lemmatization (spaCy)                         โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Stop word removal                             โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Synonym expansion (optional)                  โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 2. Full-Text Search (PostgreSQL FTS)               โ”‚  โ”‚
โ”‚ โ”‚    SELECT id, title, fwci FROM papers              โ”‚  โ”‚
โ”‚ โ”‚    WHERE to_tsvector(full_text) @@                โ”‚  โ”‚
โ”‚ โ”‚      plainto_tsquery('english', q)                 โ”‚  โ”‚
โ”‚ โ”‚    ORDER BY ts_rank(...) DESC                      โ”‚  โ”‚
โ”‚ โ”‚    LIMIT 500  (get top candidates)                 โ”‚  โ”‚
โ”‚ โ”‚    Performance: ~50ms (indexed)                    โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 3. Semantic Search (Qdrant)                        โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Embed query: q โ†’ 384-dim vector               โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Query Qdrant:                                 โ”‚  โ”‚
โ”‚ โ”‚      {                                             โ”‚  โ”‚
โ”‚ โ”‚        "vector": query_embedding,                  โ”‚  โ”‚
โ”‚ โ”‚        "limit": 500,                               โ”‚  โ”‚
โ”‚ โ”‚        "filter": {...}   // year, journal, etc    โ”‚  โ”‚
โ”‚ โ”‚      }                                             โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Return: (paper_id, score 0-1)                 โ”‚  โ”‚
โ”‚ โ”‚    Performance: ~100ms                             โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 4. Hybrid Re-Ranking                               โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Combine scores: 0.3 ร— fts_rank + 0.7 ร— sem   โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Boost by FWCI (field-normalized citations)    โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Apply diversity penalty (avoid duplicates)    โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 5. Result Enrichment                               โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Fetch full metadata from PostgreSQL           โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Generate relevance snippets (title match)     โ”‚  โ”‚
โ”‚ โ”‚    โ€ข Add S3 download URL (signed, 1-hour expiry)   โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ 6. Response (with caching)                         โ”‚  โ”‚
โ”‚ โ”‚    {                                               โ”‚  โ”‚
โ”‚ โ”‚      "results": [                                  โ”‚  โ”‚
โ”‚ โ”‚        {                                           โ”‚  โ”‚
โ”‚ โ”‚          "id": "uuid",                             โ”‚  โ”‚
โ”‚ โ”‚          "title": "...",                           โ”‚  โ”‚
โ”‚ โ”‚          "authors": [...],                         โ”‚  โ”‚
โ”‚ โ”‚          "doi": "10.1234/...",                     โ”‚  โ”‚
โ”‚ โ”‚          "year": 2023,                             โ”‚  โ”‚
โ”‚ โ”‚          "citations": 42,                          โ”‚  โ”‚
โ”‚ โ”‚          "fwci": 2.3,  // field-normalized         โ”‚  โ”‚
โ”‚ โ”‚          "relevance_score": 0.87,                  โ”‚  โ”‚
โ”‚ โ”‚          "snippet": "...matching text...",         โ”‚  โ”‚
โ”‚ โ”‚          "pdf_url": "s3://...signed_url"           โ”‚  โ”‚
โ”‚ โ”‚        }                                           โ”‚  โ”‚
โ”‚ โ”‚      ],                                            โ”‚  โ”‚
โ”‚ โ”‚      "total_count": 1234,                          โ”‚  โ”‚
โ”‚ โ”‚      "search_time_ms": 342                         โ”‚  โ”‚
โ”‚ โ”‚    }                                               โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ”‚ Caching (Redis):                                   โ”‚  โ”‚
โ”‚ โ”‚ โ€ข Cache full queries (TTL: 1 hour)                 โ”‚  โ”‚
โ”‚ โ”‚ โ€ข Cache embeddings (TTL: 24 hours)                 โ”‚  โ”‚
โ”‚ โ”‚ โ€ข Invalidate on new paper added                    โ”‚  โ”‚
โ”‚ โ”‚                                                    โ”‚  โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                           โ”‚
โ”‚ Other Endpoints:                                         โ”‚
โ”‚ โ€ข POST /papers/upload (accept PDF)                       โ”‚
โ”‚ โ€ข GET /papers/{id} (metadata + download)                 โ”‚
โ”‚ โ€ข GET /papers/by-author/{name} (author search)           โ”‚
โ”‚ โ€ข GET /search/full-text (FTS only)                       โ”‚
โ”‚ โ€ข GET /search/semantic (semantic only)                   โ”‚
โ”‚ โ€ข GET /stats (ingestion metrics)                         โ”‚
โ”‚ โ€ข GET /health (liveness check)                           โ”‚
โ”‚                                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Latency Target: <500ms (p99) for 100K papers


Summary: Tech Stack Decision Matrix

Layer Component Why Chosen Alternatives
Storage AWS S3 Standard Fast setup, pay-as-you-go MinIO (self), Yandex (EU)
Metadata DB PostgreSQL 15 FTS + JSONB, proven, no lock MongoDB (schema-less), ClickHouse
Vector DB Qdrant 41 QPS @ 50M vectors, open-source pgvector (<100K), Weaviate, Pinecone
Embeddings all-MiniLM-L6-v2 384-dim, fast, good recall all-mpnet (slower), OpenAI (cost)
PDF Extract PyMuPDF 2-3ร— PDFMiner, accurate pdfplumber, PDFMiner
LLM Enrichment Claude 3.5 Haiku Cheap (~$0.002/paper), accurate GPT-4 (2ร— cost), open-source models
Queue Celery + Redis Battle-tested, simple Bull.js (Node), RabbitMQ
API FastAPI Async, auto-docs, Python Flask (slower), Django (overkill)
CI/CD GitHub Actions Free, integrated GitLab CI, Jenkins
Deployment Docker + ECS Container-native, scalable Kubernetes (overkill for MVP)

Cost Breakdown Per 100K Papers (Monthly)

Storage Layer:       โŸจMATH:1โŸฉ0.023/GB + requests)
Metadata DB:         $40  (RDS t3.medium: 1TB SSD)
Vector DB:           $60  (EC2 t3.large: Qdrant)
Compute/Queue:       $30  (EC2 t3.micro for workers)
Networking:          $45  (NAT Gateway, S3 egress)
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
TOTAL:              $222/mo

Breakdown per paper: $0.002/mo
Annual cost:        $2,664 for 100K papers

Deployment Command

# Development
docker-compose up -d

# Production (AWS)
terraform apply  # provision S3 + RDS + EC2
docker push my-registry/papers-api:latest
aws ecs update-service --cluster papers --service api --force-new-deployment
Choose icon