Skip to main content
Xenors Xenors
Home › Data Science › Vector Databases: ChromaDB, Pinecone and FAISS

Module 11: MLOps & Model DeploymentBy Ashok Kumar Yadav4 min read

Vector Databases: ChromaDB, Pinecone and FAISS

Keyword search misses meaning. Vector databases store embeddings, numeric fingerprints of text or images, and find the closest ones to a query in milliseconds.

Vector database storing text embeddings and returning nearest neighbours for a query
Quick answer: A vector database stores embedding vectors with metadata and finds nearest neighbours using cosine, dot product or Euclidean distance, usually through approximate indexes such as HNSW. FAISS is a library, ChromaDB is an embedded database, and Pinecone is a managed service.

How vector search works

An embedding model maps text to a vector so that similar meanings sit close together. At query time the question is embedded and compared against stored vectors. Exact search compares every vector. Approximate nearest neighbour (ANN) indexes such as HNSW or IVF trade a little accuracy for large speed gains.

ToolTypeBest for
FAISSLibraryFast local search, research
ChromaDBEmbedded databasePrototypes and small apps
PineconeManaged cloud serviceProduction without operating infrastructure

Semantic search with FAISS

Normalise vectors and use inner product to get cosine similarity.

Exact cosine search with FAISS
# pip install faiss-cpu sentence-transformers
import faiss, numpy as np
from sentence_transformers import SentenceTransformer

docs = ["How to reset my password", "Refund policy for annual plans",
        "Train a random forest in Python", "Fix a CUDA out of memory error"]
model = SentenceTransformer("all-MiniLM-L6-v2")
vecs = model.encode(docs, normalize_embeddings=True).astype("float32")

index = faiss.IndexFlatIP(vecs.shape[1])
index.add(vecs)
q = model.encode(["GPU memory error when training"], normalize_embeddings=True).astype("float32")
scores, ids = index.search(q, 2)
for s, i in zip(scores[0], ids[0]):
    print(round(float(s), 3), docs[i])

Store and filter with ChromaDB

Metadata filters combine semantic matching with structured conditions.

Add vectors and query with a metadata filter
import chromadb

client = chromadb.PersistentClient(path="chroma_data")
col = client.get_or_create_collection("faq", metadata={"hnsw:space": "cosine"})
col.add(ids=["1", "2", "3"],
        documents=docs[:3],
        embeddings=vecs[:3].tolist(),
        metadatas=[{"topic": "account"}, {"topic": "billing"}, {"topic": "ml"}])
res = col.query(query_embeddings=q.tolist(), n_results=2, where={"topic": "ml"})
print(res["documents"])

Use Pinecone as a managed service

The index must be created with the same dimension as your embedding model. Check the Pinecone documentation for current index options and regions.

Upsert and query a Pinecone index
# pip install pinecone
import os
from pinecone import Pinecone, ServerlessSpec

pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
if "faq" not in pc.list_indexes().names():
    pc.create_index("faq", dimension=384, metric="cosine",
                    spec=ServerlessSpec(cloud="aws", region="us-east-1"))
idx = pc.Index("faq")
idx.upsert([(str(i), vecs[i].tolist(), {"text": docs[i]}) for i in range(len(docs))])
print(idx.query(vector=q[0].tolist(), top_k=2, include_metadata=True))

Choosing and tuning

Start with ChromaDB or FAISS locally. Move to a managed or server-based database when you need replication, access control or very large collections. Use the same embedding model for indexing and querying, and re-embed everything if you change it.

Hands-on practice

Run the examples in the downloadable Jupyter notebook. The extra checks below focus on mistakes that appear in real projects, not just syntax.

Compare cosine similarity

Practice example
import numpy as np
a=np.array([1.,0.,1.]); b=np.array([.8,.1,.9])
cos=a@b/(np.linalg.norm(a)*np.linalg.norm(b))
print(round(cos,3))
~0.994

High cosine similarity means similar direction, not necessarily identical semantics.

Apply metadata filtering

Practice example
records=[('doc1','policy'),('doc2','manual'),('doc3','policy')]
print([r for r in records if r[1]=='policy'])
[('doc1', 'policy'), ('doc3', 'policy')]

Metadata filters can improve relevance before vector ranking.

Self-check

  • Can you explain why the first practice result is trustworthy rather than just plausible?
  • What would you monitor or test before using this Vector Databases workflow in production?
  • Which assumption in the example would break first on a larger or messier dataset?

Frequently asked questions

What is an embedding?

A list of numbers produced by a model that represents the meaning of text, an image or other data.

Cosine similarity or Euclidean distance?

For normalised text embeddings they rank results identically. Use the metric your embedding model recommends.

Do I need a vector database for RAG?

Small projects can use FAISS or an in-memory index. A vector database helps with persistence, filtering and scale.

Primary references

Use these official sources when exact syntax, defaults or definitions matter beyond the examples here.

Written by Ashok Kumar Yadav
Ashok Kumar Yadav writes Xenors tutorials on applied AI, programming and data workflows. Every lesson is built around code you can run and check yourself.
GitHub · LinkedIn

Continue exploring