An AI-powered semantic search engine for personal PDFs. Search by meaning, not keywords — find information across all your documents and jump straight to the highlighted passage.
Live demo: querynest.apoorvm.com/demo/ — read-only, searches a fixed 18-document corpus, no sign-up. querynest.apoorvm.com has the landing page.
- Semantic Search: Understands meaning — search "operating system concepts" and find every relevant paper, even if those exact words never appear
- In-Document Highlighting: Relevant passages are highlighted in yellow directly in the PDF
- AI Answers with Citations: Generated answers include citations back to specific chunks/pages
- Hybrid Filtering: Combine semantic search with metadata filters (date range, document type)
- Natural Language Queries: Type "papers from 2020 to 2023" — date parsing happens automatically
| Module | Status |
|---|---|
| PDF extraction (pymupdf4llm) | Done |
| Document-aware chunking | Done |
| Embedding pipeline (fastembed / BAAI/bge-small-en-v1.5) | Done |
| Vector index (pgvector in Supabase Postgres) | Done |
| Hybrid search (semantic + date pre-filter) | Done |
| Natural language date parsing | Done |
| Date extraction from PDFs | Done |
| Background bulk upload | Done |
| FastAPI backend | Done |
| PDF storage abstraction (local disk / Supabase Storage) | Done |
| Public demo (lite backend + landing/demo frontend, deployed) | Done |
| Dev tools (chunk-viewer, dev-dashboard) | Done |
| Tests (139 passing) | Done |
| Module | Status |
|---|---|
| Answer generation with citations | Planned |
| PDF highlight annotation | Planned |
- Python 3.14+
- Node.js 18+ (for dev tools)
- PostgreSQL with pgvector extension (Supabase or local)
git clone https://github.com/Apoorv012/QueryNest-New.git
cd QueryNest-New
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
pip install -r requirements.txtCopy .env.example to .env and fill in your database URL:
cp .env.example .env# Supabase connection string — use the pooler string, not the direct db.<ref>.supabase.co
# host. The direct host is IPv6-only; Docker and most PaaS hosts (Render included) have no
# IPv6 route, so it fails with "Network is unreachable" outside a plain local Python process.
QUERYNEST_DATABASE_URL=postgresql://postgres.[ref]:[pass]@aws-0-[region].pooler.supabase.com:6543/postgres
# Storage mode: "supabase" or "local"
QUERYNEST_STORAGE_MODE=supabase
# Supabase Storage (PDF files) — separate from the Postgres connection above.
# Only needed when QUERYNEST_STORAGE_MODE=supabase.
SUPABASE_URL=https://[ref].supabase.co
SUPABASE_SERVICE_ROLE_KEY=[service role key, never the anon key]
SUPABASE_STORAGE_BUCKET=pdfs# Run the full backend (local admin use: ingest, seed, inspect — never deployed)
uvicorn core.api.main:app --reload
# Run the lite public backend (what's actually deployed to Render)
uvicorn core.api.public_main:app --reload --port 8001
# Run the CLI pipeline (ingest sample PDF)
python -m core.main
# Ingest a specific PDF
python -m core.main <pdf_path>The dev dashboard is a React app for uploading PDFs, searching, and inspecting results.
cd tools/dev-dashboard
npm install
npm run devOpens at http://localhost:5173.
Features:
- Upload PDFs (bulk, background processing with progress bar)
- Search with natural language queries (e.g. "transformer last 3 years")
- See all uploaded files with detected dates
- Override dates manually if needed
The chunk viewer is a separate tool for inspecting extraction and chunking output.
cd tools/chunk-viewer
npm install
npm run devOpens at http://localhost:5173 (run one tool at a time, or change the port).
The landing page + live demo, deployed to Vercel. Locally:
cd apps/demo
npm install
npm run devOpens at http://localhost:5174 — / is the landing page, /demo/ is the live search demo. Proxies /api to the lite public backend on :8001 in dev (see apps/demo/vite.config.ts).
| Method | Path | Description |
|---|---|---|
GET |
/ |
App info |
GET |
/health |
Health check |
GET |
/check-backend |
Same as /health, under a name ad-blockers don't filter |
POST |
/upload/bulk |
Upload PDFs (background processing) |
GET |
/upload/{job_id}/status |
Poll upload progress |
GET |
/documents |
List all documents for a user |
GET |
/documents/{id}/chunks |
Get chunks for a document |
PATCH |
/documents/{id}/date |
Override detected date |
GET |
/documents/{id}/pdf |
Fetch a document's PDF (?download=true to force download) |
POST |
/search |
Search with NL query + date filtering |
POST |
/eval/seed |
Re-seed the golden demo corpus for golden_user |
Everything here is scoped to golden_user server-side; no user_id is ever accepted from the client, and upload/eval/mutation routes simply aren't mounted.
| Method | Path | Description |
|---|---|---|
GET |
/health, /check-backend |
Health check |
GET |
/documents |
List the golden demo corpus |
GET |
/documents/{id}/pdf |
Fetch a demo PDF (?download=true to force download) |
POST |
/search |
Search the golden demo corpus (rate-limited) |
pytest # Run all tests (139)
pytest -v # Verbose output
pytest tests/query/test_parser.py # Single fileQueryNest-New/
├── core/ # Python engine
│ ├── ingest/ # PDF extraction + date extraction
│ ├── chunking/ # Document-aware chunking
│ ├── embedding/ # fastembed (BAAI/bge-small-en-v1.5)
│ ├── index/ # pgvector storage (Supabase / local)
│ ├── storage/ # PDF file storage (Supabase Storage / local disk)
│ ├── query/ # NL query parsing (date extraction)
│ ├── models/ # Data models
│ ├── eval/ # Golden-set retrieval evaluation
│ ├── api/ # FastAPI backends
│ │ ├── routes/ # Endpoint modules (upload, search, public_search, etc.)
│ │ ├── main.py # Full backend — local admin use, never deployed
│ │ ├── public_main.py # Lite backend — deployed to Render
│ │ ├── jobs.py # Background job tracking
│ │ └── store.py # In-memory store (chunk viewer)
│ └── main.py # CLI entry point
├── apps/
│ └── demo/ # Public landing + demo frontend, deployed to Vercel
├── tools/
│ ├── chunk-viewer/ # Inspect extraction + chunking output
│ └── dev-dashboard/ # Upload, search, manage documents
├── tests/ # Test suite (139 tests)
├── docs/ # Architecture decisions, evaluation
├── .github/workflows/ # Keep-alive ping for the Render backend
├── .env.example # Configuration template
├── Dockerfile # Public backend image (core.api.public_main)
├── render.yaml # Render deploy config
├── requirements.txt
└── pyproject.toml
The public demo runs on three services, all wired to the same Supabase project:
- Frontend — Vercel,
apps/demoas the project root, custom domainquerynest.apoorvm.com. - Backend — Render, Dockerfile-based web service (
render.yaml), runningcore.api.public_main. Kept warm by.github/workflows/keepalive.yml(pings/healthevery 10 min — Render's free tier spins down after ~15 min idle). - Data — Supabase Postgres (pgvector) + Supabase Storage (PDF files). The golden demo corpus is seeded/reseeded by running the full backend locally and calling
POST /eval/seed— the deployed public backend never writes anything.
Full rationale in docs/decisions.md (D15, D16) and docs/ARCHITECTURE.md's "Current Deployment" section — including the Supabase pooler-vs-direct-connection gotcha (the direct host is IPv6-only and unreachable from Docker/Render).
See docs/decisions.md for full rationale.
- D1: Document-aware chunking (heading-based, not semantic)
- D2: pgvector in Supabase Postgres for storage
- D3: Platform split (desktop offline/cloud, mobile/web cloud-only)
- D4: Embedding model — fastembed with BAAI/bge-small-en-v1.5 (384 dims, ONNX, 67MB)
- D5: Date extraction chain — user input → filename → PDF metadata → content → null
- D6: NL query parsing — regex-based date extraction from search queries
- D15: Public demo backend is a separate app, not an auth flag on the full one
- D16: PDF storage abstraction — same local/hosted split as the vector store
This project is under active development. License TBD.