Part 3 · The Building Blocks

How Search Works: The Inverted Index

LIKE '%running shoes%' scans every row and still misses “running shoe”. How the inverted index flips documents into term → document lists, how analysis makes search forgiving, how BM25 ranks results, and how search scales and stays in sync.

Lesson 198 min readintermediate#search#elasticsearch

On this page

  1. The Problem
  2. The Core Idea
  3. How It Works
  4. Trade-offs
  5. In the Real World
  6. Interview Questions
  7. Key Takeaways
  8. Further Reading

The Problem

Your shopping app has a search box. The first version is one line of SQL:

SQL
SELECT * FROM products WHERE name LIKE '%running shoes%';

It works with 1,000 products. With 5 million, each search takes seconds. The reason is that LIKE '%...%' (a pattern match with a wildcard at the start) cannot use a normal index. So the database reads every row. Users also complain about the results:

  • "Running shoe" (singular) finds nothing.
  • "Nike runing shoes" (a typo) finds nothing.
  • The best match shows up on page 4.

Real search needs a different data structure. It is called the inverted index. An inverted index is a lookup table that maps each word to the list of documents that contain that word. Real search usually runs in a separate system built around it.

The Core Idea

Open any textbook and turn to the index at the back:

Text
Photosynthesis ....... 45, 112, 214Pollination .......... 88, 90Protein .............. 12, 45, 301

You do not read every page to find "photosynthesis". You look up the word and jump straight to the pages that contain it.

A normal record says "document → words it contains". An inverted index flips that around: "word → documents that contain it". That is why it is called inverted.

How It Works

Building an inverted index by hand

Three products:

Text
Doc 1: "Red running shoes for men"Doc 2: "Blue running jacket"Doc 3: "Men's red leather shoes"

Step 1: Analyse the text. Break it into words (called tokens), make them lowercase, and cut each word down to a simple form (for example, "running" becomes "run"):

Text
Doc 1: red, running→run, shoes→shoe, for, menDoc 2: blue, running→run, jacketDoc 3: men's→men, red, leather, shoes→shoe

Step 2: Build the index. A term is one cleaned-up word stored in the index. For each term, list the documents (and the positions) where it appears. That list is called a posting list:

analysed terms: redshoe
Inverted index (term → doc IDs)
blue[2]
cotton[5]
hik[4]
jacket[2]
leather[3]
men[1, 3]
red[1, 3, 5]
run[1, 2]
shoe[1, 3, 4]
t-shirt[5]
waterproof[4]
women[5]
Results, ranked by matching terms
  1. doc 1Red running shoes for men2/2
  2. doc 3Men's red leather shoes2/2
  3. doc 4Waterproof hiking shoes1/2
  4. doc 5Red cotton t-shirt for women1/2

Green = contains every term (AND). The index is read once per term, with no scan of all documents, however many there are.

A live inverted index over five products. Type a query and watch it become term lookups, a posting-list intersection and a ranking. · interactive

Step 3: Search. For the query "red shoes":

  • Analyse it the same way: red, shoe.
  • Look up each term: red → {1, 3}, shoe → {1, 3}.
  • AND them together. This means you keep only the documents that are in both lists (an intersection): {1, 3}.
  • Rank the results (explained below). Both documents match. Doc 1 might rank higher if it is more relevant.

You do not scan every document. You only do a few quick lookups, even across millions of documents.

Text analysis: making search forgiving

  1. 1
    Raw text
    “Men's Red-Leather Running Shoes”
  2. 2
    Tokenise
    men's · red · leather · running · shoes
  3. 3
    Lowercase + stop words
    drop “the”, “for”, “a”
  4. 4
    Stem
    running → run, shoes → shoe, men's → men
  5. 5
    Index terms
    men · red · leather · run · shoe (+ synonyms, n-grams for autocomplete)
The analyser pipeline, applied identically to documents and to queries. · flow

The analyser is the part that turns raw text into index terms. Much of search quality comes from it. It must treat documents (when you index them) and queries (when you search) in the same way. These are its steps:

  • Tokenisation: split text into words. "Men's red-leather shoes" → men's, red, leather, shoes.
  • Lowercasing: Shoes = shoes.
  • Stop words: you can choose to drop very common words ("the", "for", "a"). They say little about the topic.
  • Stemming or lemmatisation: cut a word down to its root. Stemming chops off word endings with simple rules (running, runs → run; shoes → shoe). Lemmatisation uses a dictionary, so it can also turn ran into run. Now "running shoe" matches "run shoes".
  • Synonyms: words with the same meaning are treated as equal: sneakers ↔ trainers ↔ running shoes, and TV ↔ television.
  • N-grams / edge n-grams: an n-gram is a short piece of a word. Edge n-grams are pieces from the start of the word (sho, shoe, shoes). You index these pieces for autocomplete (suggestions while the user types) and partial matches.
  • Fuzzy matching: match words that are only a few edits apart. An edit is adding, removing or changing one letter. So runing still finds running. This handles typos.
  • Language-specific rules for Hindi, Japanese, German compound words (long words made from several words) and so on.

Ranking: which results come first?

Finding matches is half the job. Putting them in a good order (ranking) is the other half. Ranking gives each match a score. The best scores come first.

TF-IDF is a classic scoring formula. It multiplies two ideas:

  • TF (term frequency): how often the word appears in one document. A document that mentions "shoes" many times is probably more about shoes.
  • IDF (inverse document frequency): how rare the word is across all documents. Rare words matter more. Matching "leather" (in few documents) says more than matching "for" (in almost all).

BM25 is a ranking formula that improves on TF-IDF. It is the default in Elasticsearch, OpenSearch and Lucene. It stops giving too much credit for repeated words. It also adjusts for document length. So a short title that matches well beats a long page that mentions the word once.

Real products then add business signals (facts about your business that change the order):

  • popularity, ratings and sales,
  • recency (for news),
  • user location or personalisation,
  • boosting fields (boosting means giving one field more weight: a match in the title counts more than a match in the description),
  • and, more and more, machine-learning ranking models (models that learn the best order from past clicks).

Filters and facets

Search pages usually combine text search with structured filters: "red shoes" + size 9 + price under ₹3,000 + brand = Nike. A facet is a count of results for each value of a field. For example, "Nike (120), Adidas (95), Puma (40)" in the sidebar. Search engines compute facets for you. They store these structured fields in formats that are fast for filtering and counting.

One machine cannot hold the index for billions of documents. It also cannot answer thousands of queries per second. So search engines split the index into parts:

step 1 / 9
Client
Coordinator
Shard 1
Shard 2
Shard 3
  1. search “red shoes”, top 10
  2. query
  3. query
  4. query
  5. local top 10
  6. local top 10
  7. local top 10
    the slow one
  8. ↻ merge by score
  9. global top 10
Scatter-gather across shards. The response waits for the slowest shard — tail latency again. · sequence · step through
  • Shards: a shard is one slice of the data. Each shard holds part of the documents and their index.
  • Replicas: copies of each shard. They keep search working if a machine fails (availability). They also let more queries run at the same time.
  • Scatter-gather: a coordinator (the node that receives the query) sends the query to all shards. Each shard returns its best results. The coordinator merges them into a final top 10.

This connects to Lesson 8 on latency. A search that fans out to many shards is exposed to tail latency (the rare slow responses). The slowest shard decides the response time.

Near-real-time: new documents do not become searchable at once. They become searchable after a short refresh interval. In Elasticsearch the default is 1 second.

Keeping search in sync with your database

The search index is usually a copy of your data. The "source of truth" (the place where the correct data lives) is your main database. How do you keep the two in sync?

1. Dual writes. The app writes to the database and to the search engine.

  • It is simple. But if one write fails, the two systems disagree. This is easy to get wrong without noticing.

2. Queue or events (recommended). The app writes to the database and sends an event (a short message such as "product 991 updated") to a queue. A worker reads the event and updates the search index.

3. Change Data Capture (CDC). CDC is a method that reads the database's change log (the file where the database records every change) and sends each change to the indexer. This happens automatically, so no app code can forget to do it.

  1. App
  2. Database
    source of truth
  3. CDC
    reads the change log
  4. Queue
  5. Indexer
  6. Search cluster
    rebuildable
Keep the index as a derived copy, fed from the database's change log. · flow

An important rule: treat the search index as derived, rebuildable data. If it gets damaged, or you change the analyser, you can re-index (build the index again) from the database. Never make the search engine the only place where important data lives.

Keyword search matches words. But "cheap flights to Goa" and "budget air tickets to Goa" mean the same thing in different words. A keyword search may miss that.

Vector search finds results by meaning. A machine-learning model turns text (or images) into a list of numbers called an embedding. Similar meanings get lists that are close to each other. The search engine then finds the "nearest" lists. It uses approximate nearest neighbour (ANN) algorithms. These find close neighbours quickly, but not always the exact closest ones.

Many systems now use hybrid search. It combines keyword results (BM25) with vector results and then ranks them again. Many AI assistants also use search to find relevant documents before they answer a question. This is called retrieval-augmented generation, or RAG.

ToolNotes
Apache LuceneThe core search library behind Elasticsearch, OpenSearch and Solr
Elasticsearch / OpenSearchDistributed search and analytics engines built on Lucene. OpenSearch is a fork of Elasticsearch. Very widely used
Apache SolrMature Lucene-based search server
PostgreSQL full-text searchBuilt into Postgres. Great for small or medium needs without a separate system
Meilisearch, TypesenseSimpler engines focused on fast, typo-tolerant app search
Vector databases / extensionspgvector, and vector features in Elasticsearch/OpenSearch, among others

Trade-offs

  • ✅ Fast full-text search over huge data, plus relevance ranking, typo tolerance, synonyms and facets.
  • ❌ Another system to run. Search clusters need memory, tuning, monitoring and upgrades.
  • ❌ Eventually consistent. New or updated items appear in search after a short delay, not at once.
  • ❌ Sync complexity. Keeping the index in step with the database needs care (events, CDC, re-indexing).
  • ❌ Relevance tuning is endless. Analysers, synonyms and boosts need ongoing work and testing.

When not to add a search engine: small datasets, or simple needs like "search my 500 notes" or "filter products by category". PostgreSQL full-text search or even a well-indexed WHERE clause is often enough. Add Elasticsearch or OpenSearch when you need scale, relevance tuning, typo tolerance or rich facets.

In the Real World

Wikipedia uses Elasticsearch for its on-site search across hundreds of languages. It uses analysers made for each language, so that search works well in each one.

GitHub code search. GitHub built its own search engine for code (written in Rust). General-purpose search engines did not handle code well: symbols, exact substrings and a huge number of files. It shows that the type of content changes how you should design the index.

E-commerce search. Large online stores combine keyword search with filters (size, brand, price), facets, typo correction ("iphnoe" → "iphone") and ranking by popularity and sales. Search quality directly affects revenue: if users can't find it, they can't buy it.

Log search. Tools like the ELK stack (Elasticsearch, Logstash, Kibana) or OpenSearch are used by many companies to search through application logs. It is the same inverted-index idea, used on billions of log lines (more in Part 8, Observability).

AI assistants and "chat with your documents". Many products that let you ask questions about your own files use vector or hybrid search to find the most relevant passages first. Then they give those passages to a language model.

Interview Questions

Q1Why is LIKE '%term%' slow, and what does a search engine do instead?

A wildcard at the start of the pattern cannot use a B-tree index (the normal database index, which is sorted), so the database scans every row. A search engine builds an inverted index. It maps each analysed term to the documents that contain it. So a query becomes a few posting-list lookups and intersections. The work stays small even when the collection is very large.

Q2What is text analysis, and why must it match at index and query time?

Text analysis turns raw text into index terms. It splits text into tokens, lowercases them, removes stop words, stems them, and adds synonyms and n-grams. The query must go through the same steps. If it does not, “Running” never finds the stored term “run”.

Q3How does BM25 rank documents?

It gives each match a score. The score uses three things. First, term frequency, with smaller extra credit for each repeat. Second, inverse document frequency, so rare terms count more. Third, document length, so short and focused fields win. Products then add business signals on top: popularity, recency and field boosts.

Q4How do you keep a search index in sync with the database?

Treat the index as derived data. Send change events, or use CDC from the database log, to an indexer. Do not use dual writes, because the two systems can drift apart without anyone noticing. Always keep the ability to rebuild the whole index from the source of truth.

Q5When is PostgreSQL full-text search enough?

It is enough when data and query volume are modest, relevance needs are simple and you need no heavy facets. For example, searching an app's own notes or tickets. It saves you from running and syncing a second system. Move to Elasticsearch or OpenSearch when you need scale, typo tolerance, facets and relevance tuning.

Q6Keyword search vs vector search?

Keyword search matches words exactly (after analysis). It is precise, and you can explain why a result matched. Vector search matches meaning by using embeddings and nearest-neighbour search. So “budget air tickets” finds “cheap flights”. Hybrid search combines both and ranks again.

Key Takeaways

  • An inverted index maps term → documents, like the index at the back of a book, so search avoids scanning every document.
  • Text analysis (tokenising, lowercasing, stemming, synonyms, n-grams, fuzzy matching) makes search forgiving. BM25 plus business signals decides ranking.
  • Big search systems shard and replicate the index and use scatter-gather queries, so tail latency matters.
  • Keep search in sync with the database using events or CDC, and treat the index as rebuildable, derived data.
  • Start simple (PostgreSQL full-text search). Move to Elasticsearch or OpenSearch when you need scale and relevance features. Consider vector/hybrid search for meaning-based queries.

Further Reading

  • Introduction to Information Retrieval by Manning, Raghavan and Schütze (free online from Stanford)
  • The Elasticsearch reference documentation (the sections on text analysis and relevance)
  • The PostgreSQL documentation chapter "Full Text Search"
  • System Design Interview by Alex Xu (chapter "Design A Search Autocomplete System")
  • GitHub's engineering blog posts about building its code search engine

This ends Part 3. Next is Part 4: Databases & Data at Scale, starting with "SQL vs NoSQL: Picking the Right Data Model".

Published by Jainik · Last updated · How these lessons are made

Keep learning

Part of the study guide

Related series