Start hereQuick start · 6 you’ll reach for daily
Target versions · paceVersions
weaviate ≥ 1.25
weaviate-client (py) ≥ 4.5
weaviate-client (ts) ≥ 3
The Python client v4 is a full rewrite — gRPC by default,
client.collections.get(...) instead of GraphQL builders,
typed return objects. The v3 client (client.query.get(...).do()) is legacy — do not
start new projects there. .classes.config /
.classes.query hold the typed configuration helpers.
This sheet pins to Weaviate 1.25+ and the v4 client.
Install · serverSetup
# Docker — single node with text2vec + generative-openai bolted on
docker run -d --name weaviate -p 8080:8080 -p 50051:50051 \
-e ENABLE_MODULES="text2vec-openai,generative-openai,reranker-cohere" \
-e DEFAULT_VECTORIZER_MODULE=text2vec-openai \
-e OPENAI_APIKEY=$OPENAI_API_KEY \
-e PERSISTENCE_DATA_PATH=/var/lib/weaviate \
cr.weaviate.io/semitechnologies/weaviate:1.25
# Python client v4 — sync + async, gRPC under the hood
pip install "weaviate-client>=4.5"
# JS / TS — v3 client
npm install weaviate-client
# Health check
curl http://localhost:8080/v1/.well-known/ready
# → 200 OK once nodes are up
Where things liveCommon imports
| import weaviate | Top-level — connect_to_* live here. |
| import weaviate.classes.config as wvcc | Schema / vectorizer / index helpers. |
| from weaviate.classes.config import Property, DataType, Configure | Property + type enums. |
| from weaviate.classes.query import Filter, MetadataQuery, HybridFusion, Sort, Rerank | Query DSL. |
| from weaviate.classes.tenants import Tenant, TenantActivityStatus | Multi-tenancy types. |
| from weaviate.classes.data import DataObject, DataReference | Object + reference shapes for batch ops. |
| from weaviate.exceptions import WeaviateQueryError, UnexpectedStatusCodeError | Common exceptions. |
Local · cloud · embeddedConnections
| weaviate.connect_to_local(host="localhost", port=8080) | Docker / local. Preferred default for dev. |
| weaviate.connect_to_weaviate_cloud(cluster_url=..., auth_credentials=Auth.api_key(...)) | Managed cloud. |
| weaviate.connect_to_custom(http_host, grpc_host, ...) | Self-hosted, custom ports / TLS. |
| weaviate.connect_to_embedded() | Spins up a server in your process. Tests + notebooks. |
| headers={"X-OpenAI-Api-Key": "..."} | Pass module keys per-connection. Avoids server env. |
| client.is_ready() / client.is_live() | Health checks. |
| client.get_meta() | Server version + enabled modules. |
| with client: ... | Context manager closes the gRPC channel cleanly. |
| client.close() | Mandatory if not using with. Leaks hang the gRPC pool. |
Properties · vectorizers · indexCollections & schema
| client.collections.create(name, properties=[...], vectorizer_config=...) | Create collection (was "class" in v3). |
| Property(name, data_type=DataType.TEXT) | TEXT, INT, NUMBER, BOOL, DATE, GEO_COORDINATES, BLOB, *_ARRAY. |
| Configure.Vectorizer.text2vec_openai(model="text-embedding-3-small") | Server-side embedding via OpenAI module. |
| Configure.Vectorizer.text2vec_cohere() / huggingface() / ollama() / transformers() | Other text vectorizers. |
| Configure.Vectorizer.none() | Bring your own vectors — you pass them on insert. |
| Configure.Generative.openai(model="gpt-4o-mini") | Wire a generative module — enables col.generate.*. |
| Configure.NamedVectors.text2vec_openai(name="title_vec", source_properties=["title"]) | Multiple vectors per object — each over a different property set. |
| Configure.VectorIndex.hnsw(quantizer=Configure.VectorIndex.Quantizer.pq()) | HNSW with product / scalar / binary quantization. |
| Configure.VectorIndex.flat() | Brute force — for small collections. |
| Configure.inverted_index(bm25_b=0.75, bm25_k1=1.2) | BM25 tuning for keyword search. |
| collections.exists(name) / get(name) / delete(name) / list_all() | Lifecycle + inventory. |
import weaviate
import weaviate.classes.config as wvcc
from weaviate.classes.config import Property, DataType
client = weaviate.connect_to_local() # or connect_to_weaviate_cloud(...)
# Idempotent create
if not client.collections.exists("Article"):
client.collections.create(
name="Article",
# Vectorize with OpenAI; only the "body" property feeds the vector
vectorizer_config=wvcc.Configure.Vectorizer.text2vec_openai(
model="text-embedding-3-small",
),
generative_config=wvcc.Configure.Generative.openai(
model="gpt-4o-mini",
),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="body", data_type=DataType.TEXT),
Property(name="tags", data_type=DataType.TEXT_ARRAY),
Property(name="published", data_type=DataType.DATE),
Property(name="rating", data_type=DataType.NUMBER),
],
# HNSW + quantization
vector_index_config=wvcc.Configure.VectorIndex.hnsw(
quantizer=wvcc.Configure.VectorIndex.Quantizer.pq(),
),
)
client.close()
data.* · batchInserts & updates
| col.data.insert(properties={...}, uuid=...) | Single insert. Returns the object UUID. |
| col.data.insert_many([DataObject(properties=..., uuid=...)]) | Many at once — up to a few hundred objects. |
| with col.batch.dynamic() as b: b.add_object(properties=...) | Preferred bulk ingest — auto-tunes batch size. |
| col.batch.fixed_size(batch_size=200) | Explicit batch size when you need predictable behavior. |
| b.add_object(properties=..., vector=[...]) | BYO vector — skips the vectorizer. |
| col.data.update(uuid, properties={...}) | Patch existing object. |
| col.data.replace(uuid, properties={...}) | Overwrite all properties. |
| col.data.delete_by_id(uuid) | Delete one. |
| col.data.delete_many(where=Filter.by_property("year").less_than(2020)) | Delete by filter. |
| col.data.exists(uuid) | Quick existence check. |
near_text · bm25 · hybrid · fetchQueries
| col.query.near_text(query="...", limit=5) | Semantic / vector search via vectorizer module. |
| col.query.near_vector(near_vector=[...], limit=5) | Vector search with your own vector. |
| col.query.near_object(near_object=uuid) | Nearest neighbors of an existing object. |
| col.query.bm25(query="...", query_properties=["title^2", "body"]) | Keyword search with field boosts. |
| col.query.hybrid(query="...", alpha=0.5) | Vector + BM25 fusion. alpha 0 = pure BM25, 1 = pure vector. |
| col.query.hybrid(..., fusion_type=HybridFusion.RANKED) | RRF fusion (RELATIVE_SCORE is the default). |
| col.query.fetch_objects(filters=..., limit=100, offset=0) | Filter + paginate without similarity. |
| col.query.fetch_object_by_id(uuid) | Random access. |
| return_metadata=MetadataQuery(distance=True, score=True, explain_score=True) | What to attach to each match. |
| return_properties=["title","tags"] | Limit fields returned. Trim wire bytes. |
| limit / offset / sort / group_by | Standard query controls. Sort needs an inverted index on the field. |
import weaviate
from weaviate.classes.query import Filter, MetadataQuery, HybridFusion
client = weaviate.connect_to_local()
col = client.collections.get("Article")
# Pure semantic (vector) search
near = col.query.near_text(
query="how does retrieval-augmented generation work?",
limit=5,
filters=Filter.by_property("tags").contains_any(["rag", "embeddings"]),
return_metadata=MetadataQuery(distance=True, score=True),
)
# BM25-only (keyword)
bm = col.query.bm25(
query="RAG retrieval",
query_properties=["title^2", "body"], # boost title
limit=5,
)
# Hybrid — server-side fusion. alpha=0 → pure BM25, 1 → pure vector
hybrid = col.query.hybrid(
query="RAG retrieval generation",
alpha=0.5,
fusion_type=HybridFusion.RELATIVE_SCORE, # or RANKED (RRF)
limit=5,
return_metadata=MetadataQuery(score=True, explain_score=True),
)
for o in hybrid.objects:
print(f"{o.metadata.score:.3f} {o.properties['title']}")
client.close()
Filter.by_property · nestedFilters
| Filter.by_property("k").equal("v") | Equality. |
| Filter.by_property("k").not_equal("v") | Inequality. |
| Filter.by_property("n").greater_than(10) & ...less_or_equal(100) | Numeric range with bitwise &. |
| Filter.by_property("tags").contains_any(["a","b"]) | Array overlap. |
| Filter.by_property("tags").contains_all(["a","b"]) | Array AND. |
| Filter.by_property("title").like("rag*") | Wildcard pattern. |
| Filter.by_property("ts").greater_than(datetime(...)) | Date comparison. |
| Filter.by_id().equal(uuid) | Filter by object UUID. |
| Filter.by_ref(link_on="hasCategory").by_property("name").equal("AI") | Filter through a cross-reference. |
| f1 & f2 / f1 | f2 / ~f1 | AND, OR, NOT via operator overloading. |
Retrieve + generate in one callGenerative search (RAG)
| col.generate.near_text(query, single_prompt="Summarize: {body}") | Run a prompt per object. {property} placeholders. |
| col.generate.near_text(query, grouped_task="Combine these into a summary.") | One prompt over all retrieved objects. |
| col.generate.hybrid(query, alpha=0.5, grouped_task="...") | Hybrid retrieval + RAG generate in one call. |
| res.objects[i].generated | Per-object output (single_prompt). |
| res.generated | Combined output (grouped_task). |
| Configure.Generative.openai(model="gpt-4o-mini") | Wired at create time. Other vendors: cohere, anthropic, mistral, ollama. |
| rerank=Rerank(prop="body", query="...") | Two-stage retrieval — rerank with a cross-encoder module. |
Tenant isolationMulti-tenancy
| Configure.multi_tenancy(enabled=True, auto_tenant_creation=False) | Enable at create time. Can’t toggle later. |
| col.tenants.create(["acme", "globex"]) | Provision tenants. |
| col.tenants.get() | List all tenants + activity status. |
| col.tenants.update([Tenant(name="acme", activity_status=TenantActivityStatus.HOT)]) | HOT / COLD / FROZEN — control RAM vs disk. |
| col.with_tenant("acme").data.insert(...) | All ops are scoped via .with_tenant(...). |
| col.with_tenant("acme").query.near_text("...") | Tenant-scoped query. |
| col.tenants.remove(["acme"]) | Delete a tenant + its data. |
COLD tenants free RAM, FROZEN moves them to cloud object storage — massive savings on inactive customers.
Cross-collection linksReferences & relationships
| ReferenceProperty(name="hasCategory", target_collection="Category") | Define a typed link at schema create. |
| col.data.reference_add(from_uuid, "hasCategory", to=target_uuid) | Add a cross-reference. |
| col.data.reference_replace / reference_delete | Manage links. |
| return_references=[QueryReference(link_on="hasCategory", return_properties=["name"])] | Pull linked data in one query. |
| Filter.by_ref(link_on="hasCategory").by_property("name").equal("AI") | Filter on a reference target’s property. |
Schema · ingest · RAGEnd-to-end · Hybrid RAG
Idempotent create + batch insert + hybrid retrieve + RAG generate — all server-side, one call at the end.
# Minimal Weaviate RAG — schema, ingest, hybrid retrieve, RAG generate
import os, weaviate
import weaviate.classes.config as wvcc
from weaviate.classes.config import Property, DataType
from weaviate.classes.query import Filter
client = weaviate.connect_to_local(
headers={"X-OpenAI-Api-Key": os.environ["OPENAI_API_KEY"]},
)
NAME = "Article"
if not client.collections.exists(NAME):
client.collections.create(
name=NAME,
vectorizer_config=wvcc.Configure.Vectorizer.text2vec_openai(),
generative_config=wvcc.Configure.Generative.openai(model="gpt-4o-mini"),
properties=[
Property(name="title", data_type=DataType.TEXT),
Property(name="body", data_type=DataType.TEXT),
Property(name="tags", data_type=DataType.TEXT_ARRAY),
],
)
col = client.collections.get(NAME)
# Batch insert — Weaviate calls the vectorizer for you
with col.batch.dynamic() as batch:
for d in [
{"title": "What is RAG?", "body": "Retrieval-augmented generation joins...",
"tags": ["rag", "intro"]},
{"title": "Hybrid search", "body": "BM25 + vectors fuse for...",
"tags": ["rag", "hybrid"]},
]:
batch.add_object(properties=d)
# Hybrid retrieve + RAG generate in one call
res = col.generate.hybrid(
query="explain RAG",
alpha=0.5, limit=3,
filters=Filter.by_property("tags").contains_any(["rag"]),
grouped_task="Answer the question using only the retrieved articles.",
)
print(res.generated)
client.close()
Best practiceGood to know
client.close() leaks the channel and hangs interpreter shutdown. The with weaviate.connect_to_* form is the safe default.
col.query.near_text("...") (otherwise you must pass a vector yourself).
Common trapsWatch out for
client.query.get(...).with_near_text(...).do() is the legacy path. v4 calls go through client.collections.get(...).query.near_text(...) and return typed objects.
multi_tenancy at create time is a one-way door.
You can’t turn it on later without recreating the collection. Decide up-front whether each customer / project is a tenant.
tokenization=Tokenization.FIELD or skipped indexing for a property, bm25 / hybrid will silently return zero hits on it.