When teams add semantic search or RAG, the reflex is to bolt on a dedicated vector database. Often you don’t need to. If you already run PostgreSQL, the pgvector extension gives you vector storage and similarity search in the database you know — no new infrastructure, no separate service to operate. Here’s how to use it from .NET.
Why pgvector
Dedicated vector databases are excellent at massive scale. But for a lot of applications, pgvector on Postgres is the pragmatic choice:
- One database — your relational data and your vectors live together; you can filter by metadata and similarity in a single SQL query.
- Nothing new to operate — you already back up, monitor, and secure Postgres.
- It scales further than people expect — comfortably into millions of vectors with the right index.
Step 1: Enable the extension and create a table
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id bigserial PRIMARY KEY,
content text NOT NULL,
embedding vector(1536) -- match your embedding model's dimensions
);
The vector(1536) column stores the embedding; set the size to your model’s output dimensions.
Step 2: Insert embeddings from C#
Generate an embedding and store it alongside the text. With Npgsql (which has pgvector support), you write the vector directly:
// Pseudocode-level: generate the vector, then insert it with the text.
var vector = (await embedder.GenerateAsync(content)).Vector.ToArray();
await using var cmd = new NpgsqlCommand(
"INSERT INTO documents (content, embedding) VALUES ($1, $2)", conn);
cmd.Parameters.AddWithValue(content);
cmd.Parameters.AddWithValue(new Vector(vector)); // Npgsql pgvector type
await cmd.ExecuteNonQueryAsync();
Step 3: Search by similarity in SQL
pgvector adds distance operators. The <=> operator is cosine distance — order by it and take the nearest rows:
SELECT content
FROM documents
ORDER BY embedding <=> $1 -- $1 = your query embedding
LIMIT 5;
From C#, you embed the query, pass the vector as $1, and read back the top matches. The power move: combine it with a normal WHERE — “the 5 most similar documents for this tenant, in this category” — something a bolt-on vector store makes awkward but Postgres does in one query.
Step 4: Add an index for speed
Without an index, Postgres scans every row. For real datasets, add an approximate-nearest-neighbour index (HNSW is a good default):
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
This trades a tiny bit of recall for a large speed-up — the standard bargain for vector search at scale.
Note: Npgsql’s pgvector type names and index options evolve; verify against the pgvector and Npgsql docs. The approach — a
vectorcolumn,<=>for similarity, an HNSW index — is stable.
Takeaway
pgvector gives .NET teams vector search without adopting a new database: enable the extension, add a vector column, insert embeddings via Npgsql, and query with the <=> distance operator plus an HNSW index. You keep your relational data and vectors together — filtering by metadata and similarity in one SQL query — on infrastructure you already run. For most apps, that’s the right amount of vector database.
Next: how pgvector compares to Qdrant, Azure AI Search and SQL Server 2025 if you are still choosing a store.
Have a correction or a topic you want covered? Email mani.bc72@gmail.com.