Retrieval & Hybrid Search8 min readยท

Sanity Context vs pgvector + Postgres FTS: Build vs Buy Hybrid Search

You ship a hybrid search stack on pgvector and Postgres full-text search, and for the first month it feels like a win.

You ship a hybrid search stack on pgvector and Postgres full-text search, and for the first month it feels like a win. Then the corpus grows, the embedding column drifts out of sync with the source rows, and someone asks why the agent cited a product spec that changed three deploys ago. Now you own a re-embedding job, a tsvector migration, a reranking heuristic, and an on-call rotation for a search system that was supposed to be a means to an end. That is the real cost of build-your-own hybrid retrieval: not the first query, but every schema change after it.

Sanity Context is the AI Content Operating System for retrieval, an intelligent backend that keeps hybrid search, embeddings, and content in one governed system instead of three you have to reconcile. This article reframes the pgvector-plus-FTS decision as what it actually is: a build-vs-buy call where "build" means permanently maintaining the plumbing between your vectors, your keyword index, and your content of record.

We will compare capabilities, developer experience, operations, enterprise needs, and lock-in, then give you a decision framework so the choice is deliberate rather than defaulted.

Illustration for Sanity Context vs pgvector + Postgres FTS: Build vs Buy Hybrid Search
Illustration for Sanity Context vs pgvector + Postgres FTS: Build vs Buy Hybrid Search

The build-vs-buy tension, stated honestly

pgvector plus Postgres full-text search is a genuinely good starting point. You already run Postgres, pgvector gives you cosine and inner-product distance on an embedding column, and tsvector plus ts_rank gives you keyword relevance. For a proof of concept over a static corpus, it is hard to beat the cost of adding an extension to a database you already operate.

The tension shows up at the seam. Hybrid search is not one index; it is two retrieval modes whose scores live in different number spaces, and the quality of your results depends entirely on how you blend them. In the build path, that blend is your code: you run a vector query, run an FTS query, normalize two incompatible score distributions, apply reciprocal rank fusion or a weighted sum you tuned by hand, and then keep that fusion logic honest as the data shifts underneath it. None of that is exotic, but all of it is yours to own forever.

Sanity Context sits on the buy side of this line, and the distinction that matters is not "managed versus self-hosted." It is that hybrid retrieval lives natively inside the Content Lake, Sanity's queryable content store, rather than being assembled on top of a general-purpose database. The keyword signal, the semantic signal, and the content itself are the same system. That is the difference between operating a search stack and querying one. The rest of this comparison is really about how much of that seam you want to be responsible for.

Capabilities: two indexes you glue vs one query that blends

On the build path, a hybrid query is an orchestration problem. You issue a pgvector nearest-neighbor search, issue a separate tsvector match, and then reconcile them in application code. Score normalization is the quiet trap: pgvector distances and ts_rank scores are not comparable, so you invent a fusion step, and every time you change the embedding model or the ranking config you retune it. Postgres can absolutely do both retrieval modes well; what it does not give you is a single expression that means "rank by keyword and meaning together."

Sanity Context does the blending inside one query. In GROQ, hybrid retrieval is `text::semanticSimilarity()` for the semantic signal and BM25 `match()` for the keyword signal, combined with `score()` and `boost()` so the ranking is expressed declaratively in the same query that returns the content. There is no second service to call and no fusion layer to maintain, because the fusion is a first-class part of the query language.

The deeper capability gap is freshness. In the build path, your embeddings live in a column that a job has to repopulate whenever the source text changes, which means there is always a window where the vector and the document disagree. Sanity Context uses dataset embeddings that are tied to the content, so when an editor updates a product description the embedding propagates within minutes and there is no separate vector pipeline to babysit. Retrieval quality degrades not because your fusion math is wrong but because your index is stale, and the buy path removes the most common source of staleness by construction.

Developer experience: schema drift is the tax you pay monthly

The build-path developer experience is excellent on day one and expensive on day ninety. Standing up pgvector is a CREATE EXTENSION and a column. The tax arrives with change. Add a field to the content model and you touch the table, the embedding job, the FTS trigger, and the query. Change the embedding model and you re-embed the whole corpus and rebuild the index. Every one of those is a migration with a backfill, and backfills over millions of rows are where weekend incidents come from.

The other DX cost is that your content model and your retrieval model are separate artifacts that must be kept in agreement by discipline alone. Postgres does not know that the `body` column is Portable Text or that `status` gates whether a document should be retrievable at all; you encode that in query predicates and hope every caller remembers them.

Sanity Context collapses that gap because the schema, the content, and the retrieval path are the same system. You model your business once in the Studio, and Agent Actions give you schema-aware APIs to generate, transform, and translate content without dropping to raw SQL and hand-written triggers. Because embeddings are derived from the content rather than stored beside it, a schema change does not fan out into four coordinated migrations. The developer experience difference is not "nicer API"; it is the absence of the recurring migration work that the build path quietly commits you to.

Operations: who is on call for retrieval quality

Run pgvector plus FTS in production and you inherit an operational surface most teams underestimate. HNSW or IVFFlat index parameters have to be tuned for your recall and latency targets, and those parameters interact with memory pressure on the same database serving your application. The re-embedding pipeline is a distributed job with its own failure modes: partial backfills, model-version skew between old and new rows, and silent quality regressions when a batch fails and nobody notices until answers get worse. FTS adds its own dictionary and stemming configuration to keep current. All of this is legitimate engineering; the question is whether retrieval infrastructure is the thing your team should be paged for.

The subtler operational risk is correctness under change. When embeddings and content live in different systems, the two can disagree, and a disagreement in a retrieval system does not throw an error. It just returns a plausible, wrong answer that an agent then presents with confidence. Debugging that means proving whether the content, the embedding, or the fusion logic was stale.

Sanity Context moves that surface off your plate. Production agents connect through the Sanity Context MCP endpoint, so retrieval is a hosted capability rather than a database you scale and patch. Because dataset embeddings are tied to content and refresh within minutes, the class of incident where the vector and the document disagree largely disappears. You are still responsible for content quality; you are no longer responsible for keeping two indexes and a fusion job mutually consistent at three in the morning.

Enterprise needs: governance, staging, and compliance

For a single-team internal tool, the build path's lack of governance is invisible. At enterprise scale it becomes the risk. Retrieval over customer-facing content is a compliance surface: you need access controls on who can change what an agent can retrieve, an audit trail of those changes, and a way to review a change before it reaches production. In the pgvector path, agent instructions and retrievable content are rows and config, and "governance" is whatever review discipline your team enforces on the migration that touches them. There is no native review step between "someone edited the corpus" and "the agent answers differently."

Sanity Context treats agent behavior as content to be governed rather than config to be deployed. Editors manage agent instructions in the Studio, and Content Releases let them stage changes to agent behavior the same way they stage a website launch, so a new instruction or an updated knowledge base can be reviewed and shipped as a reviewable release instead of a raw write. Roles and Permissions, Audit logs, and Studio Workspaces put the who-changed-what where a compliance team can see it.

On the compliance baseline, Sanity is SOC 2 Type II compliant and GDPR-ready, offers regional hosting and data residency options, and publishes its sub-processor list. That is a shorter path to an enterprise security review than assembling and evidencing equivalent controls around a self-managed Postgres and a bespoke embedding pipeline yourself.

Cost and lock-in: the maintenance bill nobody quotes

The build path looks cheaper because its costs are labor, not line items. pgvector is free, Postgres is already running, and no procurement is required, so the sticker price is zero. The real bill is the engineering time that never appears on an invoice: the person who owns the re-embedding pipeline, the tuning cycles on index parameters, the migrations every schema change triggers, and the on-call load when retrieval quality regresses silently. Multiply that by the tenure of the system and the "free" option is often the more expensive one.

The lock-in argument cuts the other way from the usual reflex. Owning the plumbing feels like independence, but you are locked into your own bespoke fusion logic, your own embedding job, and the tribal knowledge of whoever built them. When that engineer leaves, the lock-in is worse than any vendor's, because at least a vendor documents its system. Your custom RRF weighting tuned across two on-call incidents lives in one person's head.

Sanity Context concentrates that spend into a system you query instead of one you operate, and it keeps your content portable: the Content Lake is queryable over GROQ and the content model is yours to export. The honest framing is not "buy is cheaper." It is that build converts a one-time integration cost into a permanent maintenance line, and you should decide whether hybrid retrieval is a differentiator worth staffing or a capability worth buying so your team can build on top of it.

A decision framework you can actually apply

Choose the build path when the conditions genuinely favor it. If your corpus is small and mostly static, if you have deep Postgres operational expertise already on the team, if retrieval quality is not on your critical path, and if the content rarely changes, then pgvector plus FTS is a reasonable, cheap, and fully controllable answer. A prototype, an internal tool over a fixed document set, or a team that considers search tuning a core competency are all legitimate reasons to keep it in-house.

Choose Sanity Context when the failure modes above are the ones that will actually bite you. If your content changes frequently, so staleness between embeddings and documents is a live risk; if agents are customer-facing, so a confident wrong answer is a real cost; if you need governance, staging, and an audit trail for what agents can retrieve; and if you would rather your engineers build product than operate two indexes and a fusion job, then the buy path is not the lazy choice, it is the deliberate one.

The question underneath the whole comparison is what business you are in. Sanity Context is the intelligent backend for companies building AI content operations at scale, which means it maps to three pillars: model your business, automate everything, and power anything. The build path asks you to reconstruct a piece of each of those around a database that was not designed for it. If retrieval is your product, build it. If retrieval is how your product reaches the truth, buy the system that keeps it grounded and spend your team on what customers actually pay for.

Hybrid retrieval: Sanity Context vs a self-built and managed vector stack

FeatureSanitypgvector / Postgres FTSPineconeContentful
Hybrid keyword + semantic queryNative: text::semanticSimilarity() and BM25 match() blended with score() and boost() in one GROQ query.Two separate queries (pgvector distance and ts_rank) that you fuse and normalize in application code.Vector-native; keyword and hybrid via sparse-dense vectors, so lexical relevance is your responsibility to encode.No native hybrid search; requires an external search service wired in via the App Framework.
Embedding freshnessDataset embeddings are tied to content, so edits propagate within minutes with no separate vector pipeline.You own a re-embedding job; vectors drift from source rows until a backfill runs.You push embeddings via your own pipeline; freshness depends on how you sync upserts to content changes.Content lives here, but embeddings are generated and stored in the external vector store you maintain.
Content model integrationSchema, content, and retrieval are one system; model once in the Studio, query with GROQ.Content model and retrieval are separate artifacts kept in agreement by discipline and query predicates.Vector index only; content of record and its schema live in a system you connect separately.Strong structured content model, but retrieval logic is assembled outside it.
Governance and staging of agent behaviorEditors govern agent instructions in the Studio; Content Releases stage changes as reviewable releases.Instructions and corpus are rows and config; review is whatever discipline your migrations enforce.No content governance layer; access control is at the index and namespace level.Editorial workflow and roles exist for content; agent-behavior staging is not a native concept.
Operational surfaceHosted retrieval via the Sanity Context MCP endpoint; no index tuning or backfill jobs to run.You tune HNSW/IVFFlat parameters, run backfills, and are on call for retrieval quality regressions.Managed vector infrastructure, but you still operate the embedding pipeline and content sync.Managed CMS, plus the external search stack you must operate and keep in sync.
Compliance baselineSOC 2 Type II compliant, GDPR-ready, regional hosting and data residency, published sub-processor list.You assemble and evidence controls around self-managed Postgres and a bespoke pipeline yourself.Offers SOC 2 and enterprise controls for the vector service; content compliance sits elsewhere.Enterprise compliance for the CMS; the added search stack has its own posture to evidence.
Lock-in profileContent stays queryable over GROQ and exportable; retrieval is a capability you query, not code you maintain.Locked into bespoke fusion logic and tribal knowledge; portability is high but so is maintenance debt.Vector-format and API lock-in; migrating hybrid logic means rebuilding it elsewhere.CMS lock-in plus a coupled external search integration to unwind on any move.