Vector Databases for Products
When a customer searches for "something to keep my coffee hot during my commute", traditional keyword search fails. There's no exact match for that query in your product titles.
But if you've embedded your products into vectors, you can find semantically similar items like insulated travel mugs, thermal flasks, and heated car cup holders.
What are Vector Embeddings?
Vector embeddings are numerical representations of data (text, images, etc.) where similar items are close together in vector space.
from openai import OpenAI
client = OpenAI()
# Convert product description to a vector
product = "Yeti Rambler 20oz Tumbler - Stainless steel, vacuum insulated, keeps drinks cold for 24 hours or hot for 12 hours"
response = client.embeddings.create(
model="text-embedding-3-small",
input=product
)
# This is a 1536-dimensional vector!
embedding = response.data[0].embedding
print(f"Vector dimensions: {len(embedding)}") # 1536
print(f"First few values: {embedding[:5]}") # [-0.023, 0.041, -0.008, ...]
More dimensions = more nuance. A 1536-dimensional vector can capture subtle differences between "lightweight running shoes" and "breathable jogging sneakers" that would be impossible in just 2 or 3 dimensions.
How Vector Search Works
Popular Vector Databases
Pinecone
- Fully managed cloud service
- Scales automatically
- Simple API
- Best for: Production apps
Weaviate
- Open source (self-hosted or cloud)
- Built-in hybrid search
- GraphQL API
- Best for: Flexibility & control
Chroma
- Open source, embeddable
- Runs in-memory or persistent
- Great Python integration
- Best for: Prototyping & dev
Setting Up Pinecone for Products
from pinecone import Pinecone, ServerlessSpec
from openai import OpenAI
# Initialize clients
pc = Pinecone(api_key="your-pinecone-key")
openai_client = OpenAI()
# Create an index for products
pc.create_index(
name="ecommerce-products",
dimension=1536, # OpenAI embedding size
metric="cosine",
spec=ServerlessSpec(
cloud="aws",
region="us-east-1"
)
)
index = pc.Index("ecommerce-products")
# Sample products to index
products = [
{
"id": "prod_001",
"name": "Yeti Rambler 20oz",
"description": "Stainless steel vacuum insulated tumbler, keeps drinks cold 24hrs or hot 12hrs",
"category": "drinkware",
"price": 35.00
},
{
"id": "prod_002",
"name": "Hydro Flask 32oz",
"description": "Wide mouth water bottle, double-wall vacuum insulation, BPA-free",
"category": "drinkware",
"price": 44.95
},
{
"id": "prod_003",
"name": "Stanley Quencher 40oz",
"description": "Insulated tumbler with handle and straw, fits in car cup holder",
"category": "drinkware",
"price": 45.00
}
]
# Embed and upsert products
def embed_text(text):
response = openai_client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
vectors_to_upsert = []
for product in products:
# Combine name and description for embedding
text = f"{product['name']}: {product['description']}"
embedding = embed_text(text)
vectors_to_upsert.append({
"id": product["id"],
"values": embedding,
"metadata": {
"name": product["name"],
"description": product["description"],
"category": product["category"],
"price": product["price"]
}
})
# Upsert to Pinecone
index.upsert(vectors=vectors_to_upsert)
print(f"Indexed {len(vectors_to_upsert)} products")
Querying the Vector Database
def search_products(query: str, top_k: int = 5, category: str = None):
"""Search for products using semantic similarity"""
# Embed the query
query_embedding = embed_text(query)
# Build filter if category specified
filter_dict = {"category": category} if category else None
# Search Pinecone
results = index.query(
vector=query_embedding,
top_k=top_k,
include_metadata=True,
filter=filter_dict
)
return results.matches
# Search for products
results = search_products("something to keep my coffee hot during commute")
for match in results:
print(f"Score: {match.score:.3f}")
print(f" Product: {match.metadata['name']}")
print(f" Price: ${match.metadata['price']}")
print()
Output:
Score: 0.892
Product: Yeti Rambler 20oz
Price: $35.00
Score: 0.867
Product: Stanley Quencher 40oz
Price: $45.00
Score: 0.854
Product: Hydro Flask 32oz
Price: $44.95
Setting Up Chroma (Local Development)
import chromadb
from chromadb.utils import embedding_functions
# Initialize Chroma client
client = chromadb.PersistentClient(path="./chroma_db")
# Use OpenAI embeddings
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
api_key="your-openai-key",
model_name="text-embedding-3-small"
)
# Create collection
collection = client.create_collection(
name="products",
embedding_function=openai_ef,
metadata={"hnsw:space": "cosine"}
)
# Add products
collection.add(
ids=["prod_001", "prod_002", "prod_003"],
documents=[
"Yeti Rambler 20oz: Stainless steel vacuum insulated tumbler",
"Hydro Flask 32oz: Wide mouth water bottle, double-wall insulation",
"Stanley Quencher 40oz: Insulated tumbler with handle and straw"
],
metadatas=[
{"category": "drinkware", "price": 35.00},
{"category": "drinkware", "price": 44.95},
{"category": "drinkware", "price": 45.00}
]
)
# Query
results = collection.query(
query_texts=["travel coffee mug"],
n_results=3
)
Setting Up Weaviate
import weaviate
from weaviate.classes.init import Auth
# Connect to Weaviate Cloud
client = weaviate.connect_to_weaviate_cloud(
cluster_url="your-cluster-url",
auth_credentials=Auth.api_key("your-api-key"),
headers={"X-OpenAI-Api-Key": "your-openai-key"}
)
# Define schema for products
client.collections.create(
name="Product",
vectorizer_config=weaviate.classes.config.Configure.Vectorizer.text2vec_openai(),
properties=[
weaviate.classes.config.Property(
name="name",
data_type=weaviate.classes.config.DataType.TEXT
),
weaviate.classes.config.Property(
name="description",
data_type=weaviate.classes.config.DataType.TEXT
),
weaviate.classes.config.Property(
name="category",
data_type=weaviate.classes.config.DataType.TEXT
),
weaviate.classes.config.Property(
name="price",
data_type=weaviate.classes.config.DataType.NUMBER
)
]
)
# Add products
products = client.collections.get("Product")
products.data.insert({
"name": "Yeti Rambler 20oz",
"description": "Stainless steel vacuum insulated tumbler",
"category": "drinkware",
"price": 35.00
})
# Search with hybrid (vector + keyword)
response = products.query.hybrid(
query="insulated coffee tumbler",
limit=5
)
Comparison: Which to Choose?
| Feature | Pinecone | Weaviate | Chroma |
|---|---|---|---|
| Hosting | Managed only | Both | Self-hosted |
| Hybrid Search | Limited | Built-in | Limited |
| Scale | Billions | Millions+ | Thousands |
| Free Tier | Yes | Yes | Free (OSS) |
| Best For | Production | Hybrid search | Development |
Start with Chroma for prototyping (it's free and runs locally). Move to Pinecone for production (managed, scales easily). Use Weaviate if you need advanced hybrid search features.
Distance Metrics Explained
Cosine Similarity
Measures angle between vectors. Best for text embeddings where direction matters more than magnitude.
cosine(A, B) = AยทB / (||A|| ร ||B||)
Euclidean Distance
Straight-line distance. Good when magnitude matters, like comparing product features.
euclidean(A, B) = sqrt(sum((A-B)ยฒ))
Dot Product
Fast and simple. Often used with normalized vectors (same as cosine).
dot(A, B) = sum(A ร B)
Key Takeaways
- Vector databases enable semantic search - Find products by meaning, not just keywords
- Embeddings capture meaning - Similar products have similar vectors
- Use cosine similarity for text - It's the standard for text embeddings
- Start with Chroma, scale with Pinecone - Match your database to your stage
Next up: Embedding Product Catalogs - How to structure and embed your product data for optimal search.