February 13, 2026 · 12 min read

Build vs. Buy: When Custom Search Pays for Itself

We built custom search for Dossier — a legal knowledge management platform with 1M+ documents. 100ms query response. Synonym matching. Fuzzy search. $220k+/year recovered in search time savings. Here's when building makes sense and when it doesn't.

The Dossier team came to us with a search problem. Their legal knowledge management platform had 1M+ documents — case law, contracts, regulatory filings, internal memos. Their lawyers were spending 15-20 minutes per search session, sifting through irrelevant results from a basic LIKE query implementation.

They'd tried Algolia. Good product, wrong fit. Legal documents need synonym matching ("breach of contract" should find "contractual violation"), citation-aware ranking (documents referenced by other documents should rank higher), and jurisdiction filtering that standard text search doesn't handle. Algolia's relevance tuning couldn't model these domain-specific requirements without hacky workarounds that broke with every index rebuild.

So we built it. Custom search. PostgreSQL full-text search with domain-specific extensions. 100ms response times on 1M+ documents. The lawyers now find what they need in 30 seconds instead of 15 minutes. Across a 50-person legal team doing hundreds of searches daily, that's $220k+/year in recovered productivity.

But custom search isn't always the right call. Here's the framework we use to decide.


The Decision Framework: Build When Search Is the Product

Factor Build Custom Buy Off-the-Shelf
Search is... Core product feature / differentiator Secondary feature ("search the docs")
Domain matching Specialized (legal, medical, technical) Standard text (products, articles, users)
Relevance model Domain-specific ranking needed Standard BM25/TF-IDF sufficient
Monthly search cost >$5k/month on hosted service <$2k/month on hosted service
Timeline Can invest 8-12 weeks upfront Need search in <4 weeks

Dossier checked every "Build Custom" box. Legal search is the product. Standard relevance doesn't understand legal document hierarchies. And at their query volume, Elasticsearch managed service would have cost $4,000-$6,000/month — $48k-$72k/year for infrastructure alone.


What We Built: PostgreSQL Full-Text Search Done Right

The conventional wisdom says: "For real search, you need Elasticsearch." That's true for Netflix-scale (billions of documents, sub-10ms latency requirements, complex real-time aggregations). For 1M-10M documents with domain-specific relevance, PostgreSQL's built-in full-text search is not only sufficient — it's advantageous.

Why PostgreSQL Over Elasticsearch for Dossier

  • No data sync — With Elasticsearch, you maintain two copies of your data (PostgreSQL for CRUD + Elasticsearch for search) and keep them in sync. Every document update triggers a re-index. With PostgreSQL search, the search index IS the database. Zero sync lag, zero consistency bugs.
  • ACID guarantees — When a lawyer updates a document, the updated version is immediately searchable within the same transaction. No eventual consistency surprises.
  • Simpler ops — One database to back up, monitor, and scale. No Elasticsearch cluster management, no JVM tuning, no split-brain scenarios.
  • Cost — Dossier's search runs on the same RDS instance as the application database. Incremental cost: $0. Elasticsearch managed service for equivalent performance: $4,000-$6,000/month.

The Search Architecture

-- Custom text search configuration with legal synonyms
CREATE TEXT SEARCH DICTIONARY legal_synonyms (
  TEMPLATE = synonym,
  SYNONYMS = legal_terms  -- 'breach of contract' = 'contractual violation'
);

CREATE TEXT SEARCH CONFIGURATION legal_search (COPY = english);
ALTER TEXT SEARCH CONFIGURATION legal_search
  ALTER MAPPING FOR asciiword, word WITH legal_synonyms, english_stem;

-- Document search with weighted ranking
CREATE INDEX idx_documents_search ON documents USING GIN (
  setweight(to_tsvector('legal_search', coalesce(title, '')), 'A') ||
  setweight(to_tsvector('legal_search', coalesce(summary, '')), 'B') ||
  setweight(to_tsvector('legal_search', coalesce(body, '')), 'C') ||
  setweight(to_tsvector('legal_search', coalesce(citations, '')), 'D')
);

-- Search query with fuzzy matching fallback
SELECT id, title,
  ts_rank_cd(search_vector, query) AS relevance,
  ts_headline('legal_search', body, query, 'MaxFragments=3, MaxWords=30') AS snippet
FROM documents,
  plainto_tsquery('legal_search', $1) AS query
WHERE search_vector @@ query
  AND jurisdiction = ANY($2)  -- jurisdiction filter
ORDER BY relevance DESC
LIMIT 20;

Key features that make this work for legal search:

  • Weighted fields — Title matches rank higher than body text. Summary higher than footnotes. This mimics how lawyers think about document relevance.
  • Custom synonym dictionary — 2,000+ legal term mappings. "Plaintiff" finds "complainant." "Tort" finds "civil wrong." This single feature eliminated 60% of "no results found" queries.
  • Fuzzy matching fallback — If exact search returns fewer than 5 results, we automatically run a trigram similarity search to catch typos and close variations. Levenshtein distance ≤ 2.
  • Jurisdiction filtering — Legal documents are jurisdiction-specific. A New York attorney searching for precedent doesn't want California case law unless explicitly requested.

The Cost Comparison

Here's what Dossier's search actually costs compared to the alternatives:

Custom (PostgreSQL) Elasticsearch (Managed) Algolia
Build cost ~$40,000 (one-time) $10,000-$20,000 $5,000-$10,000
Monthly infrastructure $0 (uses existing DB) $4,000-$6,000/mo $3,000-$5,000/mo
Year 1 total ~$40,000 $58,000-$92,000 $41,000-$70,000
Year 2+ total ~$5,000/yr (maintenance) $48,000-$72,000/yr $36,000-$60,000/yr
Custom synonyms Full control Supported Limited

The breakeven point: custom search pays for itself in 8-12 months compared to managed Elasticsearch, and in 12-18 months compared to Algolia. After year 1, the savings compound — $40k-$60k/year in avoided infrastructure costs.

When NOT to build custom

If your search costs <$2k/month and standard relevance works for your use case, buy off-the-shelf. Algolia's developer experience is excellent for e-commerce and content search. Elasticsearch is powerful for analytics-heavy use cases. Custom only makes sense when the domain demands it AND the economics justify the upfront investment.


Performance: 100ms on 1M+ Documents

Speed matters for search. Legal professionals run hundreds of searches per day. Every 100ms of latency costs attention and productivity.

Dossier's search performance benchmarks (p95, measured over 30 days of production traffic):

  • Simple keyword search: 45ms
  • Multi-word phrase search: 72ms
  • Synonym-expanded search: 89ms
  • Fuzzy search fallback: 134ms
  • Jurisdiction-filtered search: 68ms

These numbers come from: GIN indexes on pre-computed tsvectors (searches don't compute vectors at query time), materialized search vectors updated via trigger on document change, result caching for common queries (Redis, 5-minute TTL), and connection pooling with PgBouncer.

We've built this search architecture three times now — for three different legal use cases. Each iteration made it faster and more configurable. The synonym dictionary alone has grown from 500 entries to 2,000+ across those three builds.


The ROI Math: $220k+/Year in Recovered Productivity

Dossier's client measured the impact over 6 months:

  • Before: Average search session: 15-20 minutes. Legal professionals spending 2+ hours/day on search. "No results found" rate: 35%.
  • After: Average search session: 30 seconds. Time spent on search: ~15 minutes/day. "No results found" rate: 4%.

Across 50 legal professionals at $150/hour loaded cost, saving 1.75 hours/day = $220k+/year in recovered billable time. The custom search investment ($40k build + ongoing maintenance) paid for itself in the first quarter.


Frequently Asked Questions

When should I build custom search?

When search is your core product differentiator, your domain needs specialized matching (legal, medical, technical), off-the-shelf relevance doesn't fit, or you're spending $5k+/month on hosted search. Use off-the-shelf when search is secondary and standard text matching works.

How much does custom search cost?

$30,000-$60,000 initial build (8-12 weeks), $500-$2,000/month to operate. Dossier: ~$40k build, $0 incremental infrastructure (uses existing PostgreSQL). Compare to $48k-$72k/year for managed Elasticsearch.

Can PostgreSQL replace Elasticsearch?

For 80% of SaaS search needs, yes. PostgreSQL's tsvector/tsquery with GIN indexes handles millions of documents at sub-100ms. You lose complex aggregations and real-time indexing at massive scale but gain zero data sync, ACID transactions, and simpler operations.

What response time should search achieve?

Under 200ms for p95 queries. Under 100ms is ideal for search-heavy workflows. Dossier achieves 45-134ms across different query types on 1M+ documents. Over 500ms causes measurable user abandonment.

How do you calculate search ROI?

Three factors: time savings (search speed × hourly cost × users), cost avoidance (vs. hosted search), and revenue impact (deals won on search quality). Dossier's $220k/year ROI came primarily from time savings across 50 legal professionals.


Next Steps

Need Custom Search for Your SaaS?

30-minute call. We'll assess your search requirements, compare build vs. buy for your specific case, and share what we learned building Dossier.

Book Free Architecture Call

Prefer email? office@oktopeak.com