Weaviate Vector Store#
Overview#
WeaviateVector is Dify's Weaviate adapter, implementing the BaseVector abstract interface for RAG vector storage. The implementation lives at api/providers/vdb/vdb-weaviate/src/dify_vdb_weaviate/weaviate_vector.py. Configuration is driven by WeaviateConfig in api/configs/middleware/vdb/weaviate_config.py:
| Env Var | Default | Purpose |
|---|---|---|
WEAVIATE_ENDPOINT | β | HTTP endpoint (required) |
WEAVIATE_GRPC_ENDPOINT | β | gRPC endpoint; inferred from HTTP host if omitted |
WEAVIATE_API_KEY | β | Optional API key auth |
WEAVIATE_BATCH_SIZE | 100 | Objects per batch insert |
WEAVIATE_TOKENIZATION | word | Property tokenization mode |
The adapter uses a process-singleton Weaviate client with double-checked locking and registers an atexit handler for graceful teardown .
UUID Generation: History and Current State#
Old Behavior (Bug β now fixed)#
_get_uuids() previously derived each object's Weaviate UUID as uuid5(URL_NAMESPACE, page_content). This caused two problems:
- Content collision: Two documents containing identical text produced the same UUID. The second write silently overwrote the first, corrupting metadata and breaking metadata-based filters.
- Deletion mismatch: Dify's
delete_by_ids()receives the segment'sindex_node_id(a UUID4 logical key) as the IDs to delete. Since Weaviate objects were keyed by content-derived UUID5, the IDs never matched β every delete call returned 404, which was swallowed, leaving orphaned vectors.
PR #36140 proposed including doc_id in the UUID5 hash to prevent collisions, but did not resolve the deletion-side mismatch.
Current Behavior (Fixed)#
_get_uuids() now uses doc.metadata["doc_id"] directly as the Weaviate object UUID, matching the BaseVector._get_uuids() contract. doc_id is the segment's index_node_id β the same value delete_by_ids() receives, so write and delete paths are now consistent by construction.
A random uuid4() fallback is used only when doc_id is absent (e.g., test fixtures), noted in code comments as acceptable because such objects are not expected to go through the cleanup path .
This fix was introduced by PR #39176.
Deletion Patterns: UUID-based vs. Metadata-based#
The Core Deletion Bug (now fixed)#
Prior to the fix, delete_by_ids() called col.data.delete_by_id(uid) for each uid, treating logical index_node_id values as Weaviate object UUIDs. Since UUIDs were content-derived (UUID5), this always 404'd and was silently swallowed . The cleanup task reported success while no vectors were removed.
Measured impact (issue #40457): Deleting 4 documents left 9,165 orphan objects in one knowledge base. Orphans are returned by vector search but then dropped by Dify (no matching Postgres row), silently consuming top_k retrieval slots and degrading search quality over time .
Current delete_by_ids() β Dual-Path #
- UUID-path (new objects):
col.data.delete_by_id(uid)for each ID. Hits objects written after the UUID fix where Weaviate UUID ==doc_id. 404s are suppressed β expected for legacy pre-fix objects. - Metadata-path (legacy fallback):
col.data.delete_many(where=Filter.by_property("doc_id").contains_any(ids))β catches any object whosedoc_idproperty matches, regardless of what UUID was used at write time. This backward-compatible sweep ensures pre-fix orphans are reaped on the next deletion pass.
Other Deletion Methods#
| Method | Mechanism | Works against legacy UUID5 objects? |
|---|---|---|
delete_by_metadata_field(key, value) | Filter.by_property(key).equal(value) | β Yes β metadata-only, UUID-agnostic |
delete() | Drops entire collection | β Yes |
delete_by_ids() (current) | UUID + doc_id metadata dual-path | β Yes (via metadata fallback) |
delete_by_metadata_field and delete were never affected by the UUID mismatch. delete drops the entire collection and is the path taken by clean_dataset_task when a full dataset is removed.
Collection Schema, Caching, and Query Resilience#
Schema#
Collections are created with these properties :
textβ chunk content (tokenized perWEAVIATE_TOKENIZATION)document_idβ dataset document ID (used fordocument_ids_filterin search)doc_idβ segmentindex_node_id; the deletion keydoc_type,chunk_index,is_summary,original_chunk_idβ parent-child indexing metadata
_ensure_properties() backfills any missing properties on existing collections , enabling schema evolution without data loss.
Race Condition Prevention#
Collection creation acquires a Redis distributed lock (vector_indexing_lock_{collection_name}, 20 s timeout) and sets a TTL-1h cache key (vector_indexing_{collection_name}) to skip redundant existence checks on subsequent calls .
Query Retry on Schema Mismatch#
Both search_by_vector and search_by_full_text catch WeaviateQueryError, call _ensure_properties(), and retry the query once . This handles the case where a collection was created by an older Dify version that lacked newer schema properties.
Weaviate 1.39 Upgrade and Docker Registry Migration#
PR #38214 upgraded the bundled self-hosted Weaviate from 1.27.0 to 1.39.0 (currently 1.39.2 in docker-compose.yaml) and changed the image source from Docker Hub to Weaviate's own registry :
cr.weaviate.io/semitechnologies/weaviate:1.39.2
Self-hosted impact: Environments with firewall or network rules that allow docker.io but block cr.weaviate.io will fail to pull the image on next docker compose pull. Update egress rules accordingly.
Upgrade path from 1.27: Increment through each minor version with graceful stops between upgrades to avoid HNSW commit log truncation issues. Version 1.39 itself has no breaking API changes and is compatible with weaviate-client 4.x.
WEAVIATE_GRPC_ENABLED was removed (PR #33378); gRPC is always active and the endpoint is configured via WEAVIATE_GRPC_ENDPOINT .