*Last updated: August 2026*
Every team that builds a document assistant hits the same wall around week three. Vector search finds documents that are *about* the right thing but misses the exact clause, part number, or date the user asked for. Keyword search nails exact matches but returns nothing when the user phrases the question differently than the document phrases the answer. And the moment you try to fix both problems at once, you're maintaining two sources of truth — a relational database and a vector database — that quietly drift apart.
This guide covers the architecture we use to solve that problem end to end: hybrid search RAG over a document corpus, with a relational database and a vector index working as one system, cross-references between documents tracked as first-class data, and a conversational assistant that routes each question to the retrieval strategy that will actually answer it.
What is hybrid search in RAG?
Hybrid search in RAG (retrieval-augmented generation) is a retrieval strategy that runs a semantic vector search and a lexical keyword search in parallel, then merges the results into a single ranked list before passing context to the language model. The vector side catches meaning — "termination conditions" matches a clause titled "Grounds for Cancellation." The lexical side (typically BM25 or a SQL full-text index) catches precision — invoice numbers, statute references, model codes, names, and dates that embeddings blur together. Fused, the two cover each other's blind spots, which is why hybrid retrieval consistently outperforms either method alone on real document corpora.
That's the textbook definition. The part most guides skip is that a production document assistant needs a third leg: structured metadata in a relational database — permissions, document types, effective dates, and the cross-reference graph between documents. Hybrid search isn't just vector + keyword. Done properly, it's vector + keyword + SQL.
Why neither keyword search nor vector search is enough

The difference between semantic search and keyword search is the difference between *what a query means* and *what a query says* — and document corpora punish you for choosing one.
Where keyword search fails. Legal, technical, and operational documents rarely share vocabulary with the people questioning them. A user asks "can we exit the contract early?" — the document says "either party may terminate upon ninety (90) days written notice." Zero keyword overlap, perfect semantic match. Classic lexical search returns nothing and the user concludes the corpus doesn't contain the answer. It did.
Where vector search fails. Embeddings compress meaning, and compression destroys exactness. Ask a pure vector index for "section 8.3 of the master services agreement" and it will happily return sections 8.2, 8.4, and a thematically similar clause from a different contract entirely. Same story for SKUs, case numbers, dosages, and dollar amounts. Worse, these precision failures are silent: the assistant answers confidently from the almost-right passage, and nobody notices until it matters.
Where both fail together. Neither engine knows that the user is only cleared to see documents from their own business unit, that the 2023 policy was superseded in 2025, or that Exhibit B is an amendment *to* a specific master agreement. Those are relational facts. If your retrieval layer can't filter and join on them, you don't have a document assistant — you have a similarity toy.
The two-database problem: relational and vector, side by side

A vector database and a relational database are not competitors — they answer different questions about the same documents, and a serious system needs both answered.
The relational database owns facts and structure: document records, versions, effective dates, authors, access control, document type, workflow status — and, critically, the relationships between documents. Which amendment modifies which agreement. Which policy implements which regulation. Which report cites which dataset. These are joins, foreign keys, and constraints — things relational databases have done superbly for fifty years and vector stores barely do at all.
The vector index owns meaning: embeddings of document chunks that make "find me passages about early termination" answerable in milliseconds across a hundred thousand pages.
The trouble starts because both stores describe the same corpus. Every document that's added, edited, re-versioned, or deleted must be reflected in both — rows updated in one, chunks re-embedded in the other. Teams that treat the vector index as a fire-and-forget copy discover the drift months later, when the assistant confidently quotes a contract clause that was amended two quarters ago. The sync design in the next sections is not an implementation detail; it's the difference between a demo and a system.
Architecture: cross-referencing every document against your corpus

The highest-value capability in a document assistant — and the one almost no off-the-shelf tool delivers — is automatic cross-referencing: when a document enters the system, it gets connected to everything in the corpus it relates to. Here's the ingestion pipeline that makes it work:
- Parse and chunk. Split each document on its own structure — sections, clauses, headings — not fixed token windows. Structure-aware chunks keep clause boundaries intact, which matters when the answer *is* a clause.
- Embed every chunk into the vector index, carrying the document ID, section path, and version as metadata on each vector.
- Extract references and entities with an LLM pass: explicit citations ("as defined in Section 2 of the MSA"), named entities, dates, amounts, and defined terms. Write these into relational tables — a
document_referencesedge table turns your corpus into a queryable citation graph. - Cross-reference against the corpus. Run each new document's chunks as vector queries *against the existing index* to surface near-duplicates, overlapping clauses, and prior versions — then persist the strong matches as suggested links for human confirmation. New vendor contract comes in; the system immediately shows the three existing agreements with conflicting terms.
- Version, don't overwrite. Supersession is a relational edge (
supersedes/superseded_by), and retrieval filters to current versions by default while keeping history queryable.
The payoff compounds: the relational graph built in steps 3–5 becomes a retrieval signal of its own. When the assistant retrieves a clause, it can *join outward* — pulling the amendment that modifies it and the definitions it depends on — instead of hoping those passages happen to be semantically similar to the question. This is what we build in our RAG and LLM development practice, and it's the piece that turns "search over PDFs" into a system that understands how your documents relate.
One database or two? Postgres + pgvector vs a dedicated vector database

The most common architecture question we get on RAG builds: do we need a dedicated vector database for RAG, or can the relational database do both jobs?
Start with Postgres + pgvector. For corpora up to a few million chunks, PostgreSQL with the pgvector extension is the pragmatic answer — and it dissolves the two-database problem entirely. Embeddings live in a column *next to* the metadata they describe; one transaction updates the document row, its chunks, and its vectors atomically; and a single SQL query combines vector similarity, full-text search, and metadata joins with no network hop and no sync pipeline. HNSW indexing in pgvector delivers production-grade approximate nearest-neighbor performance, and your access-control filter is a WHERE clause, not a metadata-replication scheme.
Reach for a dedicated vector database when the numbers demand it. Past tens of millions of vectors, under heavy concurrent query load, or with multi-tenant isolation requirements, purpose-built engines (Qdrant, Weaviate, Milvus, Pinecone) earn their operational cost with better recall-latency curves, quantization options, and horizontal scaling. You accept the sync problem in exchange for scale headroom.
The mistake is choosing by hype in either direction. Teams burn months operating a distributed vector cluster for 200,000 chunks that Postgres would serve in single-digit milliseconds — and other teams jam 80 million vectors into an undersized Postgres instance and blame RAG when retrieval crawls. Size the corpus honestly, project a year ahead, and pick the smallest architecture that survives the projection.
Keeping relational and vector data in sync

If you do run two stores, treat synchronization as a data-engineering problem, not an afterthought:
- One writer, one order. All document mutations flow through a single ingestion service. Nothing writes to the vector store directly.
- Transactional outbox. Commit the relational change and an "embedding pending" event in the same transaction; a worker consumes events and updates the vector index. If embedding fails, the event retries — the truth store never lies about what's been indexed.
- Re-embed on edit, delete on delete. Edited sections invalidate their chunk vectors; document deletion (or supersession) must actually remove vectors. Orphaned embeddings of deleted confidential documents are a compliance incident waiting for a retrieval query.
- Nightly drift audit. A scheduled job compares chunk counts and content hashes between stores and repairs mismatches. Cheap to run, and it converts "silent staleness" into a metric with an alert.
- Embedding version tags. When you upgrade the embedding model, mixed-version vectors corrupt similarity scores. Tag every vector with its model version and re-embed in the background before switching queries.
None of this is exotic — it's the same rigor you'd apply to any derived data store. The teams that skip it are the ones whose assistants degrade a little every week.
The assistant layer: talking to both worlds at once

The conversational layer is where hybrid retrieval either becomes seamless or leaks its plumbing to the user. The pattern that works is query routing with fused retrieval:
- Classify the question. A lightweight LLM step tags each user turn: structured ("how many contracts renew in Q4?"), semantic ("what do our policies say about remote data access?"), lookup ("show me clause 8.3 of the Acme MSA"), or mixed.
- Route accordingly. Structured questions compile to SQL against the relational store — counting, filtering, and joining are database jobs, and no embedding model should be guessing at them. Lookups hit lexical/full-text search. Semantic questions run hybrid retrieval: vector + BM25 in parallel.
- Fuse and rerank. Merge the parallel result lists with reciprocal rank fusion, then pass the top candidates through a cross-encoder reranker. RRF is trivially simple and hard to beat; the reranker adds the final precision that keeps irrelevant-but-similar chunks out of the context window.
- Answer with citations. Every generated claim carries a link back to the exact chunk — document, version, section — it came from. Because versions and supersession live in the relational graph, the assistant can say "per the 2025 revision, which replaced the clause you're quoting" instead of silently blending old and new.
Multi-turn conversation adds one more requirement: the router has to resolve references like "and what about the earlier version?" against dialogue state before retrieval. That's conversational AI engineering, not prompt magic — the assistant is an agent with tools for each retrieval mode, not a single prompt with a context dump.
What this looks like in production
A build like this — ingestion pipeline, dual retrieval, citation graph, conversational layer — sounds like a year of platform work. It isn't, if it's scoped honestly: the systems we ship at NerdHeadz typically reach a production-grade first release in a small number of months with a senior team, because every layer above rides on boring, proven components — Postgres, pgvector or a managed vector store, an embedding model, an LLM, and disciplined data engineering between them. The hard part isn't any single technology. It's the architecture judgment: what goes in SQL, what goes in vectors, what gets extracted at ingest, and how the assistant decides which world to ask.
Working on a document-heavy product — contracts, compliance, research, operations manuals? Talk to our AI team about your corpus, and browse the document and data platforms in our portfolio. Still comparing partners? See our honest review of the top 10 AI development companies in 2026.
Hybrid search RAG is not a product you buy; it's an architecture discipline. The vector index answers what a question means, the relational database answers what is true and how documents relate, and the assistant layer earns its keep by knowing which to ask — then proving every answer with a citation. Start with Postgres and pgvector unless your scale genuinely says otherwise, treat store synchronization as real data engineering, and extract your cross-reference graph at ingest, because that graph is what elevates retrieval from similar to relevant.
Ready to build? NerdHeadz ships production RAG and document-AI systems in weeks, not months. Get a free estimate for your project.
“Precision failures are silent: the assistant answers confidently from the almost-right passage, and nobody notices until it matters.”
