Embeddings & Vector Databases
What embeddings are, cosine similarity, and how vector databases differ from normal databases.
What an embedding is
An embedding is a list of numbers (a vector) that represents the meaning of a piece of text (or an image, or audio), produced by a model trained specifically for this purpose. The key property that makes embeddings useful: texts with similar meaning end up with similar vectors, even if they don't share any of the same words.
For example, an embedding model would place these two sentences close together in vector space:
"The cat sat on the mat."
"A feline was resting on the rug."
Despite sharing almost no words in common, both sentences describe essentially the same situation, and a good embedding model captures that semantic similarity numerically, placing their vectors near each other. Meanwhile, "The stock market fell sharply today" would land far away from both, despite being a grammatically similar sentence, because its meaning is unrelated.
embedding_1 = embed("The cat sat on the mat.")
embedding_2 = embed("A feline was resting on the rug.")
embedding_3 = embed("The stock market fell sharply today.")
# embedding_1 and embedding_2 end up close together in vector space
# embedding_3 ends up far from both, despite similar sentence structure
Cosine similarity, briefly
Given two embedding vectors, cosine similarity measures how closely aligned their directions are, producing a score from -1 (opposite meaning) to 1 (identical meaning), largely independent of the vectors' raw magnitude. It's the standard way to turn "are these two pieces of text semantically similar?" into a single comparable number, and it's what a retrieval step actually computes, many times over, against every stored document, to find the best matches for a query.
from numpy import dot
from numpy.linalg import norm
def cosine_similarity(a, b):
return dot(a, b) / (norm(a) * norm(b))
cosine_similarity(embedding_1, embedding_2) # high, e.g. 0.89 — similar meaning
cosine_similarity(embedding_1, embedding_3) # low, e.g. 0.12 — unrelated meaning
What a vector database does differently
A normal (relational or document) database is built to find rows by exact or structured matches — WHERE customer_id = 42, or a keyword full-text search for specific terms. It has no native concept of "semantically similar but textually different."
A vector database is purpose-built to store embeddings and answer a fundamentally different kind of query: "given this query vector, find the k stored vectors closest to it" — approximate nearest-neighbor (ANN) search. The word "approximate" matters: doing an exact nearest-neighbor search means comparing the query against every single stored vector, which doesn't scale past a small collection. Vector databases use specialized indexing structures (for example, HNSW graphs) that make this search dramatically faster at the cost of a small, usually negligible, chance of missing the very closest match, in exchange for near-instant results across millions or billions of vectors.
A few real vector databases you'll encounter in practice:
- Pinecone — a fully managed, cloud-hosted vector database, popular for production RAG systems that don't want to run and scale their own infrastructure.
- pgvector — a PostgreSQL extension that adds vector storage and similarity search directly inside Postgres, useful when you want vector search alongside your existing relational data without standing up a separate system.
- Qdrant — an open-source vector database that can be self-hosted or used as a managed service, commonly used when teams want more control over deployment than a fully managed option offers.
| Traditional database | Vector database | |
|---|---|---|
| Query shape | Exact match / keyword search | "Find the k most similar vectors" |
| Underlying comparison | Equality, ranges, text match | Cosine similarity (or similar distance metric) |
| Index structure | B-trees, hash indexes | Approximate nearest-neighbor structures (e.g., HNSW) |
| Good at | Structured, exact lookups | Semantic/similarity search |
Common mistakes
- Assuming embeddings from different models are comparable — vectors from two different embedding models generally live in unrelated vector spaces; you must use the same embedding model to embed both your stored documents and your queries.
- Treating vector search as a full replacement for keyword search — it can miss queries that hinge on an exact rare term, code symbol, or product ID that doesn't carry strong "semantic" signal; many production systems combine vector search with traditional keyword search ("hybrid search").
- Not re-embedding stored documents if you switch embedding models — old vectors from a retired model won't be meaningfully comparable to new queries embedded with a different model.