← Back to Blog
PineconeVector SearchBangkok

Semantic Search for Bangkok Clinics and Restaurants with Pinecone

15 May 2026 · by Yunmin Shin

What Is Vector Search and When Do You Need It?

Traditional database search is keyword-based — it finds records where a text field contains the exact word you searched for. Vector search is semantic — it finds records that are conceptually similar to your query, even when they share no words in common.

This is a real gap on the sites we build and operate. A patient searching a clinic's site for "how do I get rid of forehead lines" won't match a treatment listed under "botulinum toxin injection — glabellar area" with keyword search, even though it's exactly the treatment they're looking for. A visitor on a restaurant discovery site searching "quiet place to work with wifi near a BTS station" won't match a listing described as "co-working-friendly café, Sukhumvit Soi 24" — again, same meaning, no shared words. Vector search closes that gap, because it matches on meaning rather than exact terms, and it's the enabling technology behind AI search, document Q&A (RAG), and chatbot knowledge retrieval generally.

What Is Pinecone, and Do You Actually Need It?

Pinecone is a managed vector database. You store embedding vectors — arrays of floating point numbers representing the semantic meaning of text — and query it for the most similar vectors to a query embedding. Pinecone handles indexing, storage, and retrieval at scale with low latency.

The honest answer for most of the small-to-medium business sites we build, though, is that you probably don't need a separate vector database at all. If you're already on Supabase (as most of our clinic and restaurant projects are), pgvector gives you vector search inside the same PostgreSQL database you're already running — no extra service, no extra bill, and one less thing to keep in sync. A single clinic's treatment catalog is a few hundred rows at most; a restaurant discovery site's listings might run into the low thousands. pgvector handles that comfortably.

Pinecone earns its place when you're operating at real scale or need retrieval latency that a general-purpose database can't match at that scale — think an aggregator indexing tens of thousands of restaurant listings with reviews and photos across a whole city, or a multi-clinic platform searching millions of treatment records and patient notes. For a single business's site, start with pgvector, and only reach for Pinecone if you outgrow it.

How Do You Generate Embeddings?

An embedding model converts text into a vector. OpenAI's text-embedding-3-small is the standard choice — cheap, fast, and good quality:

const response = await openai.embeddings.create({
  model: "text-embedding-3-small",
  input: "How long does swelling last after filler?",
});
const embedding = response.data[0].embedding; // 1536-dimensional float array

For a clinic, generate an embedding for every treatment's full description (not just its name — the embedding needs enough text to capture what the treatment actually does and treats), plus common patient questions if you're building a Q&A bot. Generate embeddings once when content is indexed, and again for each user query at search time.

How Do You Index and Query Pinecone?

If you do have the scale to justify Pinecone, install the client:

npm install @pinecone-database/pinecone

Index a treatment or listing:

const index = pinecone.index("your-index-name");
await index.upsert([{
  id: treatment.id,
  values: embedding,
  metadata: { name: treatment.name, priceRange: treatment.priceRange },
}]);

Query for similar results:

const results = await index.query({
  vector: queryEmbedding,
  topK: 5,
  includeMetadata: true,
});

The pgvector equivalent looks almost identical conceptually — store the embedding in a vector column, then query with a cosine-distance operator (<=>) ordered ascending and limited to the top matches. If you're already comfortable in SQL, that's often the faster path to shipping.

How Do You Keep the Index in Sync with Real Content?

Whichever store you use, the index is only as good as its freshness. A clinic that updates a treatment's price or adds a new aftercare note needs that change reflected in the embedding, not just the source row — a stale embedding still matches on old wording and can surface outdated pricing to a patient. The simplest reliable pattern is a database trigger or an application-level hook that re-embeds and re-upserts a record whenever its source content changes, rather than a manual "remember to re-run the indexing script" step that inevitably gets forgotten a few weeks after launch. For low-traffic content like a treatment catalog, this can even run synchronously in the same request that saves the edit — the volume is small enough that the extra API call to generate an embedding adds a negligible delay.

What Does This Mean for the Search UX Itself?

A meaning-based search invites longer, more natural queries than a keyword search box does — Thai and English speakers alike will type full questions like "which treatment for under-eye dark circles" rather than a two-word keyword. Design the search input to accept that: a placeholder that models a real question rather than "Search treatments...", and a results view that shows why a result matched (a short excerpt from the treatment description) rather than a bare list of names, since semantic matches are less immediately self-explanatory than exact keyword hits. On mobile, where most of this traffic actually happens, keep the input full-width and above the fold, and return results fast enough that it feels closer to autocomplete than a page reload — vector search over a few hundred to a few thousand rows on pgvector or Pinecone comfortably returns in well under a second, so the bottleneck is almost always the embedding API round trip for the query itself, not the vector lookup.

What Is RAG and How Does It Apply Here?

Retrieval-Augmented Generation (RAG) is the pattern behind a clinic FAQ chatbot or a restaurant recommendation assistant. Instead of asking a language model to answer from its general training data — where it might hallucinate a price or a treatment detail — you:

  1. Embed the user's question.
  2. Retrieve the most relevant treatments, FAQ entries, or listings from your vector store.
  3. Include that retrieved content in the prompt sent to the language model.
  4. Ask the model to answer using only the provided context.

For a clinic, this means a patient asking about downtime after a treatment gets an answer sourced from that clinic's actual treatment content, not a generic answer that might not match what that specific clinic offers or how they phrase their aftercare guidance. For a restaurant discovery site, it means a "recommend somewhere quiet with good coffee near Ekkamai" query pulls from real listings with real descriptions rather than the model inventing a plausible-sounding but nonexistent café. This is the correct architecture for any AI assistant that needs to answer from your specific business data rather than general knowledge.

Ready to Build Something Fast?

Get a free quote. We reply within 24 hours.

Ready to build something fast and scalable?

Get a free project quote. We reply within 24 hours.

Get a Free Quote →