Skip to content
RAG Explained Better

Cosine, Dot Product and Euclidean: Which Similarity to Use

Cosine, dot product and Euclidean decide how dense retrieval ranks chunk embeddings.

Similarity metrics decide how a RAG retriever ranks chunk embeddings against a query embedding. Cosine similarity scores the angle between vectors and cancels length; the dot product scores angle and magnitude together; Euclidean distance scores the straight-line gap between endpoints. When vectors are unit-length, cosine and the dot product rank identically, and the choice only changes results when magnitude is still in the numbers. Weaviate’s distance-metrics guidance, Elastic’s vector similarity measures post, and Pinecone’s vector-similarity learning guide all open on that ranking job. Parent stage: retrieval in RAG. The vectors themselves: embeddings.

What is cosine similarity?

Cosine similarity is the cosine of the angle between two embedding vectors: it keeps direction and cancels length. For vectors a and b it is (a · b) / (‖a‖ · ‖b‖), which equals cos θ. The score ranges from −1 (opposite directions) through 0 (orthogonal) to 1 (same direction). That is why cosine is the usual default for text RAG: two passages about the same topic can differ hugely in length, and cosine still treats them as close if they point the same way. Weaviate’s distance-metrics documentation (as of September 2026) and Elastic’s search-labs similarity post both centre that angle intuition for semantic text.

Cosine distance is defined as 1 − cosine similarity, so it ranges from 0 to 2 in Weaviate’s documentation and in Elastic’s measures write-up. Higher similarity is better; higher distance is worse. Sorting cosine similarity descending and cosine distance ascending are opposite operations. Mixing them silently returns your least-similar chunks first, which is a configuration bug that still produces “healthy looking” scores.

Ragwalla’s 2026 comparison of cosine, dot product and Euclidean for RAG repeats the same practitioner rule: prefer cosine when chunk length varies and meaning lives in direction. Steck, Ekanadham and Kallus (arXiv:2403.05440, 2024) argue that cosine on some embedding geometries is not always about semantic similarity in the everyday sense, and they discuss remedies when cosine misbehaves under matrix-factorization-style objectives. For standard dense text RAG the operational default remains cosine unless the model card says otherwise. Magnitude and training objective decide when that default is wrong, which is the next question.

What is the dot product for embeddings?

The dot product (inner product) of two vectors is a · b = Σ aᵢbᵢ: one sum of element-wise products, with no division by length. It responds to both direction and magnitude. Two vectors at the same angle score higher when either is longer. The theoretical range is unbounded.

Use the raw dot product when magnitude is genuine signal. Classic recommendation and matrix-factorization setups treat a longer item vector as higher popularity, a point Qdrant’s Essentials material and Miric’s 2025 similarity notes both make. For semantic text embeddings that is usually the wrong bias: without normalisation, longer or higher-norm chunks can outrank shorter, more relevant ones even at the same angle. That silent ranking bug shows up in production as “verbose but weak” chunks floating above precise ones.

When every vector is already unit length, the magnitude term disappears and the dot product is cosine similarity. That is why many engines expose a fast inner-product path after a one-time normalise. OpenAI’s Embeddings FAQ (as summarised in mid-2026 secondary citations; verify on the current FAQ) states that its embeddings are normalised to length 1 and recommends computing cosine via a plain dot product for speed. Weaviate’s documentation makes the same equivalence explicit when vectors are normalised. Dejan.ai’s cosine-versus-dot write-up frames the practical choice as whether you have already paid the normalisation cost at write time.

What is Euclidean distance in vector search?

Euclidean distance (L2) is the straight-line gap between two points: ‖a − b‖ = √Σ (aᵢ − bᵢ)². Identical vectors score 0; the distance grows without a fixed upper bound as points move apart. It is sensitive to both direction and magnitude, so a long document embedding and a short one on the same topic can look far apart even when cosine calls them a match. Elastic’s measures post and Pinecone’s learning guide both list L2 beside cosine and dot as the three defaults teams meet first.

Weaviate exposes squared Euclidean (l2-squared): the same ordering without the square root, which is cheaper at query time, as Weaviate’s distance-metrics documentation states (as of September 2026). For modern dense text embeddings Euclidean is rarely the first choice. On unit-length vectors the rankings lock to cosine anyway: squared L2 equals 2 − 2·cos θ, so minimum Euclidean and maximum cosine induce the same order. Reach for L2 when absolute position matters (spatial features, anomaly detection, clustering) or when the embedding model was trained with an L2 or contrastive loss that expects it.

Google’s machine-learning clustering material on supervised similarity treats distance choice as part of how neighbourhoods form, not as a free aesthetic preference. In RAG the neighbourhood is the retrieved set the generator will read, so a distance that over-weights length changes which passages enter the prompt. That is why the equivalence case matters before you pick a store default.

When are cosine and dot product the same?

Cosine similarity and the bare dot product are identical when both vectors are L2-normalised to length 1: the denominator in the cosine formula becomes 1, so cosine reduces to a · b. On those unit vectors, Euclidean ranking agrees too. The disagreement appears only while magnitude is still in the numbers. The arithmetic below is structural (the formulas applied to fixed vectors), not a published benchmark.

Same query, three candidates: cosine ties A with C; raw dot promotes C; Euclidean ranks A best
Vector Components Cosine similarity Dot product Euclidean distance
query [1, 0, 1] n/a n/a n/a
docA (same direction, same length) [1, 0, 1] 1.000 2.000 0.000
docB (orthogonal) [0, 1, 0] 0.000 0.000 1.732
docC (same direction, 2x length) [2, 0, 2] 1.000 4.000 1.414

Cosine calls docA and docC a perfect directional match. The raw dot product ranks docC above docA because it is longer. Euclidean prefers docA (distance 0) over docC (about 1.414) over docB (about 1.732). After normalize(query) and normalize(docC), both become [about 0.707, 0, about 0.707] and the dot product equals cosine at 1.

Production implication: normalise once at write time (and normalise the query the same way), then search with inner product for speed, or configure cosine and let the engine handle length. Qdrant’s Cosine collections automatically normalise vectors so Cosine, Dot and Euclidean give the same ranking for a fixed query once vectors are unit length. Never mix normalised and unnormalised vectors in the same index: scores become incomparable across rows. That equivalence is also why “which metric” often collapses to “what did the model card say about length”.

Which similarity metric should you use for RAG?

The decision rule shared across Weaviate, Qdrant and Pinecone’s vector-similarity guidance is: match the metric your embedding model was trained with. Read the model card. If the docs are unclear, cosine is the safe default for text RAG. Ragwalla’s comparison and Elastic’s scoring notes both land on the same default when the card is silent.

Unit-normalised text models are the common case. OpenAI’s Embeddings FAQ states that its embeddings (including text-embedding-3-small and text-embedding-3-large) are normalised to length 1, so cosine, dot product and Euclidean produce identical rankings; OpenAI recommends computing cosine via a plain dot product for speed (as of the FAQ cited in mid-2026 secondary sources; verify before you rely on it). Miric (2025) similarly lists OpenAI, Cohere, Voyage and Nomic as typically unit-length; confirm on the current model card for each. When rankings already agree, prefer the store’s fast path, often inner product after a one-time normalise.

Non-normalised models trained for dot product are the exception that bites. Sentence-Transformers’ msmarco-bert-base-dot-v5 is documented as non-normalised and intended for dot-product comparison (sbert.net MSMARCO docs). Using cosine on that model erases magnitude the training objective kept. Euclidean remains for spatial data, anomaly detection, clustering, or an L2-trained encoder, not the default for variable-length text chunks. Recommendation-style magnitude belongs in recs systems, not in passage retrieval for RAG. Choosing which embedding model to buy is a separate decision on embedding model selection.

Provider normalisation behaviour and recommended metrics change with model versions. Treat provider notes as dated secondary citations; the model card in front of you wins. Once the metric matches the model, the remaining failures are silent configuration bugs and high-scoring irrelevance, which are different diagnoses.

What happens if you pick the wrong similarity metric?

A mismatched metric does not throw. Scores still look healthy; the ranking is quietly wrong. That is why metric bugs survive demos: every neighbour has a number, and nobody notices the sort is inverted or magnitude-biased until labelled recall moves.

Four signatures show up in production. Unnormalised text plus raw dot product lets verbose, weakly relevant chunks float above short precise ones because magnitude leaks into the score. A dot-trained non-normalised model plus cosine discards the magnitude signal the model learned, so neighbours scramble relative to the training geometry. Cosine similarity sorted ascending, or cosine distance sorted descending, returns the least-similar chunks first. Mixed normalised and unnormalised vectors in one collection make scores incomparable across rows.

Matching the metric is necessary, not sufficient. Even a correct cosine score can put an irrelevant chunk at top-1 when the embedding space does not capture your domain. That failure is diagnosed on why RAG returns the wrong chunk, not by swapping distance functions again. Lexical misses on identifiers are a different channel: they belong on BM25 and hybrid search, not on another cosine tweak.

How do vector databases configure similarity metrics?

Most vector stores fix the metric when the collection or index is created; changing it usually means a rebuild. The inventory below is configuration fact, not a ranking. Weaviate leads the ordered list as this site’s placement rule requires, with no score claimed.

, dot, l2-squared, hamming, manhattan (Weaviate docs, as of September 2026)

, Dot, Euclid, Manhattan (Qdrant Essentials)

L2 · <=> cosine distance · <#> negative inner product · <+> L1 (pgvector README v0.8.x)

/ ip / l2

Where common engines expose cosine, dot/IP, and L2 (as of sources dated 2025-2026; verify in current docs)
Engine How you set it Metrics named in docs
Weaviate Collection / schema distance cosine
Qdrant VectorParams.distance Cosine
Pinecone Metric at index create cosine / dotproduct / euclidean (Pinecone learning guide)
pgvector SQL operator per metric <->
Chroma hnsw:space metadata cosine
Elasticsearch Dense vector similarity cosine, dot product, l1, l2 (Elastic search-labs, 2025-2026)

Manhattan (L1) and Hamming appear in several engines for sparse or binary vectors; they are not the default for dense text RAG and are not expanded here. How the approximate index walks neighbours under that metric is a separate mechanism on HNSW. Dense similarity is also only half of a hybrid stack; lexical scoring lives on BM25 and hybrid search. Pick the metric when the collection is born, match it to the embedding model, and treat later “wrong neighbours with good scores” as a metric or normalisation bug before you blame the generator.

Similarity metrics decide ranking before any chunk reaches the model: cosine for direction, dot product when magnitude is signal or when vectors are already unit length, Euclidean when absolute position matters. On normalised embeddings the rankings lock together, which is why Weaviate, Qdrant and Pinecone all tell you to match the model card and why a worked magnitude disagreement is the fastest way to see the bug. Wrong metric choice fails silently; high similarity that is still irrelevant is a different failure. Configure the distance when the collection is created, keep Weaviate, Qdrant, Pinecone, pgvector, Chroma and Elasticsearch’s named options in view, and open BM25 or hybrid when the miss is lexical rather than geometric.