Similarity Metrics: Cosine, Euclidean, Dot Product
Two vectors sit in high-dimensional space. How do you measure whether they're "close"? The answer depends on what you care about — direction or magnitude.

The core intuition
Imagine two arrows from the origin:
- ▸Cosine similarity asks: Are they pointing in the same direction?
- ▸Euclidean distance asks: How far apart are their tips?
- ▸Dot product asks: Are they pointing the same way AND how long are they?
Cosine similarity — the default for text
Cosine similarity measures the angle between two vectors, completely ignoring their length.
cosine(a, b) = (a · b) / (|a| × |b|)
Range: [-1, 1]
1.0 = identical direction (same meaning)
0.0 = perpendicular (unrelated)
-1.0 = opposite directions (opposite meaning)
Why magnitude doesn't matter for text: A short tweet and a long article about the same topic should be equally similar to a query. Cosine handles this correctly — the tweet isn't "less relevant" just because it has fewer words.
vec1 = [1, 2, 3] ← short document about topic X
vec2 = [2, 4, 6] ← same document, longer version (2× scale)
vec3 = [3, 1, -2] ← different topic entirely
cosine(vec1, vec2) = 1.0 ← perfect match (correct — same meaning)
cosine(vec1, vec3) = 0.27 ← distant (correct — different meaning)
If you used Euclidean distance here, vec1 and vec2 would appear far apart (distance = 3.74), which is wrong.
Euclidean distance — when magnitude matters
Euclidean is the straight-line distance between vector tips:
euclidean(a, b) = √( Σ(ai - bi)² )
Range: [0, ∞)
0 = identical vectors
larger = more different
Use Euclidean when the size of the vector carries real meaning:
- ▸Physical measurements (coordinates, sensor readings)
- ▸Count data (word frequency histograms)
- ▸Recommendation systems where activity level matters
For most text search, Euclidean is the wrong choice because document length pollutes the signal.
Dot product — direction + magnitude combined
dot(a, b) = Σ(ai × bi)
Range: (-∞, ∞)
larger = more similar
Dot product rewards both directional alignment and vector magnitude. This makes it useful when you want to give higher-weight results to "more prominent" items — for example, in collaborative filtering where users with more ratings should score higher than users with few.
For text search: only use dot product when your embedding model was specifically trained with a dot product objective, or when your vectors are already normalized (in which case dot product equals cosine similarity).
[Definition] When vectors are normalized to unit length (L2 norm = 1), dot product and cosine similarity give identical results. Always normalize your embeddings before indexing — then your index can use the faster dot product path internally while you reason about cosine semantically.
How cosine scores are reported
Raw cosine similarity ranges from -1 to 1, but search engines don't return this value directly. The score you see in search results is transformed to be monotonically decreasing — so that higher score always means more relevant, and the score never goes negative:
@search.score = 1 / (1 + cosine_distance)
where cosine_distance = 1 - cosine_similarity
This maps cosine similarity to a score range of 0.333 – 1.00:
- ▸Score 1.00 = perfect match (cosine similarity = 1.0)
- ▸Score 0.50 = cosine similarity = 0.0 (unrelated)
- ▸Score 0.333 = cosine similarity = -1.0 (opposite)
If you need the original cosine value from a search score:
cosine_similarity = 1 - ((1 - score) / score)
This is useful for setting quality thresholds — e.g., discard any result with cosine similarity below 0.7.
Quality benchmark across metrics
From a standard semantic search evaluation:
| Metric | NDCG@10 | Best for |
|---|---|---|
| Cosine similarity | 0.87 | Text and semantic search |
| Dot product | 0.84 | Models trained with dot objective |
| Euclidean (L2) | 0.82 | Physical / count data |

Manhattan and Chebyshev (specialist uses)
Manhattan (L1): Sum of absolute differences. Less sensitive to outliers than Euclidean. Used in anomaly detection and some clustering algorithms.
Chebyshev (L∞): Maximum absolute difference across any single dimension. Used in worst-case analysis — robotics, logistics, board-game pathfinding.
You'll rarely need these for text search.
Decision guide

When in doubt:
- ▸Normalize your vectors to unit length
- ▸Use cosine similarity (or dot product — they're identical at unit length)
- ▸Only deviate when you have a specific reason tied to your data type
That's the default that works across 95% of text search use cases.