What Is a Vector?
You do not need a GPU, a vector database, or a heavy AI framework to understand and build vector search. A small private vector store can be built using pure Python, basic mathematics, and simple data structures — giving you a strong foundation for understanding how semantic search actually works.
Every search engine faces the same fundamental challenge: given a user's query, find the most relevant content. Traditional keyword search matches words. Semantic search attempts to understand meaning.
A vector database is useful at scale, but the underlying mathematics is surprisingly simple. In this tutorial, you will build a working local vector store using nothing but Python's standard library.
What Is a Vector?
In data science, a vector is simply a list of numbers that represents something:
# A 5-dimensional vector
vector = [0.21, 0.74, -0.13, 0.56, 0.89]
Each number represents a dimension. In practice, embedding vectors often have hundreds or thousands of dimensions, but the concept is the same.
Do not assume that arbitrary numbers automatically have semantic meaning. Vectors become meaningful when they are produced by an embedding model trained to capture relationships between pieces of information.
What Is an Embedding?
An embedding is a numerical representation of text (or images, audio, or other data) that captures semantic relationships. An embedding model converts text into a vector:
# Conceptual
# "Python is a programming language"
# → [0.12, -0.34, 0.87, ..., 0.45]
# "Machine learning builds models from data"
# → [0.15, -0.28, 0.82, ..., 0.51]
# Semantically related texts produce vectors that
# are closer together in the embedding space.
The important distinction: embedding generation (turning text into vectors) usually requires a pretrained model. Vector storage and search (finding the most similar vectors) can be implemented with simple Python mathematics.
Do We Need Deep Learning?
The Mathematics of Vector Search
Three common ways to compare vectors:
| Method | Formula | Intuition |
|---|---|---|
| Cosine Similarity | (A·B) / (‖A‖×‖B‖) | Angle between vectors |
| Euclidean Distance | √Σ(Aᵢ−Bᵢ)² | Straight-line distance |
| Dot Product | Σ(Aᵢ × Bᵢ) | Magnitude-weighted similarity |
We focus on cosine similarity because it measures the direction of vectors rather than their magnitude — which makes it ideal for comparing text embeddings where semantic direction matters more than absolute values.
A concrete example:
A = [1, 0]
B = [0.9, 0.1]
C = [0, 1]
cosine(A, B) = 0.9939 ← very similar
cosine(A, C) = 0.0000 ← completely different
Build a Vector Store From Scratch
The complete implementation uses only Python's standard library — no external dependencies:
import json, math
from typing import List, Dict, Any, Optional
def cosine_similarity(vec_a: List[float], vec_b: List[float]) -> float:
"""Compute cosine similarity between two vectors."""
if len(vec_a) != len(vec_b):
raise ValueError(f"Dimension mismatch: {len(vec_a)} vs {len(vec_b)}")
dot = sum(a * b for a, b in zip(vec_a, vec_b))
mag_a = math.sqrt(sum(a * a for a in vec_a))
mag_b = math.sqrt(sum(b * b for b in vec_b))
if mag_a == 0 or mag_b == 0:
return 0.0
return dot / (mag_a * mag_b)
class LocalVectorStore:
"""A simple local vector store."""
def __init__(self, dimension: Optional[int] = None):
self._dimension = dimension
self._store: Dict[str, Dict[str, Any]] = {}
def add(self, doc_id, text, vector, metadata=None):
if self._dimension is None:
self._dimension = len(vector)
elif len(vector) != self._dimension:
raise ValueError(f"Expected {self._dimension}, got {len(vector)}")
self._store[doc_id] = {
"id": doc_id, "text": text,
"vector": vector, "metadata": metadata or {}
}
def search(self, query_vector, top_k=5):
if not self._store:
return []
results = []
for doc in self._store.values():
score = cosine_similarity(query_vector, doc["vector"])
results.append({"id": doc["id"], "text": doc["text"],
"score": round(score, 4), "metadata": doc["metadata"]})
results.sort(key=lambda x: x["score"], reverse=True)
return results[:top_k]
def delete(self, doc_id):
if doc_id in self._store:
del self._store[doc_id]
return True
return False
def count(self):
return len(self._store)
def save(self, filepath):
with open(filepath, 'w') as f:
json.dump({"dimension": self._dimension,
"documents": self._store}, f, indent=2)
@classmethod
def load(cls, filepath):
with open(filepath, 'r') as f:
data = json.load(f)
store = cls(dimension=data["dimension"])
store._store = data["documents"]
return store
That is the complete implementation. Let us walk through it step by step.
How It Works: Step by Step
The search pipeline follows a clear sequence:
- Add documents — each document gets an ID, text, vector, and optional metadata
- Store vectors — stored in a Python dictionary keyed by document ID
- Query — a query vector is compared against all stored vectors
- Score — cosine similarity produces a score between 0 and 1
- Rank — results are sorted by score descending
- Return top-k — the top-k most similar documents are returned
Try It: A Complete Example
# Create a store with 5-dimensional vectors
store = LocalVectorStore(dimension=5)
# Add documents with synthetic vectors
store.add("doc-001", "Python is a programming language.",
[0.8, 0.2, 0.1, 0.0, 0.3])
store.add("doc-002", "Machine learning builds models from data.",
[0.3, 0.9, 0.1, 0.0, 0.2])
store.add("doc-003", "Cybersecurity protects networks and systems.",
[0.1, 0.0, 0.9, 0.7, 0.1])
store.add("doc-004", "Databases store structured data.",
[0.2, 0.1, 0.0, 0.8, 0.6])
store.add("doc-005", "Statistics analyzes numerical data.",
[0.4, 0.5, 0.0, 0.3, 0.4])
# Search with a programming-related query
results = store.search([0.7, 0.3, 0.1, 0.0, 0.2], top_k=3)
for r in results:
print(f"{r['id']}: score={r['score']} — {r['text'][:40]}")
# Output:
# doc-001: score=0.9843 — Python is a programming language.
# doc-005: score=0.7909 — Statistics analyzes numerical data.
# doc-002: score=0.6851 — Machine learning builds models from d...
The Python-related document ranks highest because its vector is most similar to the query vector. The cosine similarity score tells you how similar — 0.9843 is very close to 1.0.
Adding Real Embeddings
The vectors in the example above were manually defined for clarity. In a real system, you connect the store to an embedding model:
# Conceptual: real embedding pipeline
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2") # 384-dim, CPU-friendly
text = "Python is a programming language."
vector = model.encode(text).tolist() # → list of 384 floats
store.add("doc-001", text, vector, metadata={"category": "programming"})
The key point: embedding generation is the computationally heavier step (it requires a model). Vector similarity search itself is comparatively simple — it is just math over lists of numbers.
For local CPU use, models like all-MiniLM-L6-v2 (384 dimensions) or all-mpnet-base-v2 (768 dimensions) are practical choices that run without a GPU.
Private Local Vector Search
When both embedding generation and vector search run locally, your data never leaves your machine:
# Local architecture
Local Documents
↓
Local Embedding Model (CPU)
↓
Local Vector Store (Python)
↓
Local Query
↓
Similarity Search
↓
Relevant Documents — never left your machine
Add Metadata and Persistence
Metadata lets you filter and organize documents. The store supports saving to and loading from JSON:
# Save to disk
store.save("my_vector_store.json")
# Load later
loaded = LocalVectorStore.load("my_vector_store.json")
results = loaded.search(query_vector, top_k=3)
JSON persistence is appropriate for teaching and small collections (hundreds to thousands of vectors). For millions of vectors, production databases use binary formats and specialized indexes.
Performance Characteristics
For N vectors of dimension D, brute-force cosine similarity search is approximately O(N × D).
| Dataset Size | Dimensions | Pure Python | Practical? |
|---|---|---|---|
| 100 documents | 384 | Milliseconds | Excellent |
| 1,000 documents | 384 | ~10ms | Excellent |
| 10,000 documents | 384 | ~100ms | Good |
| 1,000,000 documents | 384 | Seconds | Use a vector DB |
At scale, specialized indexes like HNSW (Hierarchical Navigable Small World) provide approximate nearest neighbor search that trades perfect accuracy for dramatic speed improvements.
Pure Python vs. NumPy vs. Vector Database
| Requirement | Pure Python | NumPy | Vector DB |
|---|---|---|---|
| Learning | Excellent | Good | More abstraction |
| Small dataset | Excellent | Excellent | Often unnecessary |
| Prototype | Excellent | Good | Useful |
| Millions of vectors | Poor fit | Better | Appropriate |
| Advanced indexing | Limited | Basic | Strong (HNSW, IVF) |
| Production | Depends | Depends | Usually preferable |
Building Toward RAG
The vector store is one component of a Retrieval-Augmented Generation (RAG) system. Here is how it fits:
Documents → Chunk → Embed → Vector Store
↓
User Query → Embed → Similarity Search → Top-K Results
↓
Retrieved Context + User Query
↓
LLM generates answer
The vector store retrieves relevant context. The LLM uses that context to generate an answer. The store itself does not generate anything — it finds what is most relevant.
Limitations
- Embedding quality matters — poor embeddings produce poor search results regardless of how good the vector store is
- Chunking affects retrieval — how you split documents into chunks directly impacts what the system can find
- Cosine similarity is not understanding — it measures vector similarity, not semantic meaning
- Brute-force search does not scale indefinitely — O(N × D) becomes slow at millions of vectors
- Vector similarity can produce false positives — mathematically similar vectors are not always semantically relevant
- Multilingual performance varies — embedding models have different strengths across languages
Suggested Project
Build a local searchable knowledge base for your own notes:
private-vector-store/
├── documents/ # Your .md or .txt files
├── vector_store.json # Persisted vectors
├── index.py # Build the store
├── search.py # Search the store
└── README.md
Suggested extensions: add metadata filtering, implement batch indexing, add a simple web interface, or connect to a local embedding model.
Key Takeaways
- Vector search is simple math — cosine similarity, dot product, and Euclidean distance are the foundations
- You can build a working vector store in ~50 lines of Python — no frameworks required
- Embedding generation and vector search are separate concerns — the search engine does not need deep learning
- Brute-force search works well for small to medium datasets — thousands of documents are perfectly manageable
- Pure Python is for learning — production systems should use specialized vector databases
- The vector store is one piece of RAG — retrieval, not generation
Related BestWordz Resources
- BestWordz Data Science Tools — statistical calculators, data analysis tools
- BestWordz Machine Learning Tools — ML metrics, evaluation tools
- BestWordz AI Tools — AI-powered utilities
- Running AI Locally on CPU — local inference without a GPU
- Model Context Protocol (MCP) Guide — connecting AI agents to your data
- BestWordz Community — discuss data science topics
Further Reading
💬 Discuss this topic
Have questions or insights about What Is a Vector?? Join the BestWordz Community.
Continue Learning: Data Science Pipeline
From data to insights
📚 Related Articles
The 10-Stage Data Science Roadmap
Data science in 2026 spans far beyond machine learning. A complete data scientist needs Python, sta…
CybersecurityWhy Build a Private RAG System?
Key Takeaway --> 🔑 KEY TAKEAWAY
AI & Machine LearningWhat Is an Embedding?
Embeddings transform text into numerical vectors that capture meaning. Semantically similar texts p…
CybersecurityCan AI Really Run Without a GPU?
You don't need a GPU or a cloud API to start working with modern AI. A consumer CPU, sufficient RAM…
CybersecurityThe 11-Stage AI Engineer Roadmap
AI engineering in 2026 is a distinct discipline requiring Python, machine learning, deep learning, …
CybersecurityKeyword Search vs Semantic Search
Semantic search finds documents by meaning, not just keywords. By building a search engine from scr…
🔧 Related Tools
CSR Generator
Generate Certificate Signing Requests with key pairs.
Try it now →Diffie-Hellman Visual
Visual walkthrough of Diffie-Hellman key exchange.
Try it now →Public vs Private Key Demo
Understand how public and private keys work together in asymmetric cryptography.
Try it now →ECDH Key Agreement
Derive a shared secret using Elliptic Curve Diffie-Hellman.
Try it now →💬 Discuss on BestWordz Community
Join the conversation about Python, Machine Learning, Deep Learning on the BestWordz Community forum.
Visit Forum →