top of page

Designing a Search System

  • Writer: Pradeep P
    Pradeep P
  • 3 days ago
  • 4 min read

Layer 6 · Post 3 of 26

Layer 6 — Modern systems · Post 65 of 88

A search system indexes documents, ranks matches, and returns results fast enough that typing still feels interactive.

What you'll learn

  • Why you almost never LIKE '%term%' on the primary database for product search

  • How an inverted index, an indexing pipeline, and a query service split writes from reads

  • The freshness vs relevance tradeoff interviewers expect you to name

The idea in one minute

Search is a derived index, not your source of truth. Documents live in the product DB (listings, messages, tickets). An indexer turns each document into tokens and posts them into an inverted index: term → list of document IDs (with positions, for phrases). A query tokenizes the user's string, looks up postings, ranks, and returns a page of IDs you hydrate from the DB or from stored fields.

Writers (admin, crawler, app) | v [ Source of truth DB ] --CDC / queue--> [ Indexer workers ] | v [ Search cluster ] ^ Clients --> [ Query API ] -------------------+ | ranking, filters, pagination | cluster down? degrade to DB or cached popular queries

Autocomplete is often a second, smaller structure (prefix terms, completion suggester), not a full ranked search on every keystroke.

Why it matters

If search is slow or wrong, users assume the catalog is empty. Interviewers want to hear index vs store, eventual consistency ("new listing appears in 2 seconds"), and ranking as a product decision, not a SQL ORDER BY.

Typeahead budgets are tens of milliseconds. You design for that, not for a 2-second "advanced search" page.

How it works

  1. Documents. You define a schema: which fields are searchable (title, body), which are filters (category, price, tenant_id), which are stored for snippets.

  2. Indexing. On create/update/delete, the app publishes an event or the indexer tails CDC. Workers analyze text (lowercase, stemming, n-grams for partial match). They write to the search engine (Elasticsearch/OpenSearch, Solr, Typesense, Vespa). Deletes must be real deletes or the index lies.

  3. Query. The API parses the query, applies tenant filters first (security: never search then filter in the app), requests a ranked window (from/size or search_after). The engine scores with BM25 plus your boosts (recency, popularity). You then fetch live prices from the primary DB if the index is allowed to be slightly stale.

  4. Ranking. Start with lexical (BM25). Add signals: click-through, business rules ("in stock first"). Learning-to-rank is a later layer — say so in an interview, do not pretend you need a neural model on day one.

  5. Failure. Cluster red: serve a degraded path (browse categories, cached top queries). Partial shards: some results missing — better to show a banner than silently incomplete inventory for a marketplace.

Near-real-time: the index is usually seconds behind. If the interview is "search as you type in Google Docs," you also talk about in-memory or per-document indexes. If it is "Amazon product search," bulk index plus incremental updates is the story.

A simple example

A seller edits a shoe title from "runner" to "marathon racing flat." The product DB commits. CDC emits product.updated. The indexer reanalyzes tokens, removes old postings, adds new ones. A shopper typing marathon hits the query API; the engine returns product_882, the API hydrates price and image from Postgres/Redis, and the card shows the new title. For one or two seconds after the edit, search might still say "runner." You state that SLA.

Typeahead for mar hits a completion index of popular queries and product prefixes, not a full BM25 of the catalog.

Common mistakes

Querying the OLTP database with full-text as the long-term plan. It works at 10k rows. It will not be your 100ms p99 at 50 million.

Indexing without tenant filters in the engine. A missed AND tenant_id = is a data leak.

Reindexing the world on every deploy of analyzers with no alias swap. Use index aliases: build v2, atomically point the alias, then drop v1.

Deep pagination with from=100000. Use search_after or cursor keys. Offset pagination is an interview trap.

Treating relevance as "whatever Elasticsearch defaults." Defaults are a start. Product search needs boosts and filters you can explain.

How this shows up in real systems

  • Elasticsearch / OpenSearch / Solr: inverted index as a service.

  • Algolia / Typesense: search as an API with tight typeahead SLAs.

  • Google / Bing: retrieval then ranking at a scale this post will not pretend you are building in 45 minutes — still use the same two-stage language (retrieve, then rank).

Layer 4's sharding shows up as index shards and replicas. Recommendations (next) reuse retrieval, then replace BM25 with a model.

Recap

  • Search is an inverted index fed by an async pipeline, not the primary DB.

  • Put authorization filters in the query. Hydrate volatile fields from source of truth.

  • Name freshness and pagination explicitly; they are the design.

Ranking documents for a query is close to ranking items for a user. That is recommendations.

Layer 6 · Post 3 of 26

Comments


About Me

DSC_7604.jpg

Hi, I am a software engineer from Bangalore, India. Love spending time on gaming and photography. This website is where I will ocassionally throw what comes to my mind. Hope it is useful or at least entertaining to you. :)

 

  • Instagram
  • Facebook
  • Twitter
  • LinkedIn
  • YouTube
  • 500px

© 2023 by Going Places. Proudly created with Wix.com

bottom of page