Module 04 · Lesson 06

Search engines and analytics stores

Two systems that are never the source of truth, and how to keep them from drifting.

Advanced 7 min read Updated 2026-11-01

What you will be able to do

  • Explain why full-text search and analytical scans are separate systems
  • Design an indexing pipeline with a defined freshness target
  • Describe how to rebuild an index without taking the site down
  • Choose between a search engine, a columnar warehouse, and a stream processor for a query need

Why not just query the database

LIKE '%phone case%' on a million-row table reads every row and cannot rank by relevance. A search engine builds an inverted index: for each term, the list of documents containing it. Matching becomes a lookup of sorted lists, ranking becomes scoring, and facets become counting per value.

Query needEngineWhy
Exact lookup by idPrimary databaseAlready indexed, strong consistency
Prefix and fuzzy text matchSearch engineInverted index plus analysers and typo tolerance
Relevance ranking, boosting, facetsSearch engineScoring model built for it
Sum revenue by region over a yearColumnar warehouseReads only needed columns, compresses, parallel scans
Count events per minute, liveStream processor plus time-series storeIncremental aggregation, not repeated scans
Never make the index the source of truth
Search engines are not built for transactional durability or multi-document consistency. If you store the only copy of a product in Elasticsearch, a mapping change or a bad merge can lose it. Keep the relational store authoritative and treat the index as rebuildable.

The indexing pipeline

Write to the database
The product row is committed in the primary store, alongside an outbox row in the same transaction.
Publish the change
A relay reads the outbox (or the replication log) and publishes an event with the row's new state.
Index idempotently
The consumer upserts by document id and stores the source version, so redelivery cannot create duplicates.
Define freshness
Measure and alert on indexing delay. A marketplace where a new listing takes 40 seconds to appear is a different product from one where it takes 40 minutes.

Change data capture straight from the database log is the most common production shape, because it cannot miss a write made by another service. The outbox pattern gets you the same reliability without log-level access. Either way, the message must carry enough state to rebuild the document without reading the source again.

Reindexing without an outage

  • Build the new index under a new name while the old one keeps serving.
  • Dual-write to both indexes during the build, or replay events from a point in time.
  • Switch the alias or pointer atomically once counts and spot checks agree.
  • Keep the old index for a rollback window, then delete it.

Mapping changes are the usual reason for this drill: renaming a field or changing an analyser requires rebuilding, because an inverted index cannot be edited in place.

Analytics: the shape of the question

Operational databases store rows for one entity at a time. Analytical questions scan many rows and few columns, which is why columnar stores exist: reading one column out of fifty is a fifth of the I/O, and similar values compress heavily. Pre-aggregating into hourly or daily rollups turns a billion-row scan into a few thousand rows.

Row store scan
All columns
Great for fetching one order, poor for summing a column
Columnar scan
Only needed columns
Often 10-50x less I/O for wide tables
Rollup table
1 row per hour per dimension
Turns a scan into a lookup
Stream aggregation
Constant memory per key
Counts and sums maintained incrementally
Two sentences that cover it
"Product search reads from a search index fed by change events, with a 10 second freshness target and a full reindex behind an alias swap. Analytics runs on a columnar warehouse fed by the same event stream, with hourly rollups so the dashboard never scans raw events."

Worth remembering

  • An inverted index maps terms to documents; a B-tree cannot do relevance ranking or tokenised matching.
  • The search index is derived data. The primary database stays the truth and the index can be rebuilt.
  • Freshness is a product decision: seconds for marketplace search, hours for an internal report.
  • Columnar storage and pre-aggregation are what make scanning billions of rows feasible.

Checkpoint

Answer all 3 questions to finish the lesson.
1. Why does `LIKE '%term%'` not scale on a relational table?
Without a known prefix the B-tree cannot narrow the search, so the engine evaluates the pattern on every row. Tokenised matching and scoring are what an inverted index provides.
2. A product is renamed and search still shows the old name ten minutes later. What should the design have specified?
The index is derived data with a propagation delay. Freshness is a requirement to be stated and monitored, not an emergent property.
3. Which workload is columnar storage best suited to?
Scanning a few columns over many rows is exactly what columnar layout and compression optimise; point lookups are better served by row stores.