AI & Engineering

How to Add AI Search to Any Website with Vector Embeddings

We’ve all experienced the frustration of searching for "running shoes" on an e-commerce website and getting zero results, only to find out later the store categorized them as "athletic sneakers."

This happens because traditional website search bars are "lexical." They act like a librarian who doesn't actually read books, but just uses `Ctrl+F` to look for exact spelling matches. Today, we are going to fix this. We are going to build a Semantic Search Engine—an AI that actually understands the meaning behind human language, using Vector Embeddings and Cloudflare's serverless ecosystem.

1. What is a Vector Embedding?

The Analogy: Imagine a massive grocery store. Apples and oranges are placed close to each other in the "Fruit" aisle. Beef and chicken are in the "Meat" aisle. If I ask you to find a pear, you intuitively know to walk toward the apples, not the beef.

The Expert Detail: Computers cannot read English. To make a computer understand "fruit," we have to translate human language into geometry. An Embedding Model (like OpenAI's text-embedding-3-small or Cloudflare's bge-base-en) reads a sentence and outputs an array of hundreds of floating-point numbers (e.g., [0.014, -0.052, 0.089...]).

Think of these numbers as physical coordinates on a 768-dimensional map. Words with similar contextual meanings are plotted physically close to each other on this map. The AI knows that the coordinates for "warm outerwear" are practically identical to the coordinates for "winter jacket."

Search Type How It Works The Result
Lexical Search (Legacy) Keyword matching (BM25, TF-IDF). Compares letters and characters. Fails on typos, synonyms, and complex phrasing.
Semantic Search (AI) Vector geometry. Plots intent and context on a multi-dimensional graph. Understands intent. "Vehicle for moving dirt" effortlessly returns "Wheelbarrow".

2. The "Hydration" Architecture

To build our search engine, we need two databases working together in harmony.

The Metadata Trap

Expert Detail: A beginner mistake is trying to store an entire 2,000-word blog post directly inside a Vector Database. Vector DBs are optimized for math, not text storage; bloating them with raw text slows them down and spikes your cloud bill.

The correct architecture is Data Hydration. Your Vector Database (like Cloudflare Vectorize) stores the floating-point coordinates and a simple product_id. When the Vector DB finds a match, it hands that ID to your standard SQL Database (like Cloudflare D1), which quickly "hydrates" the ID into the full product name, price, and description for the user.

3. Executing the Search (Cosine Similarity)

When a user types "warm outerwear" into our search bar, we execute three real-time operations inside a Cloudflare Worker.

Step A: Embed the User's Query

First, we convert the user's search string into coordinates using the exact same AI model we used to index our database.

// Using Cloudflare Workers AI to generate coordinates
const queryVector = await env.AI.run('@cf/baai/bge-base-en-v1.5', { 
  text: ["warm outerwear"] 
});

Step B: The Math Magic (Cosine Similarity)

Next, we pass those coordinates to our Vector Database. How does it find the closest match? It uses high school geometry: Cosine Similarity.

Imagine drawing a line from the center of our map to the user's query, and another line to a product in our database. The database measures the angle between those two lines. The smaller the angle, the more semantically related the two items are.

// Querying Cloudflare Vectorize for the closest semantic matches
const vectorMatches = await env.VECTOR_INDEX.query(queryVector.data[0], { 
  topK: 5, // Return the top 5 closest matches
});

// vectorMatches returns an array of IDs, e.g., [{ id: "prod_123", score: 0.98 }, ...]

Step C: Hydrate from SQL

Finally, we take those winning IDs and fetch the actual human-readable data from our SQL database.

// Extract the IDs from the vector results
const matchIds = vectorMatches.matches.map(match => match.id);

// Hydrate the data from our Cloudflare D1 SQL database
const stmt = `SELECT id, title, price FROM products WHERE id IN (${matchIds.map(() => '?').join(',')})`;
const finalProducts = await env.DB.prepare(stmt).bind(...matchIds).all();

return Response.json(finalProducts.results);

4. Bonus: The "Chunking" Strategy

If you are building a search engine for products, you can embed the product description easily. But what if you are building an AI search for massive, 50-page PDF documents?

Context Windows & Chunking

Expert Detail: Embedding models have "context limits" (often around 512 or 8192 tokens). If you feed it a whole book, it will crash. Before embedding, you must split your document into smaller Chunks (e.g., 300 words each) with slight overlaps. You then embed each chunk separately, tying them all back to the same document_id. When a user searches, the Vector DB finds the specific *paragraph* that contains the answer, making your search incredibly precise.

The Result

By combining an Embedding Model, a Vector Database, and Cosine Similarity, we've essentially taught a computer to read between the lines. Without writing a single complex synonym dictionary or natural language parser, we have built an enterprise-grade search engine that feels like magic to the end user.

RP

About Rohit Patil

Rohit Patil is a Toronto-based Senior Web Performance & Security Architect specializing in CDN engineering, Akamai, Cloudflare, WAF, and Edge Security.