
Adding Semantic Search to a Blog with Supabase and OpenAI Embeddings
Not every AI feature needs to be a full retrieval-augmented generation pipeline. Sometimes the actual need is simpler: let a reader find the right post even when their search term does not appear in it verbatim. That is semantic search, and it is a much smaller project than RAG.
TL;DR / Written for skimmers
A reader searching for color variables will not find a post titled CSS Custom Properties if the search is a literal text match. The words do not overlap, even though the post is exactly what they are looking for. For a blog, embedding the title plus excerpt, or the full content_text field, into a single vector per post is enough to power search. There is no need to chunk a short blog post the way a long document for RAG would need to be split. With the embedding stored in a pgvector column, a similarity query is a single SQL statement ordering by distance to the query vector and limiting the result set, no separate search infrastructure required. Semantic search ends at returning a ranked list of posts for a human to read. The moment those retrieved posts get fed into a language model to generate an answer, the feature has become retrieval-augmented generation, with its own chunking and prompt-assembly concerns.
Why keyword search misses what readers actually mean
A reader searching for color variables will not find a post titled CSS Custom Properties if the search is a literal text match. The words do not overlap, even though the post is exactly what they are looking for.
Embeddings represent meaning as a vector, so two phrases that mean similar things end up close together in that vector space, regardless of which exact words were used.
Generating one embedding per post is usually enough
For a blog, embedding the title plus excerpt, or the full content_text field, into a single vector per post is enough to power search. There is no need to chunk a short blog post the way a long document for RAG would need to be split.
That embedding is generated once when a post is published and stored alongside the row, so search at query time only needs to embed the much shorter user query, not the whole archive again.
Querying pgvector for the closest matches
With the embedding stored in a pgvector column, a similarity query is a single SQL statement ordering by distance to the query vector and limiting the result set, no separate search infrastructure required.
Supabase makes this approachable specifically because the vector column lives next to the rest of the post data, so the same query can also filter by category or published status in one round trip.
Where this stops being search and starts being RAG
Semantic search ends at returning a ranked list of posts for a human to read. The moment those retrieved posts get fed into a language model to generate an answer, the feature has become retrieval-augmented generation, with its own chunking and prompt-assembly concerns.
Building the simpler search feature first is still useful groundwork, since the embedding and storage decisions made here carry over directly if the project later grows into a full RAG pipeline.