This page goes beyond the feature list above: it explains how FAISS indexes are organized, walks through building and querying an index in Python, compares FAISS with vector databases, and lists the mistakes that most often cause poor recall or memory blow-ups.
FAISS is an open-source library developed by Facebook AI for efficient similarity search and clustering of dense vectors. It is highly optimized for handling very large dataset of high-dimensional vectors.
FAISS can be used in scenarios like image search, recommendation systems, and NLP embedding retrieval.
#-------------------------------------------------------------#
# pip install faiss-cpu # or 'faiss-gpu' for GPU version
#-------------------------------------------------------------#
import faiss
import numpy as np
# Create an index for L2 distance (Euclidean)
d = 128 # dimension of vectors
index = faiss.IndexFlatL2(d) # create the index
# Generate random data (vectors)
vectors = np.random.random((1000, d)).astype('float32')
# Add vectors to the index
index.add(vectors)
# Query for the nearest neighbors
query_vector = np.random.random((1, d)).astype('float32')
distances, indices = index.search(query_vector, k=5) # retrieve top-5 nearest neighbors
print("Nearest neighbors:", indices)
FAISS is a C++ library with Python bindings for nearest-neighbour search over dense float vectors. It is not a database: there is no server, no network protocol, no metadata filtering language, and no durability beyond writing an index file to disk. You give it a matrix of vectors, it builds an in-memory index, and it answers "which k stored vectors are closest to this query?" using L2 distance or inner product. That narrow scope is why it is fast and why it appears inside many other systems, including several vector databases and RAG frameworks.
The flat indexes compare the query with every stored vector. They give exact results and need no training, and they are the right choice up to roughly a few hundred thousand vectors or as a ground-truth baseline. IVF indexes cluster vectors with k-means into nlist cells and, at query time, scan only the nprobe closest cells. HNSW builds a layered proximity graph and greedily walks it toward the query. Product quantization compresses each vector into a few bytes by splitting it into sub-vectors and storing the index of the nearest centroid for each, cutting memory by an order of magnitude or more at some cost in accuracy.
Index types are often described with factory strings such as "IVF4096,PQ64" or "HNSW32,Flat", which faiss.index_factory turns into a composed index.
An IVF index must be trained on a representative sample before vectors are added, because the cell centroids come from k-means. Each added vector is appended to the inverted list of its nearest centroid. A query first finds its nprobe nearest centroids, then computes exact or quantized distances only within those lists. Raising nprobe increases recall and latency together; it is the main tuning knob after the index is built.
import numpy as np
import faiss
d, n = 384, 100_000
rng = np.random.default_rng(0)
xb = rng.standard_normal((n, d)).astype("float32")
xq = rng.standard_normal((5, d)).astype("float32")
# cosine similarity = inner product on L2-normalised vectors
faiss.normalize_L2(xb); faiss.normalize_L2(xq)
exact = faiss.IndexFlatIP(d)
exact.add(xb)
quantizer = faiss.IndexFlatIP(d)
ivf = faiss.IndexIVFFlat(quantizer, d, 1024, faiss.METRIC_INNER_PRODUCT)
ivf.train(xb[:50_000])
ivf.add(xb)
ivf.nprobe = 16
D_ex, I_ex = exact.search(xq, 10)
D_iv, I_iv = ivf.search(xq, 10)
recall = np.mean([len(set(a) & set(b)) / 10 for a, b in zip(I_ex, I_iv)])
print("recall@10 vs exact:", recall)
faiss.write_index(ivf, "docs.ivf") # persist; reload with faiss.read_index
To keep your own ids, wrap the index in faiss.IndexIDMap and call add_with_ids; store document text and metadata elsewhere, keyed by the same id.
| Need | FAISS | Vector DB (Chroma, Weaviate, pgvector) |
|---|---|---|
| Raw speed, large static corpora | Excellent, GPU support | Good |
| Metadata filters | Not built in | Built in |
| Concurrent updates, deletes | Limited, manual | Supported |
| Persistence, replication | Index files only | Managed by the server |
| Operational overhead | None (library) | A service to run |
A common pattern is to use FAISS as the search engine inside a larger service: documents and metadata live in a relational table or key-value store, FAISS holds only vectors and integer ids, and the application joins the two after search. Filtering is then done either by over-fetching (ask for 100 neighbours, filter to 10) or by maintaining separate indexes per tenant or category. When updates are frequent or filters are complex, that glue code grows quickly, and a vector database becomes the simpler choice.
float32; float64 arrays raise errors or are silently copied.