HyperSaaS
BackendDatabase

pgvector & Vector Search

How HyperSaaS uses pgvector for document embeddings and similarity search.

Why pgvector?

Instead of running a separate vector database (Pinecone, Weaviate, Qdrant), HyperSaaS keeps embeddings inside PostgreSQL using pgvector. This means:

  • No extra infrastructure to manage
  • Transactional consistency with your relational data
  • Standard SQL queries with vector operations
  • Production-ready up to ~10M vectors with HNSW indexing

Setup

pgvector must be installed as a PostgreSQL extension. The Docker image pgvector/pgvector:pg17 includes it pre-installed. For manual setups:

CREATE EXTENSION IF NOT EXISTS vector;

The Django migration handles this automatically via VectorExtension().

DocumentChunk Model

Each document chunk stores its text content alongside a 1536-dimensional embedding vector:

class DocumentChunk(BaseModel):
    document = models.ForeignKey(Document, on_delete=models.CASCADE)
    chunk_index = models.PositiveIntegerField()
    content = models.TextField()                          # Raw text
    embedding = VectorField(dimensions=1536)              # pgvector
    embedding_model = models.CharField(max_length=100)
    page_number = models.PositiveIntegerField(null=True)
    section_heading = models.CharField(max_length=500, blank=True)
    token_count = models.PositiveIntegerField(default=0)
    chunk_metadata = models.JSONField(default=dict)       # Docling metadata

HNSW Index

An HNSW (Hierarchical Navigable Small World) index enables fast approximate nearest-neighbor search:

HnswIndex(
    name="doc_chunk_embedding_hnsw_idx",
    fields=["embedding"],
    m=16,                              # Connections per node
    ef_construction=64,                # Build-time search width
    opclasses=["vector_cosine_ops"],   # Cosine distance
)

Index parameters

ParameterValueEffect
m16Higher = better recall, more memory
ef_construction64Higher = better index quality, slower build
opclassesvector_cosine_opsCosine similarity distance metric

Querying

Semantic search uses pgvector's CosineDistance annotation:

from pgvector.django import CosineDistance

DocumentChunk.objects.filter(
    document_id__in=document_ids
).annotate(
    distance=CosineDistance("embedding", query_embedding)
).order_by("distance")[:top_k]

The score is converted to similarity: score = 1.0 - distance.

Embedding Model

HyperSaaS uses OpenAI's text-embedding-3-small (1536 dimensions) by default. This is configurable:

SettingDefaultDescription
DOCUMENT_EMBEDDING_MODELtext-embedding-3-smallOpenAI model name
DOCUMENT_EMBEDDING_DIMENSIONS1536Must match VectorField dimensions
DOCUMENT_EMBEDDING_BATCH_SIZE512Texts per API call

On this page