Component Reference

Semantic Search with pgvector and Ollama

A reusable PostgreSQL pattern for AI applications that need embeddings, relational metadata, indexes, and semantic nearest-neighbor queries in one database.

This solves semantic search when PostgreSQL is already your system of record. Store text and embeddings together, then use pgvector operators and indexes for nearest-neighbor lookup.

Code

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE IF NOT EXISTS text_embeddings (
    id SERIAL PRIMARY KEY,
    source TEXT,
    text TEXT NOT NULL,
    embedding vector(1024)
);
import os

import ollama
import psycopg2
from dotenv import load_dotenv

load_dotenv()


def get_connection():
    return psycopg2.connect(
        dbname=os.getenv("DB_NAME"),
        user=os.getenv("DB_USER"),
        password=os.getenv("DB_PASSWORD"),
        host=os.getenv("DB_HOST", "localhost"),
        port=os.getenv("DB_PORT", "5432"),
    )


def get_embedding(text, model="mxbai-embed-large"):
    response = ollama.embeddings(model=model, prompt=text)
    return response["embedding"]


def store_texts(rows):
    conn = get_connection()
    cursor = conn.cursor()
    for source, text in rows:
        embedding = get_embedding(text)
        cursor.execute(
            "INSERT INTO text_embeddings (source, text, embedding) VALUES (%s, %s, %s)",
            (source, text, embedding),
        )
    conn.commit()
    conn.close()


def find_similar(query_text, top_k=5):
    embedding = get_embedding(query_text)
    conn = get_connection()
    cursor = conn.cursor()
    cursor.execute(
        """
        SELECT source, text, 1 - (embedding <=> %s) AS similarity
        FROM text_embeddings
        ORDER BY embedding <=> %s
        LIMIT %s
        """,
        (embedding, embedding, top_k),
    )
    results = cursor.fetchall()
    conn.close()
    return results

Usage

store_texts([
    ("doc-1", "PostgreSQL can store embeddings with pgvector."),
    ("doc-2", "FAISS is a local vector search library."),
])

for source, text, similarity in find_similar("vector search in postgres"):
    print(f"{similarity:.3f}", source, text)

Indexes

Use HNSW when you want strong approximate nearest-neighbor search:

CREATE INDEX ON text_embeddings
USING hnsw (embedding vector_l2_ops);

SET hnsw.ef_search = 50;

Use IVF when you want a tunable lower-memory index:

CREATE INDEX ON text_embeddings
USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);

Requirements

Install PostgreSQL, pgvector, psycopg2, python-dotenv, and ollama. The vector dimension must match the embedding model output.

Source

The article points to example code in ernanhughes/pgvector-examples.

Full explanation

For installation notes, Windows setup details, and distance/index tradeoffs, read: PostgreSQL for AI: Storing and Searching Embeddings with pgvector.