Skip to content
Episode 105 · Quiet, peaceful, same thing

Embeddings and vector databases

Monday, 10:10: a guest searches Lisbon reviews for "quiet room to work remotely" and gets zero results. The perfect review says "peaceful, fast wifi, great desk", and 38% of review searches end the same way.

Embeddings turn meaning into map coordinates, so "find something similar" becomes "find what's nearby".

30 min intermediateEmbeddingsCosine similaritySemantic searchVector databasesApproximate nearest neighbour indexespgvector vs dedicated vector databases
  1. Problem

    A guest searches reviews for “quiet room to work remotely”. The best review says “peaceful, fast wifi, great desk”.

  2. Why the simple system fails

    Keyword search finds no shared words and returns nothing useful. Synonyms, paraphrases and other languages all defeat exact matching.

  3. Engineering concept

    Represent each text as a vector whose position encodes meaning; similar meanings land close together, so search becomes nearest-neighbour lookup.

  4. Technology

    Embedding models, cosine similarity, ANN indexes (HNSW), vector databases or pgvector.

  5. Trade-offs

    Approximate search trades a little recall for huge speed. A dedicated vector DB adds scale features but also another system to run and sync.

01

The problem

2 min
Monday, 10:10. Review search for Lisbon.
  1. Maya · Product manager
    A guest typed "quiet room to work remotely": 0 results. And 38% of all review searches return nothing.
  2. Maya · Product manager
    Meanwhile this review exists: "Peaceful, fast wifi, great desk. Perfect for a work week."
  3. You · Engineer at StayHub
    Our search matches words, and they share none. Add a synonym list?
02

The big idea

4 min
Search by meaning
The big idea

Similar meaning, nearby vectors: search becomes finding the nearest points.

Picture a library shelved by topic, so a book on quiet work retreats sits next to one on peaceful coworking cafés, though their titles share no word. You walk to where your question would be shelved and look at the nearest books.

03

How it works

5 min
How semantic search works
  1. 1
    Embed every review, once

    An model turns each text into a vector of 1,536 numbers, trained so similar meanings point in similar directions. All 4.2M reviews, stored next to the text. It's a small model, not a chat model.

  2. 2
    Embed the query

    "quiet room to work remotely" becomes a vector, with the same model. Each model draws its own map.

  3. 3
    Measure closeness

    : 1 = same meaning, 0 = unrelated. "Peaceful, fast wifi, great desk": 0.84. "Quiet pool area, loud bar at night": 0.41, despite sharing "quiet".

  4. 4
    Find the nearest, fast

    A uses an (approximate nearest-neighbour) index like : ~20 ms instead of ~1.5 s for checking all 4.2M, finding ~97% of the true top 10.

  5. 5
    Filter and return

    Top 10, filtered by city and rating. A multilingual model puts "silencioso, ótimo wifi" next to its English twin. That's .

04

Watch a decision being made

4 min
Worked example

Lina adds semantic review search

Find reviews by meaning across 4.2M reviews in 9 languages, filtered by city and rating, fast enough for a search box.

  1. 1One model, versioned

    A multilingual model, 1,536 dimensions, for both reviews and queries. Store its name and version with every vector.

    Why: Mixing models silently breaks every distance.

  2. 2Size it before building

    4.2M × 1,536 numbers × 4 bytes ≈ 26 GB raw, plus index overhead.

    Why: Memory decides where the index can live. 26 GB fits in a 256 GB replica.

  3. 3Index on a replica

    Embed all reviews once in a batch; new reviews on insert. HNSW with cosine distance in .

    sql
    ALTER TABLE reviews ADD COLUMN embedding vector(1536);
    ALTER TABLE reviews ADD COLUMN embedding_model text;
    CREATE INDEX ON reviews USING hnsw (embedding vector_cosine_ops);

    Why: Documents are embedded once, queries per search. The replica keeps vector load away from bookings.

  4. 4Check recall, then ship

    500 sample queries vs exact search: ~97% of the true top 10 found. Searches with no result: 38% → 4%.

    Why: ANN is approximate by design; measure how approximate before guests do.

The whole thing in one line: One versioned model, sized to fit a Postgres replica, HNSW for ~20 ms searches, recall checked before launch.
05

In real life

3 min
Where do the vectors live?

Spotify: its own nearest-neighbour libraries

  • Open-sourced Annoy, and later Voyager, ANN libraries built for its recommendations
  • Huge catalogue and traffic, a team to build and run it

Fits when vector search is core, at enormous scale.

Supabase: vectors inside Postgres

  • Promotes pgvector so users store embeddings in the Postgres they already have
  • Filtered and joined in SQL; no second system to sync

Fits millions, not billions, of vectors.

06

What goes wrong

3 min
A dedicated vector database by default

Dual writes, sync lag, deleted reviews still showing, another bill.

Instead

pgvector if you run Postgres; move when measurements justify it.

Expecting embeddings to match exact identifiers

"room 312" or "LIS-2280" carry little meaning; embeddings blur them.

Instead

Keyword search for exact terms, vectors for meaning: .

One vector for a 50-page document

It averages every topic and matches nothing well.

Instead

Split into passages and embed each one.

07

Trade-offs

3 min
SearchStrong atWeak at
KeywordCodes, names, numbers; cheap and explainableSynonyms, paraphrases, other languages
Semantic (vectors)Meaning, descriptive queries, across languagesExact identifiers; approximate recall
Hybrid (both, merged)Both kinds of queryTwo indexes; merging to tune
Monday, 15:00. Search after launch.
  1. Maya · Product manager
    Semantic search is so much better. Let's replace keyword search everywhere.
  2. Omar · On-call SRE
    Guests also search "room 312", "wifi code", "Hotel Alfama". Embeddings blur exact things.
  3. Maya · Product manager
    Two search systems means twice the work.
08

Your job as tech lead

4 min

Claude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.

Decisions you own
  • Which embedding model, and that queries and reviews always use the same one, with the model id stored per vector.
  • Where vectors live: a column in the Postgres you run, or a second database to keep in sync.
  • What happens when the model changes: re-embed everything into a new column, then switch both sides together.
Tell Claude Code
38% of review searches return nothing because we only match keywords. Add semantic search over the 4.2M reviews from the last 3 years using pgvector in our Postgres: a `vector(1536)` column, the model id per row, an HNSW cosine index, and a batch job embedding new and edited reviews. Queries use the same model and keep the hotel filter in the same SQL. No new database; don't touch booking tables. Plan first with backfill time and cost, then show top 5 for "quiet room to work remotely" in Lisbon, p95 latency, and recall@10 on 200 queries.
Check before you accept
  • The migration has a 1,536-dimension column, a cosine HNSW index and a model-id column.
  • The Lisbon example puts "peaceful, fast wifi, great desk" near the top.
  • p95 around 20 ms and recall@10 around 0.97, measured, not claimed.
Red flags
  • It adds a separate vector database with a sync job when pgvector covers 4.2M vectors.
  • It upgrades the embedding model for queries only.
  • It reports "search works" from one query, with no latency or recall numbers.
09

Check yourself

2 min
Q1/3Debugging
After upgrading to a new embedding model for queries, search results become nearly random. Documents were embedded last year. What happened?
How confident are you?

Next episode: duplicate-review detection flags "the room was clean" and "the room was not clean" as the same review, using a 0.8 threshold copied from a blog post.

Finished?

Mark it complete to update your skill map.

Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Interactive simulation, Visual diagram, Analogy, Check your understanding, Practice ladder, Explain it yourself.
10

Technical deep dive

From cosine to an index
Worked example

Lina adds semantic review search

Make review search find reviews by meaning, for 4.2M reviews in 9 languages, filtered by city and rating, fast enough for a search box.

  1. 1Pick one embedding model

    A multilingual model, 1,536 dimensions, used for both reviews and queries. Store its name and version with every vector.

    Why: Each model has its own space. Mixing models silently breaks every distance.

  2. 2Size it before building

    4.2M × 1,536 numbers × 4 bytes ≈ 26 GB of raw vectors, plus index overhead.

    Why: Memory decides where the index can live. 26 GB fits in a 256 GB replica; 2 TB would not.

  3. 3Embed everything once

    A batch job embeds all 4.2M reviews (a few hours, a few hundred euros at illustrative prices); new reviews are embedded on insert.

    Why: Documents are embedded once; only queries are embedded per search, which keeps search cheap.

  4. 4Index for speed

    Add an HNSW index with cosine distance on a read replica.

    sql
    CREATE EXTENSION IF NOT EXISTS vector;
    ALTER TABLE reviews ADD COLUMN embedding vector(1536);
    ALTER TABLE reviews ADD COLUMN embedding_model text;
    CREATE INDEX ON reviews USING hnsw (embedding vector_cosine_ops);

    Why: Exact search takes ~1.5 s per query; HNSW ~20 ms. The replica keeps vector load away from bookings.

  5. 5Check recall, then ship

    On 500 sample queries, compare HNSW results with exact search: ~97% of the true top 10 found. Searches with no result: 38% → 4%.

    Why: ANN is approximate by design. You measure how approximate before guests do.

The whole thing in one line: Lina embedded every review once with one versioned model, sized the vectors to fit a Postgres replica, indexed them with HNSW for 20 ms searches, and checked recall against exact search before launch.
OptionWhat it isStrengthsCosts / limits
pgvectorPostgres extension (HNSW, IVFFlat)No new system; SQL filters and joins; never out of syncShares resources with other queries; very large indexes need lots of RAM
PineconeFully managed vector database serviceNo operations; scales to large collectionsAnother vendor and data copy to sync; usage-based cost
WeaviateOpen-source vector database (self-host or managed)Built-in hybrid search, multi-tenancyAnother cluster to run or pay for
QdrantOpen-source vector database (self-host or managed)Fast filtered search, quantisationAnother system to run and keep in sync
All four can do filtered nearest-neighbour search. The real question is usually operational: one more stateful system, or not?
11

Interactive simulation

12

Visual diagram

Click a component

Semantic search over hotel reviews, with pgvector inside the existing Postgres.

13

Analogy

  • A giant map of every sentenceFinding a sentence's coordinatesEmbeddingsEmbedding a text
  • A giant map of every sentenceNeighbourhoods of similar topicsEmbeddingsClusters of similar meaning
  • A giant map of every sentence"Show me what's near here"EmbeddingsSemantic search
  • A giant map of every sentenceSame direction from the centreEmbeddingsCosine similarity
  • A giant map of every sentenceHighways first, then local streetsEmbeddingsAn HNSW index

Where the analogy breaks: a real map has two dimensions. Embeddings have hundreds or thousands, and the fast index can occasionally miss a house on a side street. That's why it's called approximate.

14

Check your understanding

Q1/3Reasoning
What is the cosine similarity between A = (3, 4) and B = (4, 3)? (Give a decimal.)
How confident are you?
15

Practice ladder

Step 1 · Trace

Rank three reviews by hand

Estimation Warm-up

A toy 2D embedding model gives:

  • Query "family-friendly": (2, 1)
  • Review A "great for kids, big pool": (4, 2)
  • Review B "romantic, adults only": (−1, 3)
  • Review C "spacious rooms for families": (3, 3)

Compute the cosine similarity of the query with each review, and rank them. Use cos = (a·b) / (|a| × |b|).

Step 2 · Complete

Finish the review search query

Code Practice

Complete Lina's pgvector query: the top 5 reviews similar to the query vector $1 for hotel $2, written in the last 12 months, with their similarity score. Fill in the three blanks.

SELECT r.id, r.text,
       ___ AS similarity              -- (1) turn cosine distance into similarity
FROM reviews r
WHERE r.hotel_id = $2
  AND ___                             -- (2) last 12 months
  AND r.embedding_model = 'reviews-embed-v1'
ORDER BY ___                          -- (3) nearest first
LIMIT 5;
Step 3 · Solve

Size the policy search

Estimation Practice

Next, the concierge needs to search hotel policy documents by meaning. 9,000 hotels × about 220 passages each. The embedding model has 1,024 dimensions (float32). Searches are always filtered by hotel. Expected load: 300 searches a second at peak. StayHub runs Postgres replicas with 256 GB RAM.

Estimate the raw vector size, decide where the vectors should live, and say what you'd measure before launch.

Step 4 · Decide

Upgrade the embedding model?

Decision Stretch

The provider's embedding model v2 scores better on benchmarks. Upgrading means re-embedding all 4.2M reviews: about 6 hours of batch work and €400 at illustrative prices. Options:

  • A. Re-embed into a new column, run 500 real queries on both, switch queries and documents together if v2 wins.
  • B. Switch tonight: re-embed in place and switch queries when the job finishes.
  • C. Stay on v1.

Write a short decision: "We chose X over Y because…, accepting the risk of…".

16

Explain it yourself

Explain to a backend engineer how “quiet room to work remotely” can find a review that says “peaceful, fast wifi, great desk”, and how you'd store and query it.

0 words