Weaviate is an open-source vector database with native support for BM25 hybrid search, generative AI integration, and enterprise-grade multi-tenancy. It combines semantic search with traditional keyword matching without requiring external sparse encoders.
Key features
Native BM25 : Built-in keyword search without sparse embeddings
Generative search : RAG with OpenAI, Cohere, or custom LLMs
Multi-tenancy : Per-tenant shards for data isolation
Hybrid search : Vector + BM25 with configurable alpha balancing
GraphQL interface : Advanced querying capabilities
Cloud + self-hosted : Flexible deployment options
Installation
Install the Weaviate Python client:
pip install weaviate-client
Connection
Weaviate Cloud
from vectordb.databases.weaviate import WeaviateVectorDB
db = WeaviateVectorDB(
cluster_url = "https://my-cluster.weaviate.cloud" ,
api_key = "weaviate-api-key" ,
headers = { "X-OpenAI-Api-Key" : "sk-..." }
)
Self-hosted instance
db = WeaviateVectorDB(
cluster_url = "http://localhost:8080" ,
api_key = "" , # Empty for local
tracing_project_name = "my-project"
)
Collection creation
Basic collection
db.create_collection(
collection_name = "Articles" ,
properties = [
Property( name = "title" , data_type = DataType. TEXT ),
Property( name = "content" , data_type = DataType. TEXT ),
Property( name = "category" , data_type = DataType. TEXT )
]
)
With vectorizer
Use Weaviate’s built-in vectorization:
from weaviate.classes.config import Configure
db.create_collection(
collection_name = "Articles" ,
vectorizer_config = Configure.Vectorizer.text2vec_openai(),
generative_config = Configure.Generative.openai()
)
With multi-tenancy
db.create_collection(
collection_name = "Documents" ,
enable_multi_tenancy = True
)
# Create tenants
db.create_tenants([ "tenant_a" , "tenant_b" , "tenant_c" ])
Upserting documents
Standard upsert
data = [
{
"text" : "Weaviate supports hybrid search" ,
"category" : "database" ,
"priority" : 1 ,
"vector" : [ 0.1 , 0.2 , ... ] # Optional custom embedding
},
{
"text" : "BM25 provides keyword matching" ,
"category" : "search" ,
"priority" : 2
}
]
db.upsert(data)
Tenant-scoped upsert
db.with_tenant( "tenant_a" ).upsert(documents)
Weaviate auto-generates UUIDs if not provided. Use "id" or "uuid" keys to specify custom IDs.
Querying
Dense vector search
response = db.query(
vector = [ 0.1 , 0.2 , ... ],
limit = 10 ,
include_vectors = True ,
return_documents = True # Returns Haystack Documents
)
# Access results
for doc in response:
print ( f "Score: { doc.score } , Content: { doc.content } " )
Text-based semantic search
Uses Weaviate’s vectorizer to embed the query:
response = db.query(
query_string = "machine learning algorithms" ,
limit = 10 ,
return_documents = True
)
Hybrid search (vector + BM25)
results = db.hybrid_search(
query = "neural networks deep learning" ,
vector = embedding, # Optional pre-computed vector
top_k = 10 ,
alpha = 0.5 # 1.0 = vector only, 0.0 = BM25 only
)
The alpha parameter controls the balance:
1.0: Pure vector search (semantic)
0.5: Balanced hybrid (default)
0.0: Pure BM25 (keyword)
Weaviate uses MongoDB-style filter syntax:
# Simple equality
results = db.query(
vector = embedding,
filters = { "category" : "technology" },
limit = 10
)
# Range queries
results = db.query(
vector = embedding,
filters = { "priority" : { "$gte" : 5 }},
limit = 10
)
# Combined filters
results = db.query(
vector = embedding,
filters = {
"category" : "tech" ,
"published" : { "$gt" : "2024-01-01" }
},
limit = 10
)
# Logical OR
results = db.query(
vector = embedding,
filters = {
"$or" : [
{ "category" : "tech" },
{ "category" : "science" }
]
}
)
Supported filter operators
$eq: Equal to
$ne: Not equal to
$gt: Greater than
$gte: Greater than or equal
$lt: Less than
$lte: Less than or equal
$in: Value in list
$like: Wildcard pattern matching
$and: Logical AND (implicit for multiple conditions)
$or: Logical OR
Reranking
Improve result precision with cross-encoder reranking:
results = db.query(
query_string = "artificial intelligence" ,
limit = 50 ,
rerank = {
"prop" : "content" ,
"query" : "What are the latest AI trends?"
}
)
Generative search (RAG)
Single prompt per result
Generate content for each retrieved document:
response = db.generate(
query_string = "machine learning frameworks" ,
single_prompt = "Summarize this in one sentence: {content} " ,
limit = 5
)
for item in response.objects:
print (item.generated) # LLM-generated summary
Grouped task
Generate content from all results combined:
response = db.generate(
query_string = "neural networks" ,
grouped_task = "Write a comprehensive summary of these documents" ,
limit = 10
)
print (response.generated) # Single combined answer
Multi-tenancy
Create and manage tenants
# Create collection with multi-tenancy
db.create_collection( "SharedDocs" , enable_multi_tenancy = True )
# Add tenants
db.create_tenants([ "company_a" , "company_b" , "company_c" ])
# Check tenant existence
if db.tenant_exists( "company_a" ):
print ( "Tenant exists" )
# Delete tenants
db.delete_tenants([ "company_old" ])
Tenant-scoped operations
# Switch to tenant context
tenant_db = db.with_tenant( "company_a" )
# All operations now scoped to this tenant
tenant_db.upsert(documents)
results = tenant_db.query( vector = embedding)
# Chain operations
db.with_tenant( "company_b" ).upsert(other_docs)
Converting results
Manual conversion
Convert raw Weaviate responses to Haystack Documents:
# Query without auto-conversion
response = db.query(
vector = embedding,
limit = 10 ,
return_documents = False # Get raw response
)
# Convert manually
documents = db.query_to_documents(
response,
include_vectors = True
)
for doc in documents:
print (doc.id, doc.score, doc.embedding)
Best practices
Choose the right alpha for hybrid search
Tune the alpha parameter based on your use case: # Semantic-heavy (0.7-1.0)
# Use for: Conceptual queries, paraphrasing, cross-lingual
results = db.hybrid_search( query = "..." , alpha = 0.8 )
# Balanced (0.4-0.6)
# Use for: General-purpose search
results = db.hybrid_search( query = "..." , alpha = 0.5 )
# Keyword-heavy (0.0-0.3)
# Use for: Exact terms, product codes, technical jargon
results = db.hybrid_search( query = "..." , alpha = 0.2 )
Weaviate’s native multi-tenancy uses per-tenant shards for true isolation:
Good : 100s to 1000s of tenants per collection
Good : Enterprise SaaS with strict data isolation requirements
Avoid : Millions of micro-tenants (consider Milvus partition keys)
Leverage BM25 without sparse encoders
Weaviate’s BM25 is built-in, unlike other databases: # No sparse embeddings needed!
results = db.hybrid_search(
query = "specific technical term" ,
alpha = 0.3 # Emphasize BM25
)
Generative search configuration
Configure generative models per collection: from weaviate.classes.config import Configure
db.create_collection(
"Articles" ,
generative_config = Configure.Generative.openai(
model = "gpt-4"
)
)
Requires API key in headers: db = WeaviateVectorDB(
cluster_url = "..." ,
api_key = "..." ,
headers = { "X-OpenAI-Api-Key" : "sk-..." }
)
Error handling
try :
db.create_collection( "Articles" )
db.upsert(documents)
except ValueError as e:
print ( f "Configuration error: { e } " )
except Exception as e:
print ( f "Weaviate error: { e } " )
finally :
db.close() # Clean shutdown
Closing connections
db.close() # Releases network resources
Source reference
Implementation: src/vectordb/databases/weaviate.py
Key classes and methods:
WeaviateVectorDB.__init__(): src/vectordb/databases/weaviate.py:74
create_collection(): src/vectordb/databases/weaviate.py:159
upsert(): src/vectordb/databases/weaviate.py:207
query(): src/vectordb/databases/weaviate.py:362
hybrid_search(): src/vectordb/databases/weaviate.py:537
generate(): src/vectordb/databases/weaviate.py:578
create_tenants(): src/vectordb/databases/weaviate.py:629