How to Build Hybrid Search With Weaviate and OpenAI Embeddings
Pure vector search is great at meaning and terrible at exact strings. Ask for “SKU-4471B” and an embedding may return something merely similar. Keyword search has the opposite problem. Weaviate ships hybrid search that runs both and merges the results.
Quick answer: Use col.query.hybrid(query="...", alpha=0.5, vector=vec). The alpha parameter sets the blend: 0 is pure keyword, 1 is pure vector, 0.5 splits them evenly.
Getting the blend right matters more than most teams think. The default 0.5 is rarely the best value for real data.
How does the alpha parameter work?
Alpha controls the rank fusion between BM25 keyword scores and vector distance. Low alpha leans keyword, high leans semantic.
| alpha | Behavior | Good for |
|---|---|---|
| 0.0 | Keyword only (BM25) | Codes, names, IDs |
| 0.25 | Keyword-leaning | Product catalogs, legal |
| 0.5 | Balanced | General docs |
| 0.75 | Semantic-leaning | Support questions |
| 1.0 | Vector only | Paraphrase-heavy text |
How do you generate and store embeddings?
from openai import OpenAI
import weaviate
oc = OpenAI()
client = weaviate.connect_to_local()
col = client.collections.get("Product")
resp = oc.embeddings.create(
model="text-embedding-3-small",
input="Wireless noise-cancelling earbuds")
vec = resp.data[0].embedding # 1536 dims
col.data.insert(properties={"title": "Earbuds", "sku": "SKU-4471B"}, vector=vec)
text-embedding-3-small costs $0.02 per million tokens, which makes embedding a 100,000-document catalog a few dollars. Big models are not always better here; small is often enough for search.
How do you run a hybrid query?
q = "waterproof earbuds SKU-4471B"
qv = oc.embeddings.create(model="text-embedding-3-small",
input=q).data[0].embedding
res = col.query.hybrid(query=q, vector=qv, alpha=0.4, limit=10)
for o in res.objects:
print(round(o.metadata.score, 3), o.properties["title"])
Scores are normalized to 0-1, which makes thresholds easy to reason about. Anything under about 0.6 is usually noise.
How do you tune alpha without guessing?
Build a set of 20-30 real queries with known good answers. Run them across alpha values from 0 to 1 in steps of 0.2 and measure how often the right document lands in the top 3. Pick the winner. This takes an afternoon and usually beats gut feel by a wide margin.
What are the practical limits?
- Hybrid needs both a vector and the text indexed; storing vectors only disables the keyword half.
- BM25 tokenization matters — compound SKUs need the right analyzer or they split wrongly.
- Two searches per query means slightly higher latency than vector-only.
- Embedding costs scale with document count and any re-indexing you trigger.
Who this suits: catalogs, documentation sites, and support systems where users mix natural language with exact identifiers. Who should skip it: if all your queries are fuzzy and long, a pure vector search is simpler and only slightly worse.
Quick Summary
- Hybrid search fuses BM25 keyword scores with vector similarity.
- Control the mix with
alpha: 0 keyword, 1 vector, 0.5 balanced. - Start at alpha 0.4 for catalogs, then tune against real queries.
text-embedding-3-smallis cheap enough for large catalogs.- Scores are normalized 0-1; treat sub-0.6 results as weak.
