← Back to AI Home Day 2 of 28

Vector Libraries (FAISS) vs. Persistent Vector DBs (Chroma DB, Milvus)

Time Target: 20–30 Mins | Architectural Boundaries, CRUD Pipeline & In-Memory vs Persistent Engines

1. Core Concept: Library vs. Managed Database

A vector library (e.g. FAISS) is an algorithm implementation optimized for in-memory vector math on a single machine. A vector database (e.g. Chroma DB, Milvus) wraps indexing algorithms inside a complete data management system featuring CRUD operations, metadata filtering, and disk persistence.

Capability FAISS (Library) Chroma DB (Embedded / Client-Server) Milvus (Distributed DB)
Type In-memory algorithmic library Dedicated AI-first vector database Enterprise distributed vector database
Deployment Target Single node (CPU/GPU) without network daemon Local embedded instance or client-server Horizontally scalable cluster (cloud-native)
Data Lifecycle (CRUD) Append / Search; updates require index rebuild Full CRUD (add, get, update, delete) Full CRUD with streaming WAL and segment merging
Metadata Filtering No native support (requires custom application logic) Native metadata (where) & text (where_document) Native boolean scalar query engine on metadata
Supported Indices Extensive (Flat, IVF, LSH, HNSW, PQ) HNSW (Hierarchical Navigable Small World) Extends FAISS indices + HNSW, Annoy, DISKANN

2. Chroma DB Collection Lifecycle & CRUD Pipeline

Chroma organizes documents inside named Collections. When a collection is created, you configure the underlying distance metric (space) and embedding model:

import chromadb
from chromadb.utils import embedding_functions

# 1. Initialize client & embedding function
client = chromadb.Client()  # In-memory ephemeral client
ef = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")

# 2. Create collection with explicit HNSW metric configuration
collection = client.create_collection(
    name="kb_collection",
    metadata={"domain": "production_docs"},
    configuration={
        "embedding_function": ef,
        "hnsw": {"space": "cosine"}  # 'l2', 'ip', or 'cosine'
    }
)

Immutable Invariant: You cannot modify the embedding model or distance metric of an active collection using collection.modify(). Doing so requires creating a new collection and migrating/re-embedding the dataset.

3. Daily Targeted Drills (15–20 Mins)

Drill 2.1: The Structural Difference

Why is FAISS unable to execute a query like "Find top 5 documents WHERE category = 'legal'" directly out of the box?

Answer: FAISS indexes only numerical arrays and integer offsets. It does not store metadata payloads or execute scalar predicate logic. To filter, an external application layer must pre-filter IDs or post-filter FAISS neighbor results.

Drill 2.2: Updating Records in Chroma DB

When you call collection.update(ids=["doc1"], documents=["New updated text content"]), what does Chroma DB do with the existing embedding vector?

Answer: Chroma DB automatically re-invokes the collection's registered embedding_function in the background, calculates a new vector for the updated text, updates the storage segment, and rewires the HNSW graph edges.

Drill 2.3: Technology Selection Scenario

An enterprise needs to search across 80 million vectors with real-time updates and metadata filtering on multi-tenant tenant IDs. Why is Milvus preferred over raw FAISS or embedded Chroma DB?

Answer: Milvus provides distributed clustering, sharding, and high-availability WAL support to scale across nodes for 80M vectors, while retaining FAISS-speed indexing with built-in scalar metadata filtering. Embedded Chroma DB is single-node, and raw FAISS lacks distributed scaling and metadata indexing.

Drill 2.4: Persistent vs. Ephemeral Clients

What is the functional difference between chromadb.Client() and chromadb.PersistentClient(path="./data")?

Answer: chromadb.Client() runs purely in-memory; all collections and indices vanish when the Python process exits. PersistentClient persists collection schemas, vectors, and Parquet/SQLite storage segments to the local filesystem path.

Drill 2.5: CRUD Deletion Behavior

What happens if you run collection.delete(where={"status": "archived"}) in Chroma DB?

Answer: Chroma DB executes a metadata query against internal inverted lists to identify all matching vector IDs, marks those records as deleted (tombstoning), and excludes them from future HNSW approximate nearest neighbor traversals.

4. Practical Hands-On Exercise

Save and run this script to test the complete CRUD lifecycle and metadata search in Chroma DB:

import chromadb

# 1. Initialize local persistent client
client = chromadb.Client()
collection = client.create_collection(
    name="service_catalog",
    configuration={"hnsw": {"space": "cosine"}}
)

# 2. CREATE: Add documents with metadata
collection.add(
    ids=["srv_1", "srv_2", "srv_3"],
    documents=[
        "PostgreSQL managed database cluster with multi-region replicas",
        "Redis in-memory caching cluster for low-latency session store",
        "Chroma vector database cluster for document semantic retrieval"
    ],
    metadatas=[
        {"tier": "standard", "category": "rdbms"},
        {"tier": "cache", "category": "kv"},
        {"tier": "ai", "category": "vector"}
    ]
)

# 3. READ: Query with metadata filtering
res = collection.query(
    query_texts=["fast key-value memory storage"],
    n_results=1,
    where={"tier": "cache"}
)
print("Query Result ID:", res["ids"][0][0])
print("Document:", res["documents"][0][0])

# 4. UPDATE: Modify text and re-embed in the background
collection.update(
    ids=["srv_2"],
    documents=["Redis cache cluster with high availability Sentinel"],
    metadatas=[{"tier": "cache", "category": "kv_distributed"}]
)

# 5. DELETE: Remove single item
collection.delete(ids=["srv_1"])
print("Remaining items count:", collection.count())