.NETSemantic SearchEmbeddingsC#

Building Semantic Search in C# with Embeddings

Keyword search misses meaning. Build semantic search in C# using embeddings and vector similarity, so queries match by intent — not just matching words.

Traditional search matches words. Ask a keyword search for “how to cancel my plan” and it’ll miss a document titled “ending your subscription” — same meaning, different words. Semantic search fixes that by matching on meaning using embeddings. This guide builds a minimal semantic search in C#.

How it works

Two phases:

  1. Index — turn every document into an embedding vector and store it.
  2. Search — turn the query into a vector, then find the stored vectors closest to it.

“Closest” means most similar in meaning. That’s the entire idea.

Step 1: Index your documents

Generate an embedding per document (or per chunk) and keep it with the original text:

using Microsoft.Extensions.AI;

record Doc(string Text, ReadOnlyMemory<float> Vector);

IEmbeddingGenerator<string, Embedding<float>> embedder = /* your embeddings client */;

async Task<List<Doc>> IndexAsync(IEnumerable<string> texts)
{
    var docs = new List<Doc>();
    foreach (var text in texts)
    {
        var e = await embedder.GenerateAsync(text);
        docs.Add(new Doc(text, e.Vector));
    }
    return docs;
}

Step 2: Search by similarity

Embed the query, then rank documents by cosine similarity and return the top matches:

async Task<List<string>> SearchAsync(string query, List<Doc> docs, int topK = 3)
{
    var q = (await embedder.GenerateAsync(query)).Vector;
    return docs
        .Select(d => (d.Text, Score: Cosine(q.Span, d.Vector.Span)))
        .OrderByDescending(x => x.Score)
        .Take(topK)
        .Select(x => x.Text)
        .ToList();
}

Now searching “how do I cancel” returns “ending your subscription” — because they mean the same thing, even with zero shared keywords.

Step 3: Scale it with a vector database

The in-memory approach above is perfect for hundreds of documents. For thousands or millions, computing similarity against every vector on each query is too slow. That’s what vector databases are for — they index vectors for fast approximate nearest-neighbour search. See vector search with pgvector for a Postgres-based option that most .NET teams can adopt without new infrastructure.

Making results better

Semantic search quality comes down to a few knobs:

  • Chunking — split long documents so a match points to the relevant passage, not a whole file.
  • Hybrid search — combine keyword and vector search; keywords catch exact terms (product codes, names) that embeddings can blur.
  • Re-ranking — for hard queries, re-score the top candidates with a more precise model.

Most “semantic search isn’t accurate” problems are chunking or retrieval issues, not the embedding model — the same lesson as RAG.

Note: IEmbeddingGenerator APIs are still settling; verify against the current .NET AI docs. The two-phase design — index vectors, search by nearest vector — is fundamental.

Takeaway

Semantic search matches meaning, not words: embed your documents, embed the query, and return the nearest vectors. It’s a couple dozen lines of C# for a small corpus, and a vector database when you scale. Add chunking, hybrid search, and re-ranking to sharpen results — and you have the retrieval engine that powers everything from site search to RAG-grounded agents.


Have a correction or a topic you want covered? Email mani.bc72@gmail.com.