Distance metrics and when each is right

Your vector database asked you to pick a distance metric during setup. You picked cosine, because the tutorial did.

That’s usually the right answer, and it’s worth knowing why — and knowing the specific case where picking it silently discards information your embeddings were carrying.

The naive question

You have two vectors and you want a number saying how similar they are. What’s the obvious approach?

Treat them as points in space and measure the distance between them. That’s Euclidean distance:

d(a, b) = √( Σᵢ (aᵢ − bᵢ)² )

Straight-line distance, the Pythagorean theorem extended past three dimensions. Small means close, zero means identical.

Perfectly reasonable. It also gets a specific thing wrong for text embeddings, which is where the other metrics come from.

Where Euclidean goes wrong

Picture two vectors pointing in exactly the same direction, one twice as long as the other. Same direction, different magnitude.

Geometrically they’re some distance apart — the difference in their lengths. But if these are text embeddings, direction is generally what encodes meaning, and magnitude often encodes something less interesting: how long the text was, how confident the model was, an artifact of the architecture.

Two documents about the same subject, one a paragraph and one a page, can produce vectors pointing nearly the same way with quite different lengths. Euclidean distance calls them dissimilar. That’s usually not what you want.

The fix: measure the angle

Ignore magnitude, measure only direction. That’s cosine similarity — the cosine of the angle between the two vectors:

cos(a, b) = (a · b) / (‖a‖ ‖b‖)

The numerator is the dot product; the denominator divides out both magnitudes. What’s left depends only on direction.

It ranges from −1 (opposite) through 0 (perpendicular, unrelated) to 1 (same direction). Databases typically expose cosine distance = 1 − cos(a, b), so that smaller means closer and the metric behaves like a distance.

Now the paragraph and the page score as near-identical, which is the behaviour you wanted.

The dot product, and why it isn’t just cosine

The dot product alone:

a · b = Σᵢ aᵢ bᵢ

This is the cosine numerator without the normalization. It grows with alignment and with magnitude. Two vectors pointing the same way score higher if they’re longer.

Whether that’s a bug or a feature depends entirely on what your model put into the magnitude.

For most general-purpose text embedding models, magnitude is noise and you want it divided out. But some models are trained so that magnitude carries signal — a notion of importance, confidence, or term weight — and for those, dot product is the metric the model was designed for. Using cosine throws that information away.

This is the case worth checking rather than assuming. The rule is simple: use the metric the embedding model was trained with. Model documentation almost always states it. If it says the model is trained for cosine similarity or that outputs are normalized, use cosine. If it specifies inner product, use dot product and do not normalize.

The collapse: why cosine and dot product are often the same

Here’s the fact that resolves most of the confusion.

If all your vectors are normalized to unit length, cosine similarity and dot product are identical.

Look at the formula. If ‖a‖ = ‖b‖ = 1, the denominator is 1, and cosine similarity is the dot product. Not approximately — exactly.

Many embedding models emit normalized vectors already. If yours does, the choice between cosine and dot product is not a choice; the two produce the same ranking. Some engines exploit this: dot product is cheaper to compute, so if you normalize at insert time you get cosine’s semantics at dot product’s cost.

And one more collapse, less obvious: for normalized vectors, Euclidean distance produces the same ranking as cosine too. The relationship is

d(a, b)² = ‖a‖² + ‖b‖² − 2(a · b) = 2 − 2 cos(a, b)

when both are unit length. Euclidean distance is a monotonically decreasing function of cosine similarity, so sorting by one sorts by the other. The scores differ; the ordering doesn’t.

So: with normalized vectors, all three metrics rank identically. This is why picking cosine from a tutorial mostly works out. It’s also why “we switched metrics and nothing changed” is a common and entirely correct observation.

When the choice actually matters

Three cases:

1. Your vectors aren’t normalized and magnitude carries signal. Then cosine and dot product genuinely differ, and you should use whichever the model specifies. Getting this wrong degrades quality in a way that’s hard to spot — results stay plausible, they’re just worse.

2. You’re not using text embeddings. Feature vectors, geospatial coordinates, image descriptors with meaningful scale — for these, Euclidean is often correct and normalizing would destroy real information. The “always use cosine” advice is text-embedding advice that got over-generalised.

3. You’re mixing sources. Vectors from different models, or from the same model at different versions, don’t share a space. No metric fixes that. This is worth stating plainly because the symptom — plausible but wrong results, no errors — is easy to misdiagnose as a metric problem.

Practical consequences

Choosing the metric at collection creation is usually permanent. Most engines bake it into the index structure, so changing it means rebuilding. Get it from the model card before you create the collection.

Normalizing at insert time is cheap insurance. It makes the three metrics equivalent, removes a class of subtle bug, and lets the engine use the cheapest computation. Do it unless your model explicitly wants unnormalized inputs.

Don’t compare raw scores across metrics or collections. A cosine similarity of 0.8 and a dot product of 0.8 are not the same statement, and a score threshold tuned on one collection won’t transfer to another. Thresholds are properties of a specific setup and need re-tuning whenever the setup changes.

Score distributions are usually compressed. With high-dimensional text embeddings, similarity scores among unrelated documents often cluster in a narrow band well above zero rather than spreading across the full range. This trips people up when they set an absolute threshold expecting unrelated documents to score near zero. Look at your actual distribution before choosing a cutoff — and prefer ranking over thresholding where you can. This compression is closely related to the distance concentration discussed in the recall-latency curve.

Summary

Measures Sensitive to magnitude Use when
Cosine Angle No Text embeddings; the default
Dot product Alignment × magnitude Yes Model trained for inner product
Euclidean Straight-line distance Yes Real geometric/feature data

And the fact that saves the most confusion: normalize your vectors and all three rank the same.

The metric decides what “close” means. The index decides how you find close things without checking everything — that’s graph indexes and cluster indexes.