Before adding a vector database to a retrieval system, it helps to see exactly how a query vector is scored against document vectors. A three-document example makes the ranking step inspectable.
What you will build
Understand the ranking step of semantic search by calculating cosine similarity over a small, deterministic vector collection.
Before you start
Python 3.10 or later. The example uses synthetic vectors and does not require a model, downloads or a vector database.
1. Create cosine_search.py
from math import sqrt
def cosine(a, b):
if len(a) != len(b):
raise ValueError("Vector dimensions differ")
left = sqrt(sum(x * x for x in a))
right = sqrt(sum(y * y for y in b))
if left == 0 or right == 0:
raise ValueError("Zero vectors have no cosine direction")
return sum(x * y for x, y in zip(a, b)) / (left * right)
documents = {
"java-http": [1.0, 0.2, 0.0],
"python-json": [0.8, 0.4, 0.1],
"garden-guide": [0.0, 0.1, 1.0],
}
query = [1.0, 0.0, 0.0]
ranked = sorted(
((name, cosine(query, vector)) for name, vector in documents.items()),
key=lambda item: item[1], reverse=True
)
assert ranked[0][0] == "java-http"
for name, score in ranked:
print(f"{name}: {score:.3f}")
2. Run the example
python cosine_search.pyExpected result
The order is java-http, python-json, garden-guide. The first score rounds to 0.981 and the final score is 0.000.
How it works
The dot product measures alignment, and the two vector lengths normalize the score. In this fixture the query points entirely along the first dimension, which makes the ranking easy to inspect by hand.
These hand-written vectors are for learning the calculation; they do not encode real word meanings. To build actual semantic search, embed both queries and document chunks using the same model and compatible dimensions, then replace the fixture vectors.
The ranking score is a similarity measure, not a probability that an answer is correct. A retrieval application should retain document IDs and source locations, inspect the retrieved text, and evaluate examples that ought to return no useful result.
Keep the embedding model and chunking settings alongside stored vectors. Changing either can invalidate your comparison. At larger sizes, measure brute-force performance before deciding whether an approximate index is needed.
Test the edge cases
- Try a zero vector and confirm it is rejected.
- Use mismatched dimensions and confirm validation fails.
- Scale a nonzero vector by two: its cosine similarity should be unchanged.
Keep learning
Browse related tutorials. Reference: official documentation.
Reviewed 7 November 2026. AI-dependent examples require your own local model; generated text varies.